From e83b53fcafc14fb7b9d93b6ced9731acfe2b3978 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 3 Feb 2025 11:22:42 -0800 Subject: [PATCH 001/149] wip --- .../amazon/jdbc/util/CacheService.java | 93 +++++++++++++++++++ .../amazon/jdbc/util/monitoring/Monitor.java | 31 +++++++ .../util/monitoring/MonitorException.java | 41 ++++++++ .../monitoring/MonitorExceptionResponse.java | 22 +++++ .../util/monitoring/MonitorInitializer.java | 22 +++++ .../jdbc/util/monitoring/MonitorService.java | 71 ++++++++++++++ .../jdbc/util/monitoring/MonitorState.java | 24 +++++ .../jdbc/util/monitoring/MonitorStatus.java | 43 +++++++++ .../jdbc/util/notifications/Notification.java | 26 ++++++ .../notifications/NotificationListener.java | 21 +++++ .../notifications/NotificationService.java | 27 ++++++ 11 files changed, 421 insertions(+) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/CacheService.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorException.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorExceptionResponse.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorInitializer.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/notifications/Notification.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationListener.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationService.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CacheService.java b/wrapper/src/main/java/software/amazon/jdbc/util/CacheService.java new file mode 100644 index 000000000..15732c5cf --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CacheService.java @@ -0,0 +1,93 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util; + +import java.util.Map; +import java.util.function.Supplier; + +public interface CacheService { + /** + * Adds a cache to CacheService if it does not already exist. + * + * @param cacheName + * @param cacheSupplier + */ + void addCacheIfAbsent( + String cacheName, Supplier> cacheSupplier); + + /** + * Clears the given cache without deleting it from the CacheService. To delete the cache as well, use + * {@link #deleteCache(String)}. + * + * @param cacheName + */ + void clear(String cacheName); + + void deleteCache(String cacheName); + + // TODO: in the current code it seems the time-to-live we pass to get/set is always the same, would it be better to + // pass this in addCacheIfAbsent instead of every single get/set call? Less generic but more convenient. + T get(String cacheName, Object key, Class dataClass, long timeToLiveNs); + + /** + * Sets a value in the specified cache. If the specified cache does not exist, it will be created with default + * settings. + * + * @param cacheName + * @param key + * @param value + * @param timeToLiveNs + * @param + */ + void set(String cacheName, Object key, T value, long timeToLiveNs); + + /** + * Sets a value in the specified cache if it exists. Otherwise, returns false without doing anything. This method can + * be used if the caller does not want to create a default cache when the specified cache does not already exist. + * + * @param cacheName + * @param key + * @param value + * @param timeToLiveNs + * @return + * @param + */ + boolean setIfCacheExists(String cacheName, Object key, T value, long timeToLiveNs); + + void delete(String cacheName, Object key); + + /** + * Returns a copy of the entries in the given cache. + * + * @param cacheName + * @return + * @param + */ + Map getEntries(String cacheName); + + int size(String cacheName); + + /** + * Deletes all caches from the CacheService. + */ + void deleteAll(); + + /** + * Clears all data from all caches without deleting the caches themselves. + */ + void clearAll(); +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java new file mode 100644 index 000000000..f81592aba --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -0,0 +1,31 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +public interface Monitor { + void start(); + + void stop(); + + void restart(); + + boolean isStopped(); + + MonitorState getStatus(); + + MonitorException getException(); +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorException.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorException.java new file mode 100644 index 000000000..4df5e6501 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorException.java @@ -0,0 +1,41 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +public abstract class MonitorException { + private final String monitorType; + private final Object monitorKey; + private final Throwable cause; + + public MonitorException(String monitorType, Object monitorKey, Throwable cause) { + this.monitorType = monitorType; + this.monitorKey = monitorKey; + this.cause = cause; + } + + public String getMonitorType() { + return monitorType; + } + + public Object getMonitorKey() { + return monitorKey; + } + + public Throwable getCause() { + return cause; + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorExceptionResponse.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorExceptionResponse.java new file mode 100644 index 000000000..85f22326b --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorExceptionResponse.java @@ -0,0 +1,22 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +public enum MonitorExceptionResponse { + NO_ACTION, + RESTART +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorInitializer.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorInitializer.java new file mode 100644 index 000000000..a43e1338a --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorInitializer.java @@ -0,0 +1,22 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +@FunctionalInterface +public interface MonitorInitializer { + Monitor initialize(Object... params); +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java new file mode 100644 index 000000000..309ca3602 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -0,0 +1,71 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +import java.util.Set; +import java.util.function.Supplier; +import org.checkerframework.checker.nullness.qual.Nullable; +import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; +import software.amazon.jdbc.util.notifications.NotificationListener; + +public interface MonitorService { + boolean registerMonitorTypeIfAbsent( + String monitorType, + Supplier> cacheSupplier, + MonitorInitializer initializer, + Set exceptionResponses); + + void runIfAbsent( + String monitorType, Object monitorKey, @Nullable NotificationListener listener, Object... initializerParams); + + int numMonitors(String monitorType); + + MonitorStatus getStatus(String monitorType, Object monitorKey); + + /** + * Stops the given monitor and removes it from the cache of monitors. + * + * @param monitorType + * @param monitorKey + * @return + */ + Monitor stopAndRemoveMonitor(String monitorType, Object monitorKey); + + /** + * Stops all monitors of the given type, without deleting the cache for the monitor type. + * + * @param monitorType + */ + void stopAndRemoveMonitors(String monitorType); + + /** + * Stops all monitors of the given type and deletes the cache for the monitor type. + * + * @param monitorType + */ + void deleteMonitorType(String monitorType); + + /** + * Stops all monitors and removes them from their caches, without deleting the caches themselves. + */ + void stopAndRemoveAll(); + + /** + * Stops all monitors and deletes all monitor caches. + */ + void deleteAll(); +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java new file mode 100644 index 000000000..e7c41d803 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java @@ -0,0 +1,24 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +public enum MonitorState { + RUNNING, + IDLE, + STOPPED, + EXCEPTION +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java new file mode 100644 index 000000000..87c5794f7 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java @@ -0,0 +1,43 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +public class MonitorStatus { + private MonitorState state; + private long lastUsedTimeNs; + + public MonitorStatus(MonitorState state, long lastUsedTimeNs) { + this.state = state; + this.lastUsedTimeNs = lastUsedTimeNs; + } + + public MonitorState getState() { + return state; + } + + public void setState(MonitorState state) { + this.state = state; + } + + public long getLastUsedTimeNs() { + return lastUsedTimeNs; + } + + public void setLastUsedTimeNs(long lastUsedTimeNs) { + this.lastUsedTimeNs = lastUsedTimeNs; + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/notifications/Notification.java b/wrapper/src/main/java/software/amazon/jdbc/util/notifications/Notification.java new file mode 100644 index 000000000..62a71e1b4 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/notifications/Notification.java @@ -0,0 +1,26 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.notifications; + +public interface Notification { + + String getPublisherId(); + + Throwable getThrowable(); + + String getEventType(); +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationListener.java b/wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationListener.java new file mode 100644 index 000000000..6baf6b253 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationListener.java @@ -0,0 +1,21 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.notifications; + +public interface NotificationListener { + void processNotification(Notification notification); +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationService.java b/wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationService.java new file mode 100644 index 000000000..e5d143147 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationService.java @@ -0,0 +1,27 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.notifications; + +import java.util.function.Predicate; + +public interface NotificationService { + void notifySubscribers(Notification notification); + + void subscribe(NotificationListener listener, Predicate isSubscribedFunc); + + void unsubscribe(NotificationListener listener); +} From 29492e3954d45aef068b7abd173193b2ad7fb149 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 3 Feb 2025 11:22:42 -0800 Subject: [PATCH 002/149] wip --- .../jdbc/util/monitoring/MonitorListener.java | 21 +++++++++++++++++++ ..._advanced_jdbc_wrapper_messages.properties | 3 +++ 2 files changed, 24 insertions(+) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorListener.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorListener.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorListener.java new file mode 100644 index 000000000..8effbe076 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorListener.java @@ -0,0 +1,21 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +public interface MonitorListener { + MonitorExceptionResponse onMonitorException(MonitorException exception); +} diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 538995b68..9822a8129 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -59,6 +59,9 @@ AwsWrapperDataSource.missingJdbcProtocol=Missing JDBC protocol. Could not constr AwsWrapperDataSource.missingTarget=JDBC url or Server name is required. AwsWrapperDataSource.configurationProfileNotFound=Configuration profile ''{0}'' not found. +CacheServiceImpl.autoCreatedCache=set was called +CacheServiceImpl.classMismatch=Mismatch between specified cache data class and actual class - [specified class: {0}, actual class: {1}, cacheName: {2}, key: {3}]. + # Cluster Aware Reader Failover Handler ClusterAwareReaderFailoverHandler.interruptedThread=Thread was interrupted. ClusterAwareReaderFailoverHandler.attemptingReaderConnection=Trying to connect to host: ''{0}'', with properties ''{1}'' From 14fc451c2fcb779ae4756ec16f4ec86512dfcb68 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 3 Feb 2025 11:22:42 -0800 Subject: [PATCH 003/149] wip --- .../amazon/jdbc/util/CacheServiceImpl.java | 161 ++++++++++++++++++ ..._advanced_jdbc_wrapper_messages.properties | 2 +- 2 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java new file mode 100644 index 000000000..e601fceb1 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java @@ -0,0 +1,161 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.checkerframework.checker.nullness.qual.Nullable; + +public class CacheServiceImpl implements CacheService { + private static final Logger LOGGER = Logger.getLogger(CacheServiceImpl.class.getName()); + protected static final Set defaultCaches = + Stream.of("topology", "customEndpoint").collect(Collectors.toSet()); + private static CacheServiceImpl instance; + protected ConcurrentHashMap> caches = + new ConcurrentHashMap<>(); + + private CacheServiceImpl() { + + } + + public static CacheService getInstance() { + if (instance == null) { + instance = new CacheServiceImpl(); + } + + return instance; + } + + @Override + public void addCacheIfAbsent( + String cacheName, Supplier> cacheSupplier) { + caches.computeIfAbsent(cacheName, name -> cacheSupplier.get()); + } + + @Override + public void deleteCache(String cacheName) { + final SlidingExpirationCacheWithCleanupThread cache = caches.remove(cacheName); + if (cache != null) { + cache.clear(); + } + } + + @Override + public @Nullable T get(String cacheName, Object key, Class dataClass, long timeToLiveNs) { + final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); + if (cache == null) { + return null; + } + + final Object value = cache.get(key, timeToLiveNs); + if (dataClass.isInstance(value)) { + return dataClass.cast(value); + } + + LOGGER.fine( + Messages.get("CacheServiceImpl.classMismatch", new Object[]{dataClass, value.getClass(), cacheName, key})); + return null; + } + + @Override + public void set(String cacheName, Object key, T value, long timeToLiveNs) { + SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); + if (cache == null) { + addCacheIfAbsent(cacheName, SlidingExpirationCacheWithCleanupThread::new); + LOGGER.finest(Messages.get("CacheServiceImpl.autoCreatedCache", new Object[]{cacheName})); + } + + cache = caches.get(cacheName); + if (cache != null) { + cache.put(key, value, timeToLiveNs); + } + } + + @Override + public boolean setIfCacheExists(String cacheName, Object key, T value, long timeToLiveNs) { + final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); + if (cache == null) { + return false; + } + + cache.put(key, value, timeToLiveNs); + return true; + } + + @Override + public void delete(String cacheName, Object key) { + final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); + if (cache == null) { + return; + } + + cache.remove(key); + } + + // TODO: this is needed to suppress the warning about the casted return value, is it fine to suppress? + @SuppressWarnings("unchecked") + @Override + public @Nullable Map getEntries(String cacheName) { + final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); + if (cache == null) { + return null; + } + + return (Map) cache.getEntries(); + } + + @Override + public int size(String cacheName) { + final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); + if (cache == null) { + return 0; + } + + return cache.size(); + } + + @Override + public void deleteAll() { + for (SlidingExpirationCacheWithCleanupThread cache : caches.values()) { + cache.clear(); + } + + caches.clear(); + } + + @Override + public void clearAll() { + for (SlidingExpirationCacheWithCleanupThread cache : caches.values()) { + cache.clear(); + } + } + + @Override + public void clear(String cacheName) { + final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); + if (cache == null) { + return; + } + + cache.clear(); + } +} diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 9822a8129..4acdbf7f2 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -59,7 +59,7 @@ AwsWrapperDataSource.missingJdbcProtocol=Missing JDBC protocol. Could not constr AwsWrapperDataSource.missingTarget=JDBC url or Server name is required. AwsWrapperDataSource.configurationProfileNotFound=Configuration profile ''{0}'' not found. -CacheServiceImpl.autoCreatedCache=set was called +CacheServiceImpl.autoCreatedCache=A default cache for with type ''{0}'' was automatically created due to a call to set data in a non-existing cache. CacheServiceImpl.classMismatch=Mismatch between specified cache data class and actual class - [specified class: {0}, actual class: {1}, cacheName: {2}, key: {3}]. # Cluster Aware Reader Failover Handler From 6ec4c3d3dffc07f27f9c4fe25e28042d7052910f Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 5 Feb 2025 11:46:35 -0800 Subject: [PATCH 004/149] First implementation of CacheService and MonitorService complete --- .../amazon/jdbc/util/CacheServiceImpl.java | 2 +- .../amazon/jdbc/util/monitoring/Monitor.java | 2 +- .../jdbc/util/monitoring/MonitorGroup.java | 56 +++++++ .../jdbc/util/monitoring/MonitorService.java | 12 +- .../util/monitoring/MonitorServiceImpl.java | 152 ++++++++++++++++++ 5 files changed, 217 insertions(+), 7 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorGroup.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java index e601fceb1..bccf9159e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java @@ -30,7 +30,7 @@ public class CacheServiceImpl implements CacheService { protected static final Set defaultCaches = Stream.of("topology", "customEndpoint").collect(Collectors.toSet()); private static CacheServiceImpl instance; - protected ConcurrentHashMap> caches = + protected static ConcurrentHashMap> caches = new ConcurrentHashMap<>(); private CacheServiceImpl() { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index f81592aba..237065618 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -25,7 +25,7 @@ public interface Monitor { boolean isStopped(); - MonitorState getStatus(); + MonitorStatus getStatus(); MonitorException getException(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorGroup.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorGroup.java new file mode 100644 index 000000000..cf1d2c1bb --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorGroup.java @@ -0,0 +1,56 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +import java.util.Set; +import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; + +public class MonitorGroup { + private final String groupName; + private final MonitorInitializer initializer; + private final Set exceptionResponses; + private final SlidingExpirationCacheWithCleanupThread cache; + + public MonitorGroup(String groupName, MonitorInitializer initializer, + Set exceptionResponses, + SlidingExpirationCacheWithCleanupThread cache) { + this.groupName = groupName; + this.initializer = initializer; + this.exceptionResponses = exceptionResponses; + this.cache = cache; + } + + public String getGroupName() { + return groupName; + } + + public MonitorInitializer getInitializer() { + return initializer; + } + + public Set getExceptionResponses() { + return exceptionResponses; + } + + public SlidingExpirationCacheWithCleanupThread getCache() { + return cache; + } + + public int size() { + return cache.size(); + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 309ca3602..5b58d7e77 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -20,21 +20,23 @@ import java.util.function.Supplier; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; -import software.amazon.jdbc.util.notifications.NotificationListener; public interface MonitorService { - boolean registerMonitorTypeIfAbsent( + void registerMonitorTypeIfAbsent( String monitorType, Supplier> cacheSupplier, MonitorInitializer initializer, Set exceptionResponses); void runIfAbsent( - String monitorType, Object monitorKey, @Nullable NotificationListener listener, Object... initializerParams); + String monitorType, + Object monitorKey, + long timeToLiveNs, + Object... initializerParams); int numMonitors(String monitorType); - MonitorStatus getStatus(String monitorType, Object monitorKey); + @Nullable MonitorStatus getStatus(String monitorType, Object monitorKey, long timeToLiveNs); /** * Stops the given monitor and removes it from the cache of monitors. @@ -43,7 +45,7 @@ void runIfAbsent( * @param monitorKey * @return */ - Monitor stopAndRemoveMonitor(String monitorType, Object monitorKey); + void stopAndRemoveMonitor(String monitorType, Object monitorKey); /** * Stops all monitors of the given type, without deleting the cache for the monitor type. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java new file mode 100644 index 000000000..bad727412 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -0,0 +1,152 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; +import java.util.logging.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; +import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; + +public class MonitorServiceImpl implements MonitorService { + private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); + private final Map monitorGroups = new ConcurrentHashMap<>(); + private static MonitorServiceImpl instance; + + private MonitorServiceImpl() { + + } + + public static MonitorService getInstance() { + if (instance == null) { + instance = new MonitorServiceImpl(); + } + + return instance; + } + + + @Override + public void registerMonitorTypeIfAbsent(String monitorType, + Supplier> cacheSupplier, + MonitorInitializer initializer, + Set exceptionResponses) { + monitorGroups.computeIfAbsent(monitorType, + key -> new MonitorGroup(monitorType, initializer, exceptionResponses, cacheSupplier.get())); + } + + @Override + public void runIfAbsent( + String monitorType, + Object monitorKey, + long timeToLiveNs, + Object... initializerParams) { + MonitorGroup group = monitorGroups.get(monitorType); + if (group == null) { + throw new RuntimeException( + "A monitor with key '" + monitorKey + "' was requested, but the monitor type '" + + monitorType + "' does not exist."); + } + + SlidingExpirationCacheWithCleanupThread cache = group.getCache(); + cache.computeIfAbsent( + monitorKey, + key -> { + Monitor monitor = group.getInitializer().initialize(initializerParams); + monitor.start(); + return monitor; + }, + timeToLiveNs); + } + + @Override + public int numMonitors(String monitorType) { + MonitorGroup group = monitorGroups.get(monitorType); + if (group == null) { + return 0; + } + + return group.getCache().size(); + } + + @Override + public @Nullable MonitorStatus getStatus(String monitorType, Object monitorKey, long timeToLiveNs) { + MonitorGroup group = monitorGroups.get(monitorType); + if (group == null) { + return null; + } + + Monitor monitor = group.getCache().get(monitorKey, timeToLiveNs); + if (monitor == null) { + return null; + } + + return monitor.getStatus(); + } + + @Override + public void stopAndRemoveMonitor(String monitorType, Object monitorKey) { + MonitorGroup group = monitorGroups.get(monitorType); + if (group == null) { + return; + } + + Monitor monitor = group.getCache().get(monitorKey, 0); + if (monitor != null) { + group.getCache().remove(monitorKey); + monitor.stop(); + } + } + + @Override + public void stopAndRemoveMonitors(String monitorType) { + MonitorGroup group = monitorGroups.get(monitorType); + if (group == null) { + return; + } + + for (Object monitorKey : group.getCache().getEntries().keySet()) { + stopAndRemoveMonitor(monitorType, monitorKey); + } + } + + @Override + public void deleteMonitorType(String monitorType) { + MonitorGroup group = monitorGroups.remove(monitorType); + if (group != null) { + return; + } + + stopAndRemoveMonitors(monitorType); + } + + @Override + public void stopAndRemoveAll() { + for (String group : monitorGroups.keySet()) { + stopAndRemoveMonitors(group); + } + } + + @Override + public void deleteAll() { + for (String group : monitorGroups.keySet()) { + deleteMonitorType(group); + } + } +} From 7d596b7d5403f39ae5bf5e91636b4ea9ddb48a2a Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 5 Feb 2025 14:24:32 -0800 Subject: [PATCH 005/149] wip --- .../amazon/jdbc/util/monitoring/Monitor.java | 2 +- .../amazon/jdbc/util/monitoring/MonitorService.java | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index f81592aba..237065618 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -25,7 +25,7 @@ public interface Monitor { boolean isStopped(); - MonitorState getStatus(); + MonitorStatus getStatus(); MonitorException getException(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 309ca3602..5b58d7e77 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -20,21 +20,23 @@ import java.util.function.Supplier; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; -import software.amazon.jdbc.util.notifications.NotificationListener; public interface MonitorService { - boolean registerMonitorTypeIfAbsent( + void registerMonitorTypeIfAbsent( String monitorType, Supplier> cacheSupplier, MonitorInitializer initializer, Set exceptionResponses); void runIfAbsent( - String monitorType, Object monitorKey, @Nullable NotificationListener listener, Object... initializerParams); + String monitorType, + Object monitorKey, + long timeToLiveNs, + Object... initializerParams); int numMonitors(String monitorType); - MonitorStatus getStatus(String monitorType, Object monitorKey); + @Nullable MonitorStatus getStatus(String monitorType, Object monitorKey, long timeToLiveNs); /** * Stops the given monitor and removes it from the cache of monitors. @@ -43,7 +45,7 @@ void runIfAbsent( * @param monitorKey * @return */ - Monitor stopAndRemoveMonitor(String monitorType, Object monitorKey); + void stopAndRemoveMonitor(String monitorType, Object monitorKey); /** * Stops all monitors of the given type, without deleting the cache for the monitor type. From 184b68a25a2b620821a308ab6b9e1e33ed4c3a23 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 5 Feb 2025 14:43:04 -0800 Subject: [PATCH 006/149] wip --- .../amazon/jdbc/util/monitoring/Monitor.java | 2 - .../util/monitoring/MonitorException.java | 41 ------------------- .../jdbc/util/monitoring/MonitorListener.java | 21 ---------- .../jdbc/util/monitoring/MonitorStatus.java | 11 +++++ 4 files changed, 11 insertions(+), 64 deletions(-) delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorException.java delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorListener.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index 237065618..dad7c8802 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -26,6 +26,4 @@ public interface Monitor { boolean isStopped(); MonitorStatus getStatus(); - - MonitorException getException(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorException.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorException.java deleted file mode 100644 index 4df5e6501..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.util.monitoring; - -public abstract class MonitorException { - private final String monitorType; - private final Object monitorKey; - private final Throwable cause; - - public MonitorException(String monitorType, Object monitorKey, Throwable cause) { - this.monitorType = monitorType; - this.monitorKey = monitorKey; - this.cause = cause; - } - - public String getMonitorType() { - return monitorType; - } - - public Object getMonitorKey() { - return monitorKey; - } - - public Throwable getCause() { - return cause; - } -} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorListener.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorListener.java deleted file mode 100644 index 8effbe076..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorListener.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.util.monitoring; - -public interface MonitorListener { - MonitorExceptionResponse onMonitorException(MonitorException exception); -} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java index 87c5794f7..8091f1ccb 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java @@ -16,9 +16,12 @@ package software.amazon.jdbc.util.monitoring; +import org.checkerframework.checker.nullness.qual.Nullable; + public class MonitorStatus { private MonitorState state; private long lastUsedTimeNs; + private @Nullable Throwable exception; public MonitorStatus(MonitorState state, long lastUsedTimeNs) { this.state = state; @@ -40,4 +43,12 @@ public long getLastUsedTimeNs() { public void setLastUsedTimeNs(long lastUsedTimeNs) { this.lastUsedTimeNs = lastUsedTimeNs; } + + public @Nullable Throwable getException() { + return this.exception; + } + + public void setException(@Nullable Throwable exception) { + this.exception = exception; + } } From 9ebeb8a90f35977436f7abb702d8242efe251356 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 5 Feb 2025 14:43:04 -0800 Subject: [PATCH 007/149] wip --- .../amazon/jdbc/util/CacheServiceImpl.java | 161 ------------------ .../amazon/jdbc/util/monitoring/Monitor.java | 2 - .../util/monitoring/MonitorException.java | 41 ----- .../jdbc/util/monitoring/MonitorListener.java | 21 --- .../jdbc/util/monitoring/MonitorStatus.java | 11 ++ ..._advanced_jdbc_wrapper_messages.properties | 3 - 6 files changed, 11 insertions(+), 228 deletions(-) delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorException.java delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorListener.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java deleted file mode 100644 index e601fceb1..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java +++ /dev/null @@ -1,161 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.util; - -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.checkerframework.checker.nullness.qual.Nullable; - -public class CacheServiceImpl implements CacheService { - private static final Logger LOGGER = Logger.getLogger(CacheServiceImpl.class.getName()); - protected static final Set defaultCaches = - Stream.of("topology", "customEndpoint").collect(Collectors.toSet()); - private static CacheServiceImpl instance; - protected ConcurrentHashMap> caches = - new ConcurrentHashMap<>(); - - private CacheServiceImpl() { - - } - - public static CacheService getInstance() { - if (instance == null) { - instance = new CacheServiceImpl(); - } - - return instance; - } - - @Override - public void addCacheIfAbsent( - String cacheName, Supplier> cacheSupplier) { - caches.computeIfAbsent(cacheName, name -> cacheSupplier.get()); - } - - @Override - public void deleteCache(String cacheName) { - final SlidingExpirationCacheWithCleanupThread cache = caches.remove(cacheName); - if (cache != null) { - cache.clear(); - } - } - - @Override - public @Nullable T get(String cacheName, Object key, Class dataClass, long timeToLiveNs) { - final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); - if (cache == null) { - return null; - } - - final Object value = cache.get(key, timeToLiveNs); - if (dataClass.isInstance(value)) { - return dataClass.cast(value); - } - - LOGGER.fine( - Messages.get("CacheServiceImpl.classMismatch", new Object[]{dataClass, value.getClass(), cacheName, key})); - return null; - } - - @Override - public void set(String cacheName, Object key, T value, long timeToLiveNs) { - SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); - if (cache == null) { - addCacheIfAbsent(cacheName, SlidingExpirationCacheWithCleanupThread::new); - LOGGER.finest(Messages.get("CacheServiceImpl.autoCreatedCache", new Object[]{cacheName})); - } - - cache = caches.get(cacheName); - if (cache != null) { - cache.put(key, value, timeToLiveNs); - } - } - - @Override - public boolean setIfCacheExists(String cacheName, Object key, T value, long timeToLiveNs) { - final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); - if (cache == null) { - return false; - } - - cache.put(key, value, timeToLiveNs); - return true; - } - - @Override - public void delete(String cacheName, Object key) { - final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); - if (cache == null) { - return; - } - - cache.remove(key); - } - - // TODO: this is needed to suppress the warning about the casted return value, is it fine to suppress? - @SuppressWarnings("unchecked") - @Override - public @Nullable Map getEntries(String cacheName) { - final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); - if (cache == null) { - return null; - } - - return (Map) cache.getEntries(); - } - - @Override - public int size(String cacheName) { - final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); - if (cache == null) { - return 0; - } - - return cache.size(); - } - - @Override - public void deleteAll() { - for (SlidingExpirationCacheWithCleanupThread cache : caches.values()) { - cache.clear(); - } - - caches.clear(); - } - - @Override - public void clearAll() { - for (SlidingExpirationCacheWithCleanupThread cache : caches.values()) { - cache.clear(); - } - } - - @Override - public void clear(String cacheName) { - final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); - if (cache == null) { - return; - } - - cache.clear(); - } -} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index 237065618..dad7c8802 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -26,6 +26,4 @@ public interface Monitor { boolean isStopped(); MonitorStatus getStatus(); - - MonitorException getException(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorException.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorException.java deleted file mode 100644 index 4df5e6501..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorException.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.util.monitoring; - -public abstract class MonitorException { - private final String monitorType; - private final Object monitorKey; - private final Throwable cause; - - public MonitorException(String monitorType, Object monitorKey, Throwable cause) { - this.monitorType = monitorType; - this.monitorKey = monitorKey; - this.cause = cause; - } - - public String getMonitorType() { - return monitorType; - } - - public Object getMonitorKey() { - return monitorKey; - } - - public Throwable getCause() { - return cause; - } -} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorListener.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorListener.java deleted file mode 100644 index 8effbe076..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorListener.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.util.monitoring; - -public interface MonitorListener { - MonitorExceptionResponse onMonitorException(MonitorException exception); -} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java index 87c5794f7..8091f1ccb 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java @@ -16,9 +16,12 @@ package software.amazon.jdbc.util.monitoring; +import org.checkerframework.checker.nullness.qual.Nullable; + public class MonitorStatus { private MonitorState state; private long lastUsedTimeNs; + private @Nullable Throwable exception; public MonitorStatus(MonitorState state, long lastUsedTimeNs) { this.state = state; @@ -40,4 +43,12 @@ public long getLastUsedTimeNs() { public void setLastUsedTimeNs(long lastUsedTimeNs) { this.lastUsedTimeNs = lastUsedTimeNs; } + + public @Nullable Throwable getException() { + return this.exception; + } + + public void setException(@Nullable Throwable exception) { + this.exception = exception; + } } diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 4acdbf7f2..538995b68 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -59,9 +59,6 @@ AwsWrapperDataSource.missingJdbcProtocol=Missing JDBC protocol. Could not constr AwsWrapperDataSource.missingTarget=JDBC url or Server name is required. AwsWrapperDataSource.configurationProfileNotFound=Configuration profile ''{0}'' not found. -CacheServiceImpl.autoCreatedCache=A default cache for with type ''{0}'' was automatically created due to a call to set data in a non-existing cache. -CacheServiceImpl.classMismatch=Mismatch between specified cache data class and actual class - [specified class: {0}, actual class: {1}, cacheName: {2}, key: {3}]. - # Cluster Aware Reader Failover Handler ClusterAwareReaderFailoverHandler.interruptedThread=Thread was interrupted. ClusterAwareReaderFailoverHandler.attemptingReaderConnection=Trying to connect to host: ''{0}'', with properties ''{1}'' From 84760dc294939e6c748645e4e2dd88de073090c4 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 6 Feb 2025 15:49:55 -0800 Subject: [PATCH 008/149] Refactor interfaces based on feedback --- .../amazon/jdbc/util/CacheService.java | 93 ------------------- ...ionListener.java => ItemDisposalFunc.java} | 11 ++- ...nitializer.java => ShouldDisposeFunc.java} | 13 ++- .../jdbc/util/SlidingExpirationCache.java | 19 ---- .../amazon/jdbc/util/StorageService.java | 89 ++++++++++++++++++ .../jdbc/util/events/EventPublisher.java | 42 +++++++++ .../EventSubscriber.java} | 17 ++-- .../amazon/jdbc/util/monitoring/Monitor.java | 4 - ...esponse.java => MonitorErrorResponse.java} | 2 +- .../jdbc/util/monitoring/MonitorService.java | 67 ++++++------- .../jdbc/util/monitoring/MonitorState.java | 3 +- .../notifications/NotificationService.java | 27 ------ .../jdbc/util/SlidingExpirationCacheTest.java | 4 +- 13 files changed, 197 insertions(+), 194 deletions(-) delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/CacheService.java rename wrapper/src/main/java/software/amazon/jdbc/util/{notifications/NotificationListener.java => ItemDisposalFunc.java} (71%) rename wrapper/src/main/java/software/amazon/jdbc/util/{monitoring/MonitorInitializer.java => ShouldDisposeFunc.java} (68%) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/StorageService.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java rename wrapper/src/main/java/software/amazon/jdbc/util/{notifications/Notification.java => events/EventSubscriber.java} (66%) rename wrapper/src/main/java/software/amazon/jdbc/util/monitoring/{MonitorExceptionResponse.java => MonitorErrorResponse.java} (94%) delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationService.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CacheService.java b/wrapper/src/main/java/software/amazon/jdbc/util/CacheService.java deleted file mode 100644 index 15732c5cf..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CacheService.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.util; - -import java.util.Map; -import java.util.function.Supplier; - -public interface CacheService { - /** - * Adds a cache to CacheService if it does not already exist. - * - * @param cacheName - * @param cacheSupplier - */ - void addCacheIfAbsent( - String cacheName, Supplier> cacheSupplier); - - /** - * Clears the given cache without deleting it from the CacheService. To delete the cache as well, use - * {@link #deleteCache(String)}. - * - * @param cacheName - */ - void clear(String cacheName); - - void deleteCache(String cacheName); - - // TODO: in the current code it seems the time-to-live we pass to get/set is always the same, would it be better to - // pass this in addCacheIfAbsent instead of every single get/set call? Less generic but more convenient. - T get(String cacheName, Object key, Class dataClass, long timeToLiveNs); - - /** - * Sets a value in the specified cache. If the specified cache does not exist, it will be created with default - * settings. - * - * @param cacheName - * @param key - * @param value - * @param timeToLiveNs - * @param - */ - void set(String cacheName, Object key, T value, long timeToLiveNs); - - /** - * Sets a value in the specified cache if it exists. Otherwise, returns false without doing anything. This method can - * be used if the caller does not want to create a default cache when the specified cache does not already exist. - * - * @param cacheName - * @param key - * @param value - * @param timeToLiveNs - * @return - * @param - */ - boolean setIfCacheExists(String cacheName, Object key, T value, long timeToLiveNs); - - void delete(String cacheName, Object key); - - /** - * Returns a copy of the entries in the given cache. - * - * @param cacheName - * @return - * @param - */ - Map getEntries(String cacheName); - - int size(String cacheName); - - /** - * Deletes all caches from the CacheService. - */ - void deleteAll(); - - /** - * Clears all data from all caches without deleting the caches themselves. - */ - void clearAll(); -} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationListener.java b/wrapper/src/main/java/software/amazon/jdbc/util/ItemDisposalFunc.java similarity index 71% rename from wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationListener.java rename to wrapper/src/main/java/software/amazon/jdbc/util/ItemDisposalFunc.java index 6baf6b253..f0ad7ac39 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationListener.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ItemDisposalFunc.java @@ -14,8 +14,13 @@ * limitations under the License. */ -package software.amazon.jdbc.util.notifications; +package software.amazon.jdbc.util; -public interface NotificationListener { - void processNotification(Notification notification); +/** + * An optional function defining extra cleanup steps to take when a cache item is cleaned up. + * + * @param the type of object being disposed + */ +public interface ItemDisposalFunc { + void dispose(V item); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorInitializer.java b/wrapper/src/main/java/software/amazon/jdbc/util/ShouldDisposeFunc.java similarity index 68% rename from wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorInitializer.java rename to wrapper/src/main/java/software/amazon/jdbc/util/ShouldDisposeFunc.java index a43e1338a..3cdaa035c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorInitializer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ShouldDisposeFunc.java @@ -14,9 +14,14 @@ * limitations under the License. */ -package software.amazon.jdbc.util.monitoring; +package software.amazon.jdbc.util; -@FunctionalInterface -public interface MonitorInitializer { - Monitor initialize(Object... params); +/** + * An optional function defining the conditions under which an expired entry should be cleaned up + * at cleanup time. + * + * @param the type of object being analyzed for disposal + */ +public interface ShouldDisposeFunc { + boolean shouldDispose(V item); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/SlidingExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/SlidingExpirationCache.java index 015780948..7ba3650c3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/SlidingExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/SlidingExpirationCache.java @@ -214,25 +214,6 @@ public void setCleanupIntervalNanos(long cleanupIntervalNanos) { this.cleanupTimeNanos.set(System.nanoTime() + cleanupIntervalNanos); } - /** - * An optional function defining the conditions under which an expired entry should be cleaned up - * at cleanup time. - * - * @param the type of object being analyzed for disposal - */ - public interface ShouldDisposeFunc { - boolean shouldDispose(V item); - } - - /** - * An optional function defining extra cleanup steps to take when a cache item is cleaned up. - * - * @param the type of object being disposed - */ - public interface ItemDisposalFunc { - void dispose(V item); - } - // For testing purposes only Map getCache() { return cache; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/StorageService.java new file mode 100644 index 000000000..663077ac8 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/StorageService.java @@ -0,0 +1,89 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util; + +import org.checkerframework.checker.nullness.qual.Nullable; +import software.amazon.jdbc.plugin.customendpoint.CustomEndpointInfo; + +public interface StorageService { + /** + * Register a new item category with the storage service. This method needs to be called before adding new categories + * of items to the storage service, so that the storage service knows when and how to dispose of the item. Expected + * item categories ("topology" and "customEndpoint") will be added automatically during driver initialization, but + * this method can be called by users if they want to add a new category. + * + * @param itemCategory a String representing the item category, eg "customEndpoint". + * @param itemClass the class of item that will be stored under this category, eg `CustomEndpointInfo.class`. + * @param cleanupIntervalNs how often the item category should be cleaned of expired entries, in nanoseconds. + * @param expirationTimeNs how long an item should be stored before expiring, in nanoseconds. + * @param shouldDisposeFunc a function defining whether an item should be disposed if expired. If `null` is passed, + * the item will always be disposed if expired. + * @param itemDisposalFunc a function defining how to dispose of an item when it is removed. If `null` is passed, the + * item will be removed without performing any additional operations. + * @param the type of item that will be stored under this category, eg {@link CustomEndpointInfo}. + */ + void registerItemCategoryIfAbsent( + String itemCategory, + Class itemClass, + long cleanupIntervalNs, + long expirationTimeNs, + @Nullable ShouldDisposeFunc shouldDisposeFunc, + @Nullable ItemDisposalFunc itemDisposalFunc); + + /** + * Stores an item under the given category. + * + * @param itemCategory a String representing the item category, eg "customEndpoint". + * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". + * @param item the item to store. + * @param the type of the item being stored. + */ + void set(String itemCategory, Object key, T item); + + /** + * Gets an item stored under the given category. + * + * @param itemCategory a String representing the item category, eg "customEndpoint". + * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". + * @param the type of the item being retrieved. + * @return the item stored at the given key under the given category. + */ + @Nullable T get(String itemCategory, Object key); + + /** + * Removes an item stored under the given category. + * + * @param itemCategory a String representing the item category, eg "customEndpoint". + * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". + * @param the type of the item being removed. + * @return the item removed from the storage service. + */ + @Nullable T remove(String itemCategory, Object key); + + /** + * Clears all items from the given category. For example, storageService.clear("customEndpoint") will remove all + * custom endpoint information from the storage service. + * + * @param itemCategory a String representing the item category, eg "customEndpoint". + */ + void clear(String itemCategory); + + /** + * Clears all information from all categories of the storage service. + */ + void clearAll(); +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java new file mode 100644 index 000000000..03fcb2026 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java @@ -0,0 +1,42 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.events; + +public interface EventPublisher { + /** + * Register the given subscriber for the given event classes. + * + * @param subscriber the subscriber to be notified when the given event classes occur. + * @param eventClasses the classes of event that the subscriber should be notified of. + */ + void subscribe(EventSubscriber subscriber, Class... eventClasses); + + /** + * Unsubscribe the subscriber from the given event classes. + * + * @param subscriber the subscriber to unsubscribe from the given event classes. + * @param eventClasses the classes of events that the subscriber wants to unsubscribe from. + */ + void unsubscribe(EventSubscriber subscriber, Class... eventClasses); + + /** + * Publish an event. All subscribers to the given event class will be notified of the event. + * + * @param event the event to publish. + */ + void publish(Object event); +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/notifications/Notification.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java similarity index 66% rename from wrapper/src/main/java/software/amazon/jdbc/util/notifications/Notification.java rename to wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java index 62a71e1b4..fadcd3155 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/notifications/Notification.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java @@ -14,13 +14,14 @@ * limitations under the License. */ -package software.amazon.jdbc.util.notifications; +package software.amazon.jdbc.util.events; -public interface Notification { - - String getPublisherId(); - - Throwable getThrowable(); - - String getEventType(); +public interface EventSubscriber { + /** + * Process an event. This method will only be called on this subscriber if it has subscribed to the event class via + * {@link EventPublisher#subscribe}. + * + * @param event the event to process. + */ + void processEvent(Object event); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index dad7c8802..b20f0ceda 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -21,9 +21,5 @@ public interface Monitor { void stop(); - void restart(); - - boolean isStopped(); - MonitorStatus getStatus(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorExceptionResponse.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorErrorResponse.java similarity index 94% rename from wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorExceptionResponse.java rename to wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorErrorResponse.java index 85f22326b..9c0b2a9d8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorExceptionResponse.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorErrorResponse.java @@ -16,7 +16,7 @@ package software.amazon.jdbc.util.monitoring; -public enum MonitorExceptionResponse { +public enum MonitorErrorResponse { NO_ACTION, RESTART } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 5b58d7e77..464d8a339 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -19,55 +19,60 @@ import java.util.Set; import java.util.function.Supplier; import org.checkerframework.checker.nullness.qual.Nullable; -import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; +import software.amazon.jdbc.util.ShouldDisposeFunc; public interface MonitorService { + /** + * Register a new monitor type with the monitor service. This method needs to be called before adding new types of + * monitors to the monitor service, so that the monitor service knows when a running monitor should be stopped. + * Expected monitor types ("topology" and "customEndpoint") will be added automatically during driver initialization, + * but this method can be called by users if they want to add a new monitor type. + * + * @param monitorType a String representing the monitor type, eg "customEndpoint". + * @param errorResponses a Set defining actions to take if the monitor is in an error state. + * @param expirationTimeNs how long a monitor should be stored before expiring, in nanoseconds. If the monitor is + * expired and shouldDisposeFunc returns `true`, the monitor will be stopped. + * @param shouldDisposeFunc a function defining whether an item should be stopped if expired. If `null` is passed, the + * monitor will always be stopped if the monitor is expired. + */ void registerMonitorTypeIfAbsent( String monitorType, - Supplier> cacheSupplier, - MonitorInitializer initializer, - Set exceptionResponses); - - void runIfAbsent( - String monitorType, - Object monitorKey, - long timeToLiveNs, - Object... initializerParams); - - int numMonitors(String monitorType); - - @Nullable MonitorStatus getStatus(String monitorType, Object monitorKey, long timeToLiveNs); + Set errorResponses, + long expirationTimeNs, + @Nullable ShouldDisposeFunc shouldDisposeFunc); /** - * Stops the given monitor and removes it from the cache of monitors. + * Creates and starts the given monitor if it does not already exist and stores it under the given monitor type and + * key. * - * @param monitorType - * @param monitorKey - * @return + * @param monitorType a String representing the monitor type, eg "customEndpoint". + * @param key the key for the monitor, eg + * "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". + * @param monitorSupplier an initialization lambda that can be used to create the monitor if it is absent. */ - void stopAndRemoveMonitor(String monitorType, Object monitorKey); + void runIfAbsent( + String monitorType, + Object key, + Supplier monitorSupplier); /** - * Stops all monitors of the given type, without deleting the cache for the monitor type. + * Stops the given monitor and removes it from the monitor service. * - * @param monitorType + * @param monitorType a String representing the monitor type, eg "customEndpoint". + * @param key the key for the monitor, eg + * "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". */ - void stopAndRemoveMonitors(String monitorType); + void stopAndRemove(String monitorType, Object key); /** - * Stops all monitors of the given type and deletes the cache for the monitor type. + * Stops all monitors for the given type and removes them from the monitor service. * - * @param monitorType + * @param monitorType a String representing the monitor type, eg "customEndpoint". */ - void deleteMonitorType(String monitorType); + void stopAndRemoveMonitors(String monitorType); /** - * Stops all monitors and removes them from their caches, without deleting the caches themselves. + * Stops all monitors and removes them from the monitor service. */ void stopAndRemoveAll(); - - /** - * Stops all monitors and deletes all monitor caches. - */ - void deleteAll(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java index e7c41d803..62790e1f5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java @@ -18,7 +18,6 @@ public enum MonitorState { RUNNING, - IDLE, STOPPED, - EXCEPTION + ERROR } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationService.java b/wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationService.java deleted file mode 100644 index e5d143147..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/util/notifications/NotificationService.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.util.notifications; - -import java.util.function.Predicate; - -public interface NotificationService { - void notifySubscribers(Notification notification); - - void subscribe(NotificationListener listener, Predicate isSubscribedFunc); - - void unsubscribe(NotificationListener listener); -} diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/SlidingExpirationCacheTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/SlidingExpirationCacheTest.java index 8878219c8..f9f265212 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/util/SlidingExpirationCacheTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/util/SlidingExpirationCacheTest.java @@ -34,8 +34,8 @@ import org.mockito.MockitoAnnotations; public class SlidingExpirationCacheTest { - @Mock SlidingExpirationCache.ItemDisposalFunc mockDisposalFunc; - @Mock SlidingExpirationCache.ShouldDisposeFunc mockShouldDisposeFunc; + @Mock ItemDisposalFunc mockDisposalFunc; + @Mock ShouldDisposeFunc mockShouldDisposeFunc; private AutoCloseable closeable; @BeforeEach From 44a530fcb488b007e851309bca35a2384d0c525e Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 7 Feb 2025 09:45:15 -0800 Subject: [PATCH 009/149] Add example event --- .../events/WriterChangedExampleEvent.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/events/WriterChangedExampleEvent.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/WriterChangedExampleEvent.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/WriterChangedExampleEvent.java new file mode 100644 index 000000000..d6e215966 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/WriterChangedExampleEvent.java @@ -0,0 +1,35 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.events; + +public class WriterChangedExampleEvent { + final String newWriterUrl; + final String oldWriterUrl; + + public WriterChangedExampleEvent(String newWriterUrl, String oldWriterUrl) { + this.newWriterUrl = newWriterUrl; + this.oldWriterUrl = oldWriterUrl; + } + + public String getNewWriterUrl() { + return newWriterUrl; + } + + public String getOldWriterUrl() { + return oldWriterUrl; + } +} From cfa82ad8b6783c5bfb23bf545b1fc7362539aefc Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 7 Feb 2025 13:45:46 -0800 Subject: [PATCH 010/149] Add StorageService#exists method --- .../java/software/amazon/jdbc/util/StorageService.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/StorageService.java index 663077ac8..49a11e694 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/StorageService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/StorageService.java @@ -74,6 +74,15 @@ void registerItemCategoryIfAbsent( */ @Nullable T remove(String itemCategory, Object key); + /** + * Indicates whether an item exists under the given item category and key. + * + * @param itemCategory a String representing the item category, eg "customEndpoint". + * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". + * @return true if the item exists under the given item category and key, otherwise returns false. + */ + boolean exists(String itemCategory, Object key); + /** * Clears all items from the given category. For example, storageService.clear("customEndpoint") will remove all * custom endpoint information from the storage service. From ed1667b2685d93115cb878c8505cff8d617347a1 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 7 Feb 2025 14:33:52 -0800 Subject: [PATCH 011/149] Add Event interface --- .../amazon/jdbc/util/events/Event.java | 21 +++++++++++++++++++ .../jdbc/util/events/EventPublisher.java | 2 +- .../jdbc/util/events/EventSubscriber.java | 2 +- .../events/WriterChangedExampleEvent.java | 2 +- 4 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/events/Event.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/Event.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/Event.java new file mode 100644 index 000000000..891972603 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/Event.java @@ -0,0 +1,21 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.events; + +// A marker interface for events that need to be communicated between different components. +public interface Event { +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java index 03fcb2026..e18f36a22 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java @@ -38,5 +38,5 @@ public interface EventPublisher { * * @param event the event to publish. */ - void publish(Object event); + void publish(Event event); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java index fadcd3155..4cf7a25fd 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java @@ -23,5 +23,5 @@ public interface EventSubscriber { * * @param event the event to process. */ - void processEvent(Object event); + void processEvent(Event event); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/WriterChangedExampleEvent.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/WriterChangedExampleEvent.java index d6e215966..07a08924f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/WriterChangedExampleEvent.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/WriterChangedExampleEvent.java @@ -16,7 +16,7 @@ package software.amazon.jdbc.util.events; -public class WriterChangedExampleEvent { +public class WriterChangedExampleEvent implements Event { final String newWriterUrl; final String oldWriterUrl; From 7e6fe25b0e94488d0f9ba5994c8f0c424f7280f1 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 7 Feb 2025 14:42:25 -0800 Subject: [PATCH 012/149] Move StorageService into its own 'storage' package --- .../amazon/jdbc/util/{ => storage}/StorageService.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) rename wrapper/src/main/java/software/amazon/jdbc/util/{ => storage}/StorageService.java (96%) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java similarity index 96% rename from wrapper/src/main/java/software/amazon/jdbc/util/StorageService.java rename to wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java index 49a11e694..a2737fa3e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/StorageService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java @@ -14,10 +14,12 @@ * limitations under the License. */ -package software.amazon.jdbc.util; +package software.amazon.jdbc.util.storage; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.plugin.customendpoint.CustomEndpointInfo; +import software.amazon.jdbc.util.ItemDisposalFunc; +import software.amazon.jdbc.util.ShouldDisposeFunc; public interface StorageService { /** From a12410fba30b1d90f33d4bcb3ba44b6e8a2414bb Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 7 Feb 2025 15:06:21 -0800 Subject: [PATCH 013/149] wip --- .../StorageServiceImpl.java} | 36 ++++++++++++------- 1 file changed, 24 insertions(+), 12 deletions(-) rename wrapper/src/main/java/software/amazon/jdbc/util/{CacheServiceImpl.java => storage/StorageServiceImpl.java} (78%) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java similarity index 78% rename from wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java rename to wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index bccf9159e..617c93650 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CacheServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -14,8 +14,9 @@ * limitations under the License. */ -package software.amazon.jdbc.util; +package software.amazon.jdbc.util.storage; +import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -24,30 +25,41 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import org.checkerframework.checker.nullness.qual.Nullable; - -public class CacheServiceImpl implements CacheService { - private static final Logger LOGGER = Logger.getLogger(CacheServiceImpl.class.getName()); - protected static final Set defaultCaches = - Stream.of("topology", "customEndpoint").collect(Collectors.toSet()); - private static CacheServiceImpl instance; +import software.amazon.jdbc.util.ItemDisposalFunc; +import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ShouldDisposeFunc; +import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; + +public class StorageServiceImpl implements StorageService { + private static final Logger LOGGER = Logger.getLogger(StorageServiceImpl.class.getName()); + public static final String TOPOLOGY = "topology"; + public static final String CUSTOM_ENDPOINT = "customEndpoint"; + public static final Set defaultCaches = + Collections.unmodifiableSet(Stream.of(TOPOLOGY, CUSTOM_ENDPOINT).collect(Collectors.toSet())); + private static StorageServiceImpl instance; protected static ConcurrentHashMap> caches = new ConcurrentHashMap<>(); - private CacheServiceImpl() { + private StorageServiceImpl() { } - public static CacheService getInstance() { + public static StorageService getInstance() { if (instance == null) { - instance = new CacheServiceImpl(); + instance = new StorageServiceImpl(); } return instance; } @Override - public void addCacheIfAbsent( - String cacheName, Supplier> cacheSupplier) { + public void registerItemCategoryIfAbsent( + String itemCategory, + Class itemClass, + long cleanupIntervalNs, + long expirationTimeNs, + @Nullable ShouldDisposeFunc shouldDisposeFunc, + @Nullable ItemDisposalFunc itemDisposalFunc) { caches.computeIfAbsent(cacheName, name -> cacheSupplier.get()); } From 3e1752085610affa5cf08194a8ced44fadbfc93d Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 7 Feb 2025 15:38:23 -0800 Subject: [PATCH 014/149] PR comments --- .../jdbc/util/events/EventPublisher.java | 6 ++++-- .../jdbc/util/monitoring/MonitorStatus.java | 21 +++++-------------- 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java index e18f36a22..694d07895 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java @@ -16,6 +16,8 @@ package software.amazon.jdbc.util.events; +import java.util.Set; + public interface EventPublisher { /** * Register the given subscriber for the given event classes. @@ -23,7 +25,7 @@ public interface EventPublisher { * @param subscriber the subscriber to be notified when the given event classes occur. * @param eventClasses the classes of event that the subscriber should be notified of. */ - void subscribe(EventSubscriber subscriber, Class... eventClasses); + void subscribe(EventSubscriber subscriber, Set> eventClasses); /** * Unsubscribe the subscriber from the given event classes. @@ -31,7 +33,7 @@ public interface EventPublisher { * @param subscriber the subscriber to unsubscribe from the given event classes. * @param eventClasses the classes of events that the subscriber wants to unsubscribe from. */ - void unsubscribe(EventSubscriber subscriber, Class... eventClasses); + void unsubscribe(EventSubscriber subscriber, Set> eventClasses); /** * Publish an event. All subscribers to the given event class will be notified of the event. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java index 8091f1ccb..2fb4a3c14 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java @@ -19,36 +19,25 @@ import org.checkerframework.checker.nullness.qual.Nullable; public class MonitorStatus { - private MonitorState state; - private long lastUsedTimeNs; - private @Nullable Throwable exception; + private final MonitorState state; + private final long lastUsedTimeNs; + private final @Nullable Throwable exception; - public MonitorStatus(MonitorState state, long lastUsedTimeNs) { + public MonitorStatus(MonitorState state, long lastUsedTimeNs, @Nullable Throwable exception) { this.state = state; this.lastUsedTimeNs = lastUsedTimeNs; + this.exception = exception; } public MonitorState getState() { return state; } - public void setState(MonitorState state) { - this.state = state; - } - public long getLastUsedTimeNs() { return lastUsedTimeNs; } - public void setLastUsedTimeNs(long lastUsedTimeNs) { - this.lastUsedTimeNs = lastUsedTimeNs; - } - public @Nullable Throwable getException() { return this.exception; } - - public void setException(@Nullable Throwable exception) { - this.exception = exception; - } } From 07b36fcca774e507d46cae0f3431410d78057509 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 11 Feb 2025 14:04:20 -0800 Subject: [PATCH 015/149] Implemented ExpirationCache and StorageServiceImpl --- .../jdbc/util/storage/ExpirationCache.java | 315 ++++++++++++++++++ .../jdbc/util/storage/StorageService.java | 51 +-- .../jdbc/util/storage/StorageServiceImpl.java | 153 ++++----- ..._advanced_jdbc_wrapper_messages.properties | 5 + 4 files changed, 408 insertions(+), 116 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java new file mode 100644 index 000000000..e12ca9cd2 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -0,0 +1,315 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.storage; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import java.util.logging.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; +import software.amazon.jdbc.util.ItemDisposalFunc; +import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ShouldDisposeFunc; + +public class ExpirationCache { + private static final Logger LOGGER = + Logger.getLogger(ExpirationCache.class.getName()); + + protected final ExecutorService cleanupThreadPool = Executors.newFixedThreadPool(1, runnableTarget -> { + final Thread monitoringThread = new Thread(runnableTarget); + monitoringThread.setDaemon(true); + return monitoringThread; + }); + + protected final Map cache = new ConcurrentHashMap<>(); + protected final Class valueClass; + protected final boolean isRenewableExpiration; + protected final long cleanupIntervalNanos; + protected final long timeToLiveNanos; + protected final AtomicReference> shouldDisposeFunc = new AtomicReference<>(null); + protected final ItemDisposalFunc itemDisposalFunc; + + public ExpirationCache( + final Class valueClass, + final boolean isRenewableExpiration, + final long cleanupIntervalNanos, + final long timeToLiveNanos, + final @Nullable ShouldDisposeFunc shouldDisposeFunc, + final @Nullable ItemDisposalFunc itemDisposalFunc) { + this.valueClass = valueClass; + this.isRenewableExpiration = isRenewableExpiration; + this.cleanupIntervalNanos = cleanupIntervalNanos; + this.timeToLiveNanos = timeToLiveNanos; + this.shouldDisposeFunc.set(shouldDisposeFunc); + this.itemDisposalFunc = itemDisposalFunc; + this.initCleanupThread(); + } + + protected void initCleanupThread() { + cleanupThreadPool.submit(() -> { + while (!Thread.currentThread().isInterrupted()) { + try { + TimeUnit.NANOSECONDS.sleep(this.cleanupIntervalNanos); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + LOGGER.fine(Messages.get("ExpirationCache.cleanupThreadInterrupted")); + break; + } + + LOGGER.finest("ExpirationCache.cleaningUpCache"); + cache.forEach((key, value) -> { + try { + removeIfExpired(key); + } catch (Exception ex) { + // ignore + } + }); + } + }); + cleanupThreadPool.shutdown(); + } + + /** + * In addition to performing the logic defined by {@link Map#computeIfAbsent}, cleans up expired + * entries if we have hit cleanup time. If an expired entry is requested and we have not hit + * cleanup time or {@link ShouldDisposeFunc} indicated the entry should not be closed, the entry + * will be marked as non-expired. + * + * @param key the key with which the specified value is to be associated + * @param mappingFunction the function to compute a value + * @return the current (existing or computed) value associated with the specified key, or null if + * the computed value is null. + */ + public @Nullable V computeIfAbsent( + final K key, + Function mappingFunction) { + final CacheItem cacheItem = cache.computeIfAbsent( + key, + k -> new CacheItem( + mappingFunction.apply(k), + System.nanoTime() + this.timeToLiveNanos)); + + if (this.isRenewableExpiration) { + cacheItem.extendExpiration(this.timeToLiveNanos); + } else if (cacheItem.shouldCleanup()) { + return null; + } + + return cacheItem.item; + } + + public @Nullable V put( + final K key, + final V value) { + final CacheItem + cacheItem = cache.put(key, new CacheItem(value, System.nanoTime() + this.timeToLiveNanos)); + if (cacheItem == null) { + return null; + } + + // cacheItem is the previous value associated with the key. Since it has now been replaced with the new value, + // its expiration does not need to be extended. + if (cacheItem.shouldCleanup()) { + return null; + } + + return cacheItem.item; + } + + public @Nullable V get(final K key) { + final CacheItem cacheItem = cache.get(key); + if (cacheItem == null) { + return null; + } + + if (this.isRenewableExpiration) { + cacheItem.extendExpiration(this.timeToLiveNanos); + } else if (cacheItem.shouldCleanup()) { + return null; + } + + return cacheItem.item; + } + + public boolean exists(final K key) { + final CacheItem cacheItem = cache.get(key); + return cacheItem != null && !cacheItem.shouldCleanup(); + } + + /** + * Cleanup expired entries if we have hit the cleanup time, then remove and dispose the value + * associated with the given key. + * + * @param key the key associated with the value to be removed/disposed + * @return the value removed from the cache. If the value was expired, it will still be returned. + */ + public @Nullable V remove(final K key) { + return removeAndDispose(key); + } + + protected @Nullable V removeAndDispose(K key) { + final CacheItem cacheItem = cache.remove(key); + if (cacheItem == null) { + return null; + } + + if (itemDisposalFunc != null) { + itemDisposalFunc.dispose(cacheItem.item); + } + + return cacheItem.item; + } + + protected void removeIfExpired(K key) { + // A list is used to store the cached item for later disposal since lambdas require references to outer variables + // to be final. This allows us to dispose of the item after it has been removed and the cache has been unlocked, + // which is important because the disposal function may be long-running. + final List itemList = new ArrayList<>(1); + cache.computeIfPresent(key, (k, cacheItem) -> { + if (cacheItem.shouldCleanup()) { + itemList.add(cacheItem.item); + // Removes the item from the cache map. + return null; + } + + return cacheItem; + }); + + if (itemList.isEmpty()) { + return; + } + + V item = itemList.get(0); + if (item != null && itemDisposalFunc != null) { + itemDisposalFunc.dispose(item); + } + } + + /** + * Remove and dispose of all entries in the cache. + */ + public void clear() { + for (K key : cache.keySet()) { + removeAndDispose(key); + } + cache.clear(); + } + + /** + * Get a map copy of all entries in the cache, including expired entries. + * + * @return a map copy of all entries in the cache, including expired entries + */ + public Map getEntries() { + final Map entries = new HashMap<>(); + for (final Map.Entry entry : this.cache.entrySet()) { + entries.put(entry.getKey(), entry.getValue().item); + } + return entries; + } + + /** + * Get the current size of the cache, including expired entries. + * + * @return the current size of the cache, including expired entries. + */ + public int size() { + return this.cache.size(); + } + + public Class getValueClass() { + return this.valueClass; + } + + protected class CacheItem { + private final V item; + private long expirationTimeNanos; + + /** + * CacheItem constructor. + * + * @param item the item value + * @param expirationTimeNanos the amount of time before a CacheItem should be marked as expired. + */ + public CacheItem(final V item, final long expirationTimeNanos) { + this.item = item; + this.expirationTimeNanos = expirationTimeNanos; + } + + /** + * Determines if a cache item should be cleaned up. An item should be cleaned up if it has past + * its expiration time and {@link ShouldDisposeFunc} (if defined) indicates that it should be + * cleaned up. + * + * @return true if the cache item should be cleaned up at cleanup time. Otherwise, returns + * false. + */ + boolean shouldCleanup() { + final boolean isExpired = this.expirationTimeNanos != 0 && System.nanoTime() > this.expirationTimeNanos; + final ShouldDisposeFunc tempShouldDisposeFunc = shouldDisposeFunc.get(); + if (tempShouldDisposeFunc != null) { + return isExpired && tempShouldDisposeFunc.shouldDispose(this.item); + } + return isExpired; + } + + /** + * Renew a cache item's expiration time and return the value. + * + * @param timeToLiveNanos the new expiration duration for the item + */ + public void extendExpiration(final long timeToLiveNanos) { + this.expirationTimeNanos = System.nanoTime() + timeToLiveNanos; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((item == null) ? 0 : item.hashCode()); + return result; + } + + @Override + @SuppressWarnings("unchecked") + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + // First check null and type (use instanceof for correct type checking) + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + CacheItem other = (CacheItem) obj; + return this.item.equals(other.item) && this.expirationTimeNanos == other.expirationTimeNanos; + } + + @Override + public String toString() { + return "CacheItem [item=" + item + ", expirationTime=" + expirationTimeNanos + "]"; + } + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java index a2737fa3e..067922eaf 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java @@ -17,7 +17,6 @@ package software.amazon.jdbc.util.storage; import org.checkerframework.checker.nullness.qual.Nullable; -import software.amazon.jdbc.plugin.customendpoint.CustomEndpointInfo; import software.amazon.jdbc.util.ItemDisposalFunc; import software.amazon.jdbc.util.ShouldDisposeFunc; @@ -28,23 +27,27 @@ public interface StorageService { * item categories ("topology" and "customEndpoint") will be added automatically during driver initialization, but * this method can be called by users if they want to add a new category. * - * @param itemCategory a String representing the item category, eg "customEndpoint". - * @param itemClass the class of item that will be stored under this category, eg `CustomEndpointInfo.class`. - * @param cleanupIntervalNs how often the item category should be cleaned of expired entries, in nanoseconds. - * @param expirationTimeNs how long an item should be stored before expiring, in nanoseconds. - * @param shouldDisposeFunc a function defining whether an item should be disposed if expired. If `null` is passed, - * the item will always be disposed if expired. - * @param itemDisposalFunc a function defining how to dispose of an item when it is removed. If `null` is passed, the - * item will be removed without performing any additional operations. - * @param the type of item that will be stored under this category, eg {@link CustomEndpointInfo}. + * @param itemCategory a String representing the item category, eg "customEndpoint". + * @param itemClass the class of item that will be stored under this category, eg `CustomEndpointInfo + * .class`. + * @param isRenewableExpiration controls whether the item's expiration should be renewed if the item is fetched, + * regardless of whether it is already expired or not. + * @param cleanupIntervalNanos how often the item category should be cleaned of expired entries, in nanoseconds. + * @param expirationTimeoutNanos how long an item should be stored before expiring, in nanoseconds. + * @param shouldDisposeFunc a function defining whether an item should be disposed if expired. If null is passed, + * the item will always be disposed if expired. + * @param itemDisposalFunc a function defining how to dispose of an item when it is removed. If null is + * passed, the + * item will be removed without performing any additional operations. */ - void registerItemCategoryIfAbsent( + void registerItemCategoryIfAbsent( String itemCategory, - Class itemClass, - long cleanupIntervalNs, - long expirationTimeNs, - @Nullable ShouldDisposeFunc shouldDisposeFunc, - @Nullable ItemDisposalFunc itemDisposalFunc); + Class itemClass, + boolean isRenewableExpiration, + long cleanupIntervalNanos, + long expirationTimeoutNanos, + @Nullable ShouldDisposeFunc shouldDisposeFunc, + @Nullable ItemDisposalFunc itemDisposalFunc); /** * Stores an item under the given category. @@ -54,7 +57,7 @@ void registerItemCategoryIfAbsent( * @param item the item to store. * @param the type of the item being stored. */ - void set(String itemCategory, Object key, T item); + void set(String itemCategory, Object key, Object item); /** * Gets an item stored under the given category. @@ -67,23 +70,23 @@ void registerItemCategoryIfAbsent( @Nullable T get(String itemCategory, Object key); /** - * Removes an item stored under the given category. + * Indicates whether an item exists under the given item category and key. * * @param itemCategory a String representing the item category, eg "customEndpoint". * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". - * @param the type of the item being removed. - * @return the item removed from the storage service. + * @return true if the item exists under the given item category and key, otherwise returns false. */ - @Nullable T remove(String itemCategory, Object key); + boolean exists(String itemCategory, Object key); /** - * Indicates whether an item exists under the given item category and key. + * Removes an item stored under the given category. * * @param itemCategory a String representing the item category, eg "customEndpoint". * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". - * @return true if the item exists under the given item category and key, otherwise returns false. + * @param the type of the item being removed. + * @return the item removed from the storage service. */ - boolean exists(String itemCategory, Object key); + @Nullable T remove(String itemCategory, Object key); /** * Clears all items from the given category. For example, storageService.clear("customEndpoint") will remove all diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 617c93650..992fed081 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -16,158 +16,127 @@ package software.amazon.jdbc.util.storage; -import java.util.Collections; import java.util.Map; -import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.util.ItemDisposalFunc; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ShouldDisposeFunc; -import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; public class StorageServiceImpl implements StorageService { private static final Logger LOGGER = Logger.getLogger(StorageServiceImpl.class.getName()); public static final String TOPOLOGY = "topology"; public static final String CUSTOM_ENDPOINT = "customEndpoint"; - public static final Set defaultCaches = - Collections.unmodifiableSet(Stream.of(TOPOLOGY, CUSTOM_ENDPOINT).collect(Collectors.toSet())); - private static StorageServiceImpl instance; - protected static ConcurrentHashMap> caches = - new ConcurrentHashMap<>(); + protected static Map> caches = new ConcurrentHashMap<>(); - private StorageServiceImpl() { + public StorageServiceImpl() { } - public static StorageService getInstance() { - if (instance == null) { - instance = new StorageServiceImpl(); - } - - return instance; - } - @Override - public void registerItemCategoryIfAbsent( + public void registerItemCategoryIfAbsent( String itemCategory, - Class itemClass, - long cleanupIntervalNs, - long expirationTimeNs, - @Nullable ShouldDisposeFunc shouldDisposeFunc, - @Nullable ItemDisposalFunc itemDisposalFunc) { - caches.computeIfAbsent(cacheName, name -> cacheSupplier.get()); + Class itemClass, + boolean isRenewableExpiration, + long timeToLiveNanos, + long cleanupIntervalNanos, + @Nullable ShouldDisposeFunc shouldDisposeFunc, + @Nullable ItemDisposalFunc itemDisposalFunc) { + caches.computeIfAbsent( + itemCategory, + category -> new ExpirationCache<>( + itemClass, + isRenewableExpiration, + timeToLiveNanos, + cleanupIntervalNanos, + shouldDisposeFunc, + itemDisposalFunc)); } @Override - public void deleteCache(String cacheName) { - final SlidingExpirationCacheWithCleanupThread cache = caches.remove(cacheName); - if (cache != null) { - cache.clear(); + public void set(String itemCategory, Object key, Object value) { + ExpirationCache cache = caches.get(itemCategory); + if (cache == null) { + // TODO: what should we do if the item category isn't registered? + return; } + + cache.put(key, value); } @Override - public @Nullable T get(String cacheName, Object key, Class dataClass, long timeToLiveNs) { - final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); + @SuppressWarnings("unchecked") + public @Nullable T get(String itemCategory, Object key) { + final ExpirationCache cache = caches.get(itemCategory); if (cache == null) { return null; } - final Object value = cache.get(key, timeToLiveNs); - if (dataClass.isInstance(value)) { - return dataClass.cast(value); + final Object value = cache.get(key); + if (value == null) { + return null; } - LOGGER.fine( - Messages.get("CacheServiceImpl.classMismatch", new Object[]{dataClass, value.getClass(), cacheName, key})); - return null; - } - - @Override - public void set(String cacheName, Object key, T value, long timeToLiveNs) { - SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); - if (cache == null) { - addCacheIfAbsent(cacheName, SlidingExpirationCacheWithCleanupThread::new); - LOGGER.finest(Messages.get("CacheServiceImpl.autoCreatedCache", new Object[]{cacheName})); + Class valueClass = cache.getValueClass(); + if (valueClass.isInstance(value)) { + return (T) value; } - cache = caches.get(cacheName); - if (cache != null) { - cache.put(key, value, timeToLiveNs); - } + LOGGER.fine( + Messages.get( + "StorageServiceImpl.classMismatch", + new Object[]{key, itemCategory, valueClass, value.getClass()})); + return null; } @Override - public boolean setIfCacheExists(String cacheName, Object key, T value, long timeToLiveNs) { - final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); + public boolean exists(String itemCategory, Object key) { + final ExpirationCache cache = caches.get(itemCategory); if (cache == null) { return false; } - cache.put(key, value, timeToLiveNs); - return true; + return cache.exists(key); } @Override - public void delete(String cacheName, Object key) { - final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); - if (cache == null) { - return; - } - - cache.remove(key); - } - - // TODO: this is needed to suppress the warning about the casted return value, is it fine to suppress? @SuppressWarnings("unchecked") - @Override - public @Nullable Map getEntries(String cacheName) { - final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); + public @Nullable T remove(String itemCategory, Object key) { + final ExpirationCache cache = caches.get(itemCategory); if (cache == null) { return null; } - return (Map) cache.getEntries(); - } + final Object value = cache.remove(key); + if (value == null) { + return null; + } - @Override - public int size(String cacheName) { - final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); - if (cache == null) { - return 0; + Class valueClass = cache.getValueClass(); + if (valueClass.isInstance(value)) { + return (T) value; } - return cache.size(); + LOGGER.fine( + Messages.get( + "StorageServiceImpl.classMismatch", + new Object[]{key, itemCategory, valueClass, value.getClass()})); + return null; } @Override - public void deleteAll() { - for (SlidingExpirationCacheWithCleanupThread cache : caches.values()) { - cache.clear(); + public void clear(String itemCategory) { + final ExpirationCache cache = caches.get(itemCategory); + if (cache != null) { + cache.clear();; } - - caches.clear(); } @Override public void clearAll() { - for (SlidingExpirationCacheWithCleanupThread cache : caches.values()) { + for (ExpirationCache cache : caches.values()) { cache.clear(); } } - - @Override - public void clear(String cacheName) { - final SlidingExpirationCacheWithCleanupThread cache = caches.get(cacheName); - if (cache == null) { - return; - } - - cache.clear(); - } } diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 538995b68..439fe74ed 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -345,10 +345,15 @@ ReadWriteSplittingPlugin.failedToConnectToReader=Failed to connect to reader hos ReadWriteSplittingPlugin.unsupportedHostSpecSelectorStrategy=Unsupported host selection strategy ''{0}'' specified in plugin configuration parameter ''readerHostSelectorStrategy''. Please visit the Read/Write Splitting Plugin documentation for all supported strategies. ReadWriteSplittingPlugin.errorVerifyingInitialHostSpecRole=An error occurred while obtaining the connected host's role. This could occur if the connection is broken or if you are not connected to an Aurora database. +ExpirationCache.cleaningUpCache=Cleaning up cache... +ExpirationCache.cleanupThreadInterrupted=The cleanup thread was interrupted. + SAMLCredentialsProviderFactory.getSamlAssertionFailed=Failed to get SAML Assertion due to exception: ''{0}'' SamlAuthPlugin.javaStsSdkNotInClasspath=Required dependency 'AWS Java SDK for AWS Secret Token Service' is not on the classpath. SamlAuthPlugin.unhandledException=Unhandled exception: ''{0}'' +StorageServiceImpl.classMismatch=The item stored at {0} did not have the expected type. Items stored under the {1} category should have a type of {2}, but the stored item had a type of {3}. Returning null. + # Wrapper Utils WrapperUtils.noWrapperClassExists=No wrapper class exists for ''{0}''. WrapperUtils.failedToInitializeClass=Can''t initialize class ''{0}''. From 8bfbd4ff3e63f982f16b2f8fa9a6f17014f1890a Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 11 Feb 2025 18:26:58 -0800 Subject: [PATCH 016/149] Replaced topologyCache with StorageService --- .../hostlistprovider/RdsHostListProvider.java | 37 ++- .../ClusterTopologyMonitorImpl.java | 26 +- .../MonitoringRdsHostListProvider.java | 11 +- .../MonitoringRdsMultiAzHostListProvider.java | 4 +- .../MultiAzClusterTopologyMonitorImpl.java | 11 +- .../jdbc/util/monitoring/MonitorGroup.java | 17 +- .../util/monitoring/MonitorServiceImpl.java | 260 +++++++++--------- .../jdbc/util/storage/ItemCategory.java | 27 ++ .../jdbc/util/storage/StorageService.java | 19 +- .../jdbc/util/storage/StorageServiceImpl.java | 35 ++- .../RdsHostListProviderTest.java | 36 +-- .../RdsMultiAzDbClusterListProviderTest.java | 31 ++- 12 files changed, 288 insertions(+), 226 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemCategory.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index eb677a1c2..208823af5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -29,6 +29,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Properties; @@ -56,6 +57,9 @@ import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SynchronousExecutor; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.storage.ItemCategory; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.storage.StorageServiceImpl; public class RdsHostListProvider implements DynamicHostListProvider { @@ -89,7 +93,7 @@ public class RdsHostListProvider implements DynamicHostListProvider { protected static final ConnectionUrlParser connectionUrlParser = new ConnectionUrlParser(); protected static final int defaultTopologyQueryTimeoutMs = 5000; protected static final long suggestedClusterIdRefreshRateNano = TimeUnit.MINUTES.toNanos(10); - protected static final CacheMap> topologyCache = new CacheMap<>(); + protected static final StorageService storageService = new StorageServiceImpl(); protected static final CacheMap suggestedPrimaryClusterIdCache = new CacheMap<>(); protected static final CacheMap primaryClusterIdCache = new CacheMap<>(); @@ -241,7 +245,7 @@ public FetchTopologyResult getTopology(final Connection conn, final boolean forc this.clusterIdChanged(oldClusterId); } - final List cachedHosts = topologyCache.get(this.clusterId); + final List cachedHosts = storageService.get(ItemCategory.TOPOLOGY, this.clusterId); // This clusterId is a primary one and is about to create a new entry in the cache. // When a primary entry is created it needs to be suggested for other (non-primary) entries. @@ -262,7 +266,7 @@ public FetchTopologyResult getTopology(final Connection conn, final boolean forc final List hosts = queryForTopology(conn); if (!Utils.isNullOrEmpty(hosts)) { - topologyCache.put(this.clusterId, hosts, this.refreshRateNano); + storageService.set(ItemCategory.TOPOLOGY, this.clusterId, hosts); if (needToSuggest) { this.suggestPrimaryCluster(hosts); } @@ -283,7 +287,12 @@ protected void clusterIdChanged(final String oldClusterId) { } protected ClusterSuggestedResult getSuggestedClusterId(final String url) { - for (final Entry> entry : topologyCache.getEntries().entrySet()) { + Map> entries = storageService.getEntries(ItemCategory.TOPOLOGY); + if (entries == null) { + return null; + } + + for (final Entry> entry : entries.entrySet()) { final String key = entry.getKey(); // clusterId final List hosts = entry.getValue(); final boolean isPrimaryCluster = primaryClusterIdCache.get(key, false, @@ -315,7 +324,12 @@ protected void suggestPrimaryCluster(final @NonNull List primaryCluste primaryClusterHostUrls.add(hostSpec.getUrl()); } - for (final Entry> entry : topologyCache.getEntries().entrySet()) { + Map> entries = storageService.getEntries(ItemCategory.TOPOLOGY); + if (entries == null) { + return; + } + + for (final Entry> entry : entries.entrySet()) { final String clusterId = entry.getKey(); final List clusterHosts = entry.getValue(); final boolean isPrimaryCluster = primaryClusterIdCache.get(clusterId, false, @@ -493,14 +507,14 @@ protected String getHostEndpoint(final String nodeName) { * cached topology is outdated, it returns null. */ public @Nullable List getCachedTopology() { - return topologyCache.get(this.clusterId); + return storageService.get(ItemCategory.TOPOLOGY, this.clusterId); } /** * Clear topology cache for all clusters. */ public static void clearAll() { - topologyCache.clear(); + storageService.clear(ItemCategory.TOPOLOGY); primaryClusterIdCache.clear(); suggestedPrimaryClusterIdCache.clear(); } @@ -509,7 +523,7 @@ public static void clearAll() { * Clear topology cache for the current cluster. */ public void clear() { - topologyCache.remove(this.clusterId); + storageService.remove(ItemCategory.TOPOLOGY, this.clusterId); } @Override @@ -586,7 +600,12 @@ private void validateHostPatternSetting(final String hostPattern) { public static void logCache() { LOGGER.finest(() -> { final StringBuilder sb = new StringBuilder(); - final Set>> cacheEntries = topologyCache.getEntries().entrySet(); + Map> entries = storageService.getEntries(ItemCategory.TOPOLOGY); + if (entries == null) { + return ""; + } + + final Set>> cacheEntries = entries.entrySet(); if (cacheEntries.isEmpty()) { sb.append("Cache is empty."); diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 80a586249..f1c1f0cf5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -48,13 +48,14 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.hostavailability.HostAvailability; -import software.amazon.jdbc.util.CacheMap; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SynchronousExecutor; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.storage.ItemCategory; +import software.amazon.jdbc.util.storage.StorageService; public class ClusterTopologyMonitorImpl implements ClusterTopologyMonitor { @@ -77,12 +78,11 @@ public class ClusterTopologyMonitorImpl implements ClusterTopologyMonitor { protected final long refreshRateNano; protected final long highRefreshRateNano; - protected final long topologyCacheExpirationNano; protected final Properties properties; protected final Properties monitoringProperties; protected final PluginService pluginService; protected final HostSpec initialHostSpec; - protected final CacheMap> topologyMap; + protected final StorageService storageService; protected final String topologyQuery; protected final String nodeIdQuery; protected final String writerTopologyQuery; @@ -117,7 +117,7 @@ public class ClusterTopologyMonitorImpl implements ClusterTopologyMonitor { public ClusterTopologyMonitorImpl( final String clusterId, - final CacheMap> topologyMap, + final StorageService storageService, final HostSpec initialHostSpec, final Properties properties, final PluginService pluginService, @@ -125,13 +125,12 @@ public ClusterTopologyMonitorImpl( final HostSpec clusterInstanceTemplate, final long refreshRateNano, final long highRefreshRateNano, - final long topologyCacheExpirationNano, final String topologyQuery, final String writerTopologyQuery, final String nodeIdQuery) { this.clusterId = clusterId; - this.topologyMap = topologyMap; + this.storageService = storageService; this.initialHostSpec = initialHostSpec; this.pluginService = pluginService; this.hostListProviderService = hostListProviderService; @@ -139,7 +138,6 @@ public ClusterTopologyMonitorImpl( this.properties = properties; this.refreshRateNano = refreshRateNano; this.highRefreshRateNano = highRefreshRateNano; - this.topologyCacheExpirationNano = topologyCacheExpirationNano; this.topologyQuery = topologyQuery; this.writerTopologyQuery = writerTopologyQuery; this.nodeIdQuery = nodeIdQuery; @@ -187,7 +185,7 @@ public List forceRefresh(final boolean shouldVerifyWriter, final long && System.nanoTime() < this.ignoreNewTopologyRequestsEndTimeNano.get()) { // Previous failover has just completed. We can use results of it without triggering a new topology update. - List currentHosts = this.topologyMap.get(this.clusterId); + List currentHosts = storageService.get(ItemCategory.TOPOLOGY, this.clusterId); LOGGER.finest( Utils.logTopology(currentHosts, Messages.get("ClusterTopologyMonitorImpl.ignoringTopologyRequest"))); if (currentHosts != null) { @@ -220,7 +218,7 @@ public List forceRefresh(@Nullable Connection connection, final long t protected List waitTillTopologyGetsUpdated(final long timeoutMs) throws TimeoutException { - List currentHosts = this.topologyMap.get(this.clusterId); + List currentHosts = storageService.get(ItemCategory.TOPOLOGY, this.clusterId); List latestHosts; synchronized (this.requestToUpdateTopology) { @@ -240,7 +238,7 @@ protected List waitTillTopologyGetsUpdated(final long timeoutMs) throw // Note that we are checking reference equality instead of value equality here. We will break out of the loop if // there is a new entry in the topology map, even if the value of the hosts in latestHosts is the same as // currentHosts. - while (currentHosts == (latestHosts = this.topologyMap.get(this.clusterId)) + while (currentHosts == (latestHosts = storageService.get(ItemCategory.TOPOLOGY, this.clusterId)) && System.nanoTime() < end) { try { synchronized (this.topologyUpdated) { @@ -302,7 +300,7 @@ public void run() { this.nodeThreadsWriterHostSpec.set(null); this.nodeThreadsLatestTopology.set(null); - List hosts = this.topologyMap.get(this.clusterId); + List hosts = storageService.get(ItemCategory.TOPOLOGY, this.clusterId); if (hosts == null) { // need any connection to get topology hosts = this.openAnyConnectionAndUpdateTopology(); @@ -398,7 +396,7 @@ public void run() { // Do not log topology while in high refresh rate. It's noisy! if (this.highRefreshRateEndTimeNano == 0) { - LOGGER.finest(Utils.logTopology(this.topologyMap.get(this.clusterId))); + LOGGER.finest(Utils.logTopology(storageService.get(ItemCategory.TOPOLOGY, this.clusterId))); } this.delay(false); @@ -594,7 +592,7 @@ protected void delay(boolean useHighRefreshRate) throws InterruptedException { protected void updateTopologyCache(final @NonNull List hosts) { synchronized (this.requestToUpdateTopology) { - this.topologyMap.put(this.clusterId, hosts, this.topologyCacheExpirationNano); + storageService.set(ItemCategory.TOPOLOGY, this.clusterId, hosts); synchronized (this.topologyUpdated) { this.requestToUpdateTopology.set(false); @@ -834,7 +832,7 @@ public void run() { this.monitor.nodeThreadsWriterHostSpec.set(hostSpec); this.monitor.nodeThreadsStop.set(true); LOGGER.fine(Utils.logTopology( - this.monitor.topologyMap.get(this.monitor.clusterId))); + this.monitor.storageService.get(ItemCategory.TOPOLOGY, this.monitor.clusterId))); } // Setting the connection to null here prevents the finally block diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index 398f9b605..5e418ba5f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -32,6 +32,7 @@ import software.amazon.jdbc.cleanup.CanReleaseResources; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider; import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; +import software.amazon.jdbc.util.storage.ItemCategory; public class MonitoringRdsHostListProvider extends RdsHostListProvider implements BlockingHostListProvider, CanReleaseResources { @@ -47,8 +48,6 @@ public class MonitoringRdsHostListProvider extends RdsHostListProvider protected static final long CACHE_CLEANUP_NANO = TimeUnit.MINUTES.toNanos(1); protected static final long MONITOR_EXPIRATION_NANO = TimeUnit.MINUTES.toNanos(15); - protected static final long TOPOLOGY_CACHE_EXPIRATION_NANO = TimeUnit.MINUTES.toNanos(5); - protected static final SlidingExpirationCacheWithCleanupThread monitors = new SlidingExpirationCacheWithCleanupThread<>( ClusterTopologyMonitor::canDispose, @@ -109,9 +108,9 @@ protected void init() throws SQLException { protected ClusterTopologyMonitor initMonitor() { return monitors.computeIfAbsent(this.clusterId, (key) -> new ClusterTopologyMonitorImpl( - key, topologyCache, this.initialHostSpec, this.properties, this.pluginService, + key, storageService, this.initialHostSpec, this.properties, this.pluginService, this.hostListProviderService, this.clusterInstanceTemplate, - this.refreshRateNano, this.highRefreshRateNano, TOPOLOGY_CACHE_EXPIRATION_NANO, + this.refreshRateNano, this.highRefreshRateNano, this.topologyQuery, this.writerTopologyQuery, this.nodeIdQuery), @@ -141,9 +140,9 @@ protected void clusterIdChanged(final String oldClusterId) { monitors.remove(oldClusterId); } - final List existingHosts = topologyCache.get(oldClusterId); + final List existingHosts = storageService.get(ItemCategory.TOPOLOGY, oldClusterId); if (existingHosts != null) { - topologyCache.put(this.clusterId, existingHosts, TOPOLOGY_CACHE_EXPIRATION_NANO); + storageService.set(ItemCategory.TOPOLOGY, this.clusterId, existingHosts); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java index f08448a80..a70b23334 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java @@ -48,9 +48,9 @@ public MonitoringRdsMultiAzHostListProvider( protected ClusterTopologyMonitor initMonitor() { return monitors.computeIfAbsent(this.clusterId, (key) -> new MultiAzClusterTopologyMonitorImpl( - key, topologyCache, this.initialHostSpec, this.properties, this.pluginService, + key, storageService, this.initialHostSpec, this.properties, this.pluginService, this.hostListProviderService, this.clusterInstanceTemplate, - this.refreshRateNano, this.highRefreshRateNano, TOPOLOGY_CACHE_EXPIRATION_NANO, + this.refreshRateNano, this.highRefreshRateNano, this.topologyQuery, this.writerTopologyQuery, this.nodeIdQuery, diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java index c313481e3..bb2924813 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java @@ -22,14 +22,13 @@ import java.sql.Statement; import java.sql.Timestamp; import java.time.Instant; -import java.util.List; import java.util.Properties; import java.util.logging.Logger; import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.util.CacheMap; import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.storage.StorageService; public class MultiAzClusterTopologyMonitorImpl extends ClusterTopologyMonitorImpl { @@ -40,7 +39,7 @@ public class MultiAzClusterTopologyMonitorImpl extends ClusterTopologyMonitorImp public MultiAzClusterTopologyMonitorImpl( final String clusterId, - final CacheMap> topologyMap, + final StorageService storageService, final HostSpec initialHostSpec, final Properties properties, final PluginService pluginService, @@ -48,15 +47,13 @@ public MultiAzClusterTopologyMonitorImpl( final HostSpec clusterInstanceTemplate, final long refreshRateNano, final long highRefreshRateNano, - final long topologyCacheExpirationNano, final String topologyQuery, final String writerTopologyQuery, final String nodeIdQuery, final String fetchWriterNodeQuery, final String fetchWriterNodeColumnName) { - super(clusterId, topologyMap, initialHostSpec, properties, pluginService, hostListProviderService, - clusterInstanceTemplate, refreshRateNano, highRefreshRateNano, topologyCacheExpirationNano, - topologyQuery, writerTopologyQuery, nodeIdQuery); + super(clusterId, storageService, initialHostSpec, properties, pluginService, hostListProviderService, + clusterInstanceTemplate, refreshRateNano, highRefreshRateNano, topologyQuery, writerTopologyQuery, nodeIdQuery); this.fetchWriterNodeQuery = fetchWriterNodeQuery; this.fetchWriterNodeColumnName = fetchWriterNodeColumnName; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorGroup.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorGroup.java index cf1d2c1bb..a2fd7cc93 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorGroup.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorGroup.java @@ -17,19 +17,20 @@ package software.amazon.jdbc.util.monitoring; import java.util.Set; +import java.util.function.Supplier; import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; public class MonitorGroup { private final String groupName; - private final MonitorInitializer initializer; - private final Set exceptionResponses; + private final Supplier supplier; + private final Set exceptionResponses; private final SlidingExpirationCacheWithCleanupThread cache; - public MonitorGroup(String groupName, MonitorInitializer initializer, - Set exceptionResponses, + public MonitorGroup(String groupName, Supplier supplier, + Set exceptionResponses, SlidingExpirationCacheWithCleanupThread cache) { this.groupName = groupName; - this.initializer = initializer; + this.supplier = supplier; this.exceptionResponses = exceptionResponses; this.cache = cache; } @@ -38,11 +39,11 @@ public String getGroupName() { return groupName; } - public MonitorInitializer getInitializer() { - return initializer; + public Supplier getSupplier() { + return supplier; } - public Set getExceptionResponses() { + public Set getExceptionResponses() { return exceptionResponses; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index bad727412..e80d80fd6 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -16,137 +16,129 @@ package software.amazon.jdbc.util.monitoring; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.function.Supplier; -import java.util.logging.Logger; -import org.checkerframework.checker.nullness.qual.Nullable; -import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; - -public class MonitorServiceImpl implements MonitorService { - private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); - private final Map monitorGroups = new ConcurrentHashMap<>(); - private static MonitorServiceImpl instance; - - private MonitorServiceImpl() { - - } - - public static MonitorService getInstance() { - if (instance == null) { - instance = new MonitorServiceImpl(); - } - - return instance; - } - - - @Override - public void registerMonitorTypeIfAbsent(String monitorType, - Supplier> cacheSupplier, - MonitorInitializer initializer, - Set exceptionResponses) { - monitorGroups.computeIfAbsent(monitorType, - key -> new MonitorGroup(monitorType, initializer, exceptionResponses, cacheSupplier.get())); - } - - @Override - public void runIfAbsent( - String monitorType, - Object monitorKey, - long timeToLiveNs, - Object... initializerParams) { - MonitorGroup group = monitorGroups.get(monitorType); - if (group == null) { - throw new RuntimeException( - "A monitor with key '" + monitorKey + "' was requested, but the monitor type '" - + monitorType + "' does not exist."); - } - - SlidingExpirationCacheWithCleanupThread cache = group.getCache(); - cache.computeIfAbsent( - monitorKey, - key -> { - Monitor monitor = group.getInitializer().initialize(initializerParams); - monitor.start(); - return monitor; - }, - timeToLiveNs); - } - - @Override - public int numMonitors(String monitorType) { - MonitorGroup group = monitorGroups.get(monitorType); - if (group == null) { - return 0; - } - - return group.getCache().size(); - } - - @Override - public @Nullable MonitorStatus getStatus(String monitorType, Object monitorKey, long timeToLiveNs) { - MonitorGroup group = monitorGroups.get(monitorType); - if (group == null) { - return null; - } - - Monitor monitor = group.getCache().get(monitorKey, timeToLiveNs); - if (monitor == null) { - return null; - } - - return monitor.getStatus(); - } - - @Override - public void stopAndRemoveMonitor(String monitorType, Object monitorKey) { - MonitorGroup group = monitorGroups.get(monitorType); - if (group == null) { - return; - } - - Monitor monitor = group.getCache().get(monitorKey, 0); - if (monitor != null) { - group.getCache().remove(monitorKey); - monitor.stop(); - } - } - - @Override - public void stopAndRemoveMonitors(String monitorType) { - MonitorGroup group = monitorGroups.get(monitorType); - if (group == null) { - return; - } - - for (Object monitorKey : group.getCache().getEntries().keySet()) { - stopAndRemoveMonitor(monitorType, monitorKey); - } - } - - @Override - public void deleteMonitorType(String monitorType) { - MonitorGroup group = monitorGroups.remove(monitorType); - if (group != null) { - return; - } - - stopAndRemoveMonitors(monitorType); - } - - @Override - public void stopAndRemoveAll() { - for (String group : monitorGroups.keySet()) { - stopAndRemoveMonitors(group); - } - } - - @Override - public void deleteAll() { - for (String group : monitorGroups.keySet()) { - deleteMonitorType(group); - } - } -} +// public class MonitorServiceImpl implements MonitorService { +// private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); +// private final Map monitorGroups = new ConcurrentHashMap<>(); +// private static MonitorServiceImpl instance; +// +// private MonitorServiceImpl() { +// +// } +// +// public static MonitorService getInstance() { +// if (instance == null) { +// instance = new MonitorServiceImpl(); +// } +// +// return instance; +// } +// +// +// @Override +// public void registerMonitorTypeIfAbsent(String monitorType, +// Supplier> cacheSupplier, +// MonitorInitializer initializer, +// Set exceptionResponses) { +// monitorGroups.computeIfAbsent(monitorType, +// key -> new MonitorGroup(monitorType, initializer, exceptionResponses, cacheSupplier.get())); +// } +// +// @Override +// public void runIfAbsent( +// String monitorType, +// Object monitorKey, +// long timeToLiveNs, +// Object... initializerParams) { +// MonitorGroup group = monitorGroups.get(monitorType); +// if (group == null) { +// throw new RuntimeException( +// "A monitor with key '" + monitorKey + "' was requested, but the monitor type '" +// + monitorType + "' does not exist."); +// } +// +// SlidingExpirationCacheWithCleanupThread cache = group.getCache(); +// cache.computeIfAbsent( +// monitorKey, +// key -> { +// Monitor monitor = group.getInitializer().initialize(initializerParams); +// monitor.start(); +// return monitor; +// }, +// timeToLiveNs); +// } +// +// @Override +// public int numMonitors(String monitorType) { +// MonitorGroup group = monitorGroups.get(monitorType); +// if (group == null) { +// return 0; +// } +// +// return group.getCache().size(); +// } +// +// @Override +// public @Nullable MonitorStatus getStatus(String monitorType, Object monitorKey, long timeToLiveNs) { +// MonitorGroup group = monitorGroups.get(monitorType); +// if (group == null) { +// return null; +// } +// +// Monitor monitor = group.getCache().get(monitorKey, timeToLiveNs); +// if (monitor == null) { +// return null; +// } +// +// return monitor.getStatus(); +// } +// +// @Override +// public void stopAndRemoveMonitor(String monitorType, Object monitorKey) { +// MonitorGroup group = monitorGroups.get(monitorType); +// if (group == null) { +// return; +// } +// +// Monitor monitor = group.getCache().get(monitorKey, 0); +// if (monitor != null) { +// group.getCache().remove(monitorKey); +// monitor.stop(); +// } +// } +// +// @Override +// public void stopAndRemoveMonitors(String monitorType) { +// MonitorGroup group = monitorGroups.get(monitorType); +// if (group == null) { +// return; +// } +// +// for (Object monitorKey : group.getCache().getEntries().keySet()) { +// stopAndRemoveMonitor(monitorType, monitorKey); +// } +// } +// +// @Override +// public void deleteMonitorType(String monitorType) { +// MonitorGroup group = monitorGroups.remove(monitorType); +// if (group != null) { +// return; +// } +// +// stopAndRemoveMonitors(monitorType); +// } +// +// @Override +// public void stopAndRemoveAll() { +// for (String group : monitorGroups.keySet()) { +// stopAndRemoveMonitors(group); +// } +// } +// +// @Override +// public void deleteAll() { +// for (String group : monitorGroups.keySet()) { +// deleteMonitorType(group); +// } +// } +// } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemCategory.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemCategory.java new file mode 100644 index 000000000..e92b871e2 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemCategory.java @@ -0,0 +1,27 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.storage; + +public class ItemCategory { + public static final String TOPOLOGY = "topology"; + public static final String CUSTOM_ENDPOINT = "customEndpoint"; + + private ItemCategory() { + throw new UnsupportedOperationException( + "ItemCategories should not be instantiated because its purpose is to provide a set of static constants."); + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java index 067922eaf..bcb3b1098 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java @@ -16,6 +16,7 @@ package software.amazon.jdbc.util.storage; +import java.util.Map; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.util.ItemDisposalFunc; import software.amazon.jdbc.util.ShouldDisposeFunc; @@ -55,19 +56,19 @@ void registerItemCategoryIfAbsent( * @param itemCategory a String representing the item category, eg "customEndpoint". * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". * @param item the item to store. - * @param the type of the item being stored. + * @param the type of the item being stored. */ - void set(String itemCategory, Object key, Object item); + void set(String itemCategory, Object key, V item); /** * Gets an item stored under the given category. * * @param itemCategory a String representing the item category, eg "customEndpoint". * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". - * @param the type of the item being retrieved. + * @param the type of the item being retrieved. * @return the item stored at the given key under the given category. */ - @Nullable T get(String itemCategory, Object key); + @Nullable V get(String itemCategory, Object key); /** * Indicates whether an item exists under the given item category and key. @@ -83,10 +84,10 @@ void registerItemCategoryIfAbsent( * * @param itemCategory a String representing the item category, eg "customEndpoint". * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". - * @param the type of the item being removed. + * @param the type of the item being removed. * @return the item removed from the storage service. */ - @Nullable T remove(String itemCategory, Object key); + @Nullable V remove(String itemCategory, Object key); /** * Clears all items from the given category. For example, storageService.clear("customEndpoint") will remove all @@ -100,4 +101,10 @@ void registerItemCategoryIfAbsent( * Clears all information from all categories of the storage service. */ void clearAll(); + + // TODO: this is only called by the suggestedClusterId logic in RdsHostListProvider, which will be removed. This + // method should possibly be removed at that point as well. + @Nullable Map getEntries(String itemCategory); + + int size(String itemCategory); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 992fed081..1c075be72 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -26,8 +26,6 @@ public class StorageServiceImpl implements StorageService { private static final Logger LOGGER = Logger.getLogger(StorageServiceImpl.class.getName()); - public static final String TOPOLOGY = "topology"; - public static final String CUSTOM_ENDPOINT = "customEndpoint"; protected static Map> caches = new ConcurrentHashMap<>(); public StorageServiceImpl() { @@ -55,7 +53,7 @@ public void registerItemCategoryIfAbsent( } @Override - public void set(String itemCategory, Object key, Object value) { + public void set(String itemCategory, Object key, V value) { ExpirationCache cache = caches.get(itemCategory); if (cache == null) { // TODO: what should we do if the item category isn't registered? @@ -67,7 +65,7 @@ public void set(String itemCategory, Object key, Object value) { @Override @SuppressWarnings("unchecked") - public @Nullable T get(String itemCategory, Object key) { + public @Nullable V get(String itemCategory, Object key) { final ExpirationCache cache = caches.get(itemCategory); if (cache == null) { return null; @@ -80,7 +78,7 @@ public void set(String itemCategory, Object key, Object value) { Class valueClass = cache.getValueClass(); if (valueClass.isInstance(value)) { - return (T) value; + return (V) value; } LOGGER.fine( @@ -102,7 +100,7 @@ public boolean exists(String itemCategory, Object key) { @Override @SuppressWarnings("unchecked") - public @Nullable T remove(String itemCategory, Object key) { + public @Nullable V remove(String itemCategory, Object key) { final ExpirationCache cache = caches.get(itemCategory); if (cache == null) { return null; @@ -115,7 +113,7 @@ public boolean exists(String itemCategory, Object key) { Class valueClass = cache.getValueClass(); if (valueClass.isInstance(value)) { - return (T) value; + return (V) value; } LOGGER.fine( @@ -129,7 +127,7 @@ public boolean exists(String itemCategory, Object key) { public void clear(String itemCategory) { final ExpirationCache cache = caches.get(itemCategory); if (cache != null) { - cache.clear();; + cache.clear(); } } @@ -139,4 +137,25 @@ public void clearAll() { cache.clear(); } } + + @Override + public @Nullable Map getEntries(String itemCategory) { + final ExpirationCache cache = caches.get(itemCategory); + if (cache == null) { + return null; + } + + // TODO: check if this can be safely casted + return (Map) cache.getEntries(); + } + + @Override + public int size(String itemCategory) { + final ExpirationCache cache = caches.get(itemCategory); + if (cache == null) { + return 0; + } + + return cache.size(); + } } diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java index 745364ef5..4c1b5b260 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java @@ -65,10 +65,12 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider.FetchTopologyResult; +import software.amazon.jdbc.util.storage.ItemCategory; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.storage.StorageServiceImpl; class RdsHostListProviderTest { - - private final long defaultRefreshRateNano = TimeUnit.SECONDS.toNanos(5); + private final StorageService storageService = new StorageServiceImpl(); private RdsHostListProvider rdsHostListProvider; @Mock private Connection mockConnection; @@ -123,9 +125,8 @@ void testGetTopology_returnCachedTopology() throws SQLException { rdsHostListProvider = Mockito.spy( getRdsHostListProvider(mockHostListProviderService, "protocol://url/")); - final Instant lastUpdated = Instant.now(); final List expected = hosts; - RdsHostListProvider.topologyCache.put(rdsHostListProvider.clusterId, expected, defaultRefreshRateNano); + storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, expected); final FetchTopologyResult result = rdsHostListProvider.getTopology(mockConnection, false); assertEquals(expected, result.hosts); @@ -139,7 +140,7 @@ void testGetTopology_withForceUpdate_returnsUpdatedTopology() throws SQLExceptio getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url")); rdsHostListProvider.isInitialized = true; - RdsHostListProvider.topologyCache.put(rdsHostListProvider.clusterId, hosts, defaultRefreshRateNano); + storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, hosts); final List newHosts = Collections.singletonList( new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("newHost").build()); @@ -159,7 +160,7 @@ void testGetTopology_noForceUpdate_queryReturnsEmptyHostList() throws SQLExcepti rdsHostListProvider.isInitialized = true; final List expected = hosts; - RdsHostListProvider.topologyCache.put(rdsHostListProvider.clusterId, expected, defaultRefreshRateNano); + storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, expected); doReturn(new ArrayList<>()).when(rdsHostListProvider).queryForTopology(mockConnection); @@ -181,7 +182,7 @@ void testGetTopology_withForceUpdate_returnsInitialHostList() throws SQLExceptio verify(rdsHostListProvider, atMostOnce()).queryForTopology(mockConnection); assertNotNull(result.hosts); assertEquals( - Arrays.asList(new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("url").build()), + Collections.singletonList(new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("url").build()), result.hosts); } @@ -229,7 +230,7 @@ void testGetCachedTopology_returnCachedTopology() throws SQLException { rdsHostListProvider = getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url"); final List expected = hosts; - RdsHostListProvider.topologyCache.put(rdsHostListProvider.clusterId, expected, defaultRefreshRateNano); + storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, expected); final List result = rdsHostListProvider.getCachedTopology(); assertEquals(expected, result); @@ -243,8 +244,7 @@ void testGetCachedTopology_returnNull() throws InterruptedException, SQLExceptio rdsHostListProvider.clear(); rdsHostListProvider = getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url"); - final long refreshRateOneNanosecond = 1; - RdsHostListProvider.topologyCache.put(rdsHostListProvider.clusterId, hosts, refreshRateOneNanosecond); + storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, hosts); TimeUnit.NANOSECONDS.sleep(1); // Test getCachedTopology with expired cache. @@ -270,7 +270,7 @@ void testTopologyCache_NoSuggestedClusterId() throws SQLException { doReturn(topologyClusterA) .when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, RdsHostListProvider.topologyCache.size()); + assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); final List topologyProvider1 = provider1.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -293,7 +293,7 @@ void testTopologyCache_NoSuggestedClusterId() throws SQLException { final List topologyProvider2 = provider2.refresh(mock(Connection.class)); assertEquals(topologyClusterB, topologyProvider2); - assertEquals(2, RdsHostListProvider.topologyCache.size()); + assertEquals(2, storageService.size(ItemCategory.TOPOLOGY)); } @Test @@ -323,7 +323,7 @@ void testTopologyCache_SuggestedClusterIdForRds() throws SQLException { doReturn(topologyClusterA).when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, RdsHostListProvider.topologyCache.size()); + assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); final List topologyProvider1 = provider1.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -340,7 +340,7 @@ void testTopologyCache_SuggestedClusterIdForRds() throws SQLException { final List topologyProvider2 = provider2.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider2); - assertEquals(1, RdsHostListProvider.topologyCache.size()); + assertEquals(1, storageService.size(ItemCategory.TOPOLOGY)); } @Test @@ -370,7 +370,7 @@ void testTopologyCache_SuggestedClusterIdForInstance() throws SQLException { doReturn(topologyClusterA).when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, RdsHostListProvider.topologyCache.size()); + assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); final List topologyProvider1 = provider1.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -387,7 +387,7 @@ void testTopologyCache_SuggestedClusterIdForInstance() throws SQLException { final List topologyProvider2 = provider2.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider2); - assertEquals(1, RdsHostListProvider.topologyCache.size()); + assertEquals(1, storageService.size(ItemCategory.TOPOLOGY)); } @Test @@ -417,7 +417,7 @@ void testTopologyCache_AcceptSuggestion() throws SQLException { doAnswer(a -> topologyClusterA).when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, RdsHostListProvider.topologyCache.size()); + assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); List topologyProvider1 = provider1.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -437,7 +437,7 @@ void testTopologyCache_AcceptSuggestion() throws SQLException { assertNotEquals(provider1.clusterId, provider2.clusterId); assertFalse(provider1.isPrimaryClusterId); assertTrue(provider2.isPrimaryClusterId); - assertEquals(2, RdsHostListProvider.topologyCache.size()); + assertEquals(2, storageService.size(ItemCategory.TOPOLOGY)); assertEquals("cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com", RdsHostListProvider.suggestedPrimaryClusterIdCache.get(provider1.clusterId)); diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java index a99ead6c6..81e287565 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java @@ -46,7 +46,6 @@ import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; @@ -61,10 +60,14 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider.FetchTopologyResult; +import software.amazon.jdbc.util.storage.ItemCategory; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.storage.StorageServiceImpl; class RdsMultiAzDbClusterListProviderTest { private final long defaultRefreshRateNano = TimeUnit.SECONDS.toNanos(5); + private final StorageService storageService = new StorageServiceImpl(); private RdsMultiAzDbClusterListProvider rdsMazDbClusterHostListProvider; @Mock private Connection mockConnection; @@ -125,7 +128,7 @@ void testGetTopology_returnCachedTopology() throws SQLException { final Instant lastUpdated = Instant.now(); final List expected = hosts; - RdsMultiAzDbClusterListProvider.topologyCache.put( + storageService.set( rdsMazDbClusterHostListProvider.clusterId, expected, defaultRefreshRateNano); final FetchTopologyResult result = rdsMazDbClusterHostListProvider.getTopology(mockConnection, false); @@ -140,7 +143,7 @@ void testGetTopology_withForceUpdate_returnsUpdatedTopology() throws SQLExceptio getRdsMazDbClusterHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url")); rdsMazDbClusterHostListProvider.isInitialized = true; - RdsMultiAzDbClusterListProvider.topologyCache.put( + storageService.set( rdsMazDbClusterHostListProvider.clusterId, hosts, defaultRefreshRateNano); final List newHosts = Collections.singletonList( @@ -161,7 +164,7 @@ void testGetTopology_noForceUpdate_queryReturnsEmptyHostList() throws SQLExcepti rdsMazDbClusterHostListProvider.isInitialized = true; final List expected = hosts; - RdsMultiAzDbClusterListProvider.topologyCache.put( + storageService.set( rdsMazDbClusterHostListProvider.clusterId, expected, defaultRefreshRateNano); doReturn(new ArrayList<>()).when(rdsMazDbClusterHostListProvider).queryForTopology(mockConnection); @@ -205,7 +208,7 @@ void testGetCachedTopology_returnCachedTopology() throws SQLException { mockHostListProviderService, "jdbc:someprotocol://url"); final List expected = hosts; - RdsMultiAzDbClusterListProvider.topologyCache.put( + storageService.set( rdsMazDbClusterHostListProvider.clusterId, expected, defaultRefreshRateNano); final List result = rdsMazDbClusterHostListProvider.getCachedTopology(); @@ -223,7 +226,7 @@ void testGetCachedTopology_returnNull() throws InterruptedException, SQLExceptio rdsMazDbClusterHostListProvider = getRdsMazDbClusterHostListProvider( mockHostListProviderService, "jdbc:someprotocol://url"); final long refreshRateOneNanosecond = 1; - RdsMultiAzDbClusterListProvider.topologyCache.put( + storageService.set( rdsMazDbClusterHostListProvider.clusterId, hosts, refreshRateOneNanosecond); TimeUnit.NANOSECONDS.sleep(1); @@ -250,7 +253,7 @@ void testTopologyCache_NoSuggestedClusterId() throws SQLException { doReturn(topologyClusterA) .when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, RdsMultiAzDbClusterListProvider.topologyCache.size()); + assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); final List topologyProvider1 = provider1.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -273,7 +276,7 @@ void testTopologyCache_NoSuggestedClusterId() throws SQLException { final List topologyProvider2 = provider2.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterB, topologyProvider2); - assertEquals(2, RdsMultiAzDbClusterListProvider.topologyCache.size()); + assertEquals(2, storageService.size(ItemCategory.TOPOLOGY)); } @Test @@ -303,7 +306,7 @@ void testTopologyCache_SuggestedClusterIdForRds() throws SQLException { doReturn(topologyClusterA).when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, RdsMultiAzDbClusterListProvider.topologyCache.size()); + assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); final List topologyProvider1 = provider1.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -320,7 +323,7 @@ void testTopologyCache_SuggestedClusterIdForRds() throws SQLException { final List topologyProvider2 = provider2.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider2); - assertEquals(1, RdsMultiAzDbClusterListProvider.topologyCache.size()); + assertEquals(1, storageService.size(ItemCategory.TOPOLOGY)); } @Test @@ -350,7 +353,7 @@ void testTopologyCache_SuggestedClusterIdForInstance() throws SQLException { doReturn(topologyClusterA).when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, RdsMultiAzDbClusterListProvider.topologyCache.size()); + assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); final List topologyProvider1 = provider1.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -367,7 +370,7 @@ void testTopologyCache_SuggestedClusterIdForInstance() throws SQLException { final List topologyProvider2 = provider2.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider2); - assertEquals(1, RdsMultiAzDbClusterListProvider.topologyCache.size()); + assertEquals(1, storageService.size(ItemCategory.TOPOLOGY)); } @Test @@ -397,7 +400,7 @@ void testTopologyCache_AcceptSuggestion() throws SQLException { doAnswer(a -> topologyClusterA).when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, RdsMultiAzDbClusterListProvider.topologyCache.size()); + assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); List topologyProvider1 = provider1.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -417,7 +420,7 @@ void testTopologyCache_AcceptSuggestion() throws SQLException { assertNotEquals(provider1.clusterId, provider2.clusterId); assertFalse(provider1.isPrimaryClusterId); assertTrue(provider2.isPrimaryClusterId); - assertEquals(2, RdsMultiAzDbClusterListProvider.topologyCache.size()); + assertEquals(2, storageService.size(ItemCategory.TOPOLOGY)); assertEquals("cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com", RdsMultiAzDbClusterListProvider.suggestedPrimaryClusterIdCache.get(provider1.clusterId)); From f7ef88f5f7110d1696d2335e33d5c5b2668f4ba2 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 12 Feb 2025 13:46:22 -0800 Subject: [PATCH 017/149] Error when passing Topology.class as the itemClass --- .../java/software/amazon/jdbc/Driver.java | 8 +++++ .../amazon/jdbc/util/storage/Topology.java | 32 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/storage/Topology.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index b66e216b4..6f3acd6b9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.List; import java.util.Properties; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.logging.ConsoleHandler; @@ -64,6 +65,10 @@ import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.storage.ItemCategory; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.storage.StorageServiceImpl; +import software.amazon.jdbc.util.storage.Topology; import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -113,6 +118,9 @@ public static void register() throws SQLException { final Driver driver = new Driver(); DriverManager.registerDriver(driver); registeredDriver = driver; + + StorageService storageService = new StorageServiceImpl(); + storageService.registerItemCategoryIfAbsent(ItemCategory.TOPOLOGY, Topology.class, false, TimeUnit.MINUTES.toNanos(10), TimeUnit.MINUTES.toNanos(5), null, null); } public static void deregister() throws SQLException { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/Topology.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/Topology.java new file mode 100644 index 000000000..36ced73e4 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/Topology.java @@ -0,0 +1,32 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.storage; + +import java.util.List; +import software.amazon.jdbc.HostSpec; + +public class Topology { + private final List hosts; + + public Topology(List hosts) { + this.hosts = hosts; + } + + public List getHosts() { + return hosts; + } +} From 53a142950f58b5ae4c860a79d00c3631b248ce61 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 12 Feb 2025 17:40:07 -0800 Subject: [PATCH 018/149] Added itemClass parameter to get() method --- .../java/software/amazon/jdbc/Driver.java | 9 ++- .../jdbc/util/storage/StorageService.java | 14 ++-- .../jdbc/util/storage/StorageServiceImpl.java | 64 ++++++++----------- ..._advanced_jdbc_wrapper_messages.properties | 3 +- 4 files changed, 44 insertions(+), 46 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index 6f3acd6b9..d31f3e7f6 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -120,7 +120,14 @@ public static void register() throws SQLException { registeredDriver = driver; StorageService storageService = new StorageServiceImpl(); - storageService.registerItemCategoryIfAbsent(ItemCategory.TOPOLOGY, Topology.class, false, TimeUnit.MINUTES.toNanos(10), TimeUnit.MINUTES.toNanos(5), null, null); + storageService.registerItemCategoryIfAbsent( + ItemCategory.TOPOLOGY, + Topology.class, + false, + TimeUnit.MINUTES.toNanos(10), + TimeUnit.MINUTES.toNanos(5), + null, + null); } public static void deregister() throws SQLException { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java index bcb3b1098..723370c8a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java @@ -41,14 +41,14 @@ public interface StorageService { * passed, the * item will be removed without performing any additional operations. */ - void registerItemCategoryIfAbsent( + void registerItemCategoryIfAbsent( String itemCategory, - Class itemClass, + Class itemClass, boolean isRenewableExpiration, long cleanupIntervalNanos, long expirationTimeoutNanos, - @Nullable ShouldDisposeFunc shouldDisposeFunc, - @Nullable ItemDisposalFunc itemDisposalFunc); + @Nullable ShouldDisposeFunc shouldDisposeFunc, + @Nullable ItemDisposalFunc itemDisposalFunc); /** * Stores an item under the given category. @@ -68,7 +68,7 @@ void registerItemCategoryIfAbsent( * @param the type of the item being retrieved. * @return the item stored at the given key under the given category. */ - @Nullable V get(String itemCategory, Object key); + @Nullable V get(String itemCategory, Object key, Class itemClass); /** * Indicates whether an item exists under the given item category and key. @@ -84,10 +84,8 @@ void registerItemCategoryIfAbsent( * * @param itemCategory a String representing the item category, eg "customEndpoint". * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". - * @param the type of the item being removed. - * @return the item removed from the storage service. */ - @Nullable V remove(String itemCategory, Object key); + void remove(String itemCategory, Object key); /** * Clears all items from the given category. For example, storageService.clear("customEndpoint") will remove all diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 1c075be72..1e7ad7136 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -26,21 +26,21 @@ public class StorageServiceImpl implements StorageService { private static final Logger LOGGER = Logger.getLogger(StorageServiceImpl.class.getName()); - protected static Map> caches = new ConcurrentHashMap<>(); + protected static Map> caches = new ConcurrentHashMap<>(); public StorageServiceImpl() { } @Override - public void registerItemCategoryIfAbsent( + public void registerItemCategoryIfAbsent( String itemCategory, - Class itemClass, + Class itemClass, boolean isRenewableExpiration, long timeToLiveNanos, long cleanupIntervalNanos, - @Nullable ShouldDisposeFunc shouldDisposeFunc, - @Nullable ItemDisposalFunc itemDisposalFunc) { + @Nullable ShouldDisposeFunc shouldDisposeFunc, + @Nullable ItemDisposalFunc itemDisposalFunc) { caches.computeIfAbsent( itemCategory, category -> new ExpirationCache<>( @@ -54,19 +54,27 @@ public void registerItemCategoryIfAbsent( @Override public void set(String itemCategory, Object key, V value) { - ExpirationCache cache = caches.get(itemCategory); + final ExpirationCache cache = caches.get(itemCategory); if (cache == null) { // TODO: what should we do if the item category isn't registered? return; } - cache.put(key, value); + Class expectedType = cache.getValueClass(); + if (!expectedType.isInstance(value)) { + throw new IllegalArgumentException( + Messages.get( + "StorageServiceImpl.incorrectValueType", + new Object[]{itemCategory, expectedType, value.getClass(), value})); + } + + ExpirationCache typedCache = (ExpirationCache) cache; + typedCache.put(key, value); } @Override - @SuppressWarnings("unchecked") - public @Nullable V get(String itemCategory, Object key) { - final ExpirationCache cache = caches.get(itemCategory); + public @Nullable V get(String itemCategory, Object key, Class itemClass) { + final ExpirationCache cache = caches.get(itemCategory); if (cache == null) { return null; } @@ -76,15 +84,14 @@ public void set(String itemCategory, Object key, V value) { return null; } - Class valueClass = cache.getValueClass(); - if (valueClass.isInstance(value)) { - return (V) value; + if (itemClass.isInstance(value)) { + return itemClass.cast(value); } LOGGER.fine( Messages.get( - "StorageServiceImpl.classMismatch", - new Object[]{key, itemCategory, valueClass, value.getClass()})); + "StorageServiceImpl.itemClassMismatch", + new Object[]{key, itemCategory, itemClass, value.getClass()})); return null; } @@ -99,28 +106,13 @@ public boolean exists(String itemCategory, Object key) { } @Override - @SuppressWarnings("unchecked") - public @Nullable V remove(String itemCategory, Object key) { + public void remove(String itemCategory, Object key) { final ExpirationCache cache = caches.get(itemCategory); if (cache == null) { - return null; - } - - final Object value = cache.remove(key); - if (value == null) { - return null; - } - - Class valueClass = cache.getValueClass(); - if (valueClass.isInstance(value)) { - return (V) value; + return; } - LOGGER.fine( - Messages.get( - "StorageServiceImpl.classMismatch", - new Object[]{key, itemCategory, valueClass, value.getClass()})); - return null; + cache.remove(key); } @Override @@ -140,18 +132,18 @@ public void clearAll() { @Override public @Nullable Map getEntries(String itemCategory) { - final ExpirationCache cache = caches.get(itemCategory); + final ExpirationCache cache = caches.get(itemCategory); if (cache == null) { return null; } - // TODO: check if this can be safely casted + // TODO: fix this cast to be type safe, or remove this method after removing its callers return (Map) cache.getEntries(); } @Override public int size(String itemCategory) { - final ExpirationCache cache = caches.get(itemCategory); + final ExpirationCache cache = caches.get(itemCategory); if (cache == null) { return 0; } diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 439fe74ed..5c8451b0c 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -352,7 +352,8 @@ SAMLCredentialsProviderFactory.getSamlAssertionFailed=Failed to get SAML Asserti SamlAuthPlugin.javaStsSdkNotInClasspath=Required dependency 'AWS Java SDK for AWS Secret Token Service' is not on the classpath. SamlAuthPlugin.unhandledException=Unhandled exception: ''{0}'' -StorageServiceImpl.classMismatch=The item stored at {0} did not have the expected type. Items stored under the {1} category should have a type of {2}, but the stored item had a type of {3}. Returning null. +StorageServiceImpl.incorrectValueType=Attempted to store an incorrect item type under item category {0}. Items under this category should have type {1} but the passed in item had a type of {2}. Item value: {3}. +StorageServiceImpl.itemClassMismatch=The item stored at {0} under the category {1} did not have the expected type. The expected type was {2}, but the stored item had a type of {3}. Returning null. # Wrapper Utils WrapperUtils.noWrapperClassExists=No wrapper class exists for ''{0}''. From 5d3f698f9d0a0a9722efdb2dab9a046f44ef5600 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 13 Feb 2025 09:28:01 -0800 Subject: [PATCH 019/149] Failover integration test passing --- .../hostlistprovider/RdsHostListProvider.java | 33 ++++++++++--------- .../ClusterTopologyMonitorImpl.java | 31 ++++++++++++----- .../MonitoringRdsHostListProvider.java | 6 ++-- .../jdbc/util/storage/StorageService.java | 22 +++++++------ 4 files changed, 56 insertions(+), 36 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index 208823af5..f55e1d8de 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -60,6 +60,7 @@ import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; +import software.amazon.jdbc.util.storage.Topology; public class RdsHostListProvider implements DynamicHostListProvider { @@ -245,14 +246,15 @@ public FetchTopologyResult getTopology(final Connection conn, final boolean forc this.clusterIdChanged(oldClusterId); } - final List cachedHosts = storageService.get(ItemCategory.TOPOLOGY, this.clusterId); + final Topology cachedTopology = storageService.get(ItemCategory.TOPOLOGY, this.clusterId, Topology.class); + final List cachedHosts = cachedTopology == null ? null : cachedTopology.getHosts(); // This clusterId is a primary one and is about to create a new entry in the cache. // When a primary entry is created it needs to be suggested for other (non-primary) entries. // Remember a flag to do suggestion after cache is updated. - final boolean needToSuggest = cachedHosts == null && this.isPrimaryClusterId; + final boolean needToSuggest = cachedTopology == null && this.isPrimaryClusterId; - if (cachedHosts == null || forceUpdate) { + if (cachedTopology == null || forceUpdate) { // need to re-fetch topology @@ -266,7 +268,7 @@ public FetchTopologyResult getTopology(final Connection conn, final boolean forc final List hosts = queryForTopology(conn); if (!Utils.isNullOrEmpty(hosts)) { - storageService.set(ItemCategory.TOPOLOGY, this.clusterId, hosts); + storageService.set(ItemCategory.TOPOLOGY, this.clusterId, new Topology(hosts)); if (needToSuggest) { this.suggestPrimaryCluster(hosts); } @@ -287,14 +289,14 @@ protected void clusterIdChanged(final String oldClusterId) { } protected ClusterSuggestedResult getSuggestedClusterId(final String url) { - Map> entries = storageService.getEntries(ItemCategory.TOPOLOGY); + Map entries = storageService.getEntries(ItemCategory.TOPOLOGY); if (entries == null) { return null; } - for (final Entry> entry : entries.entrySet()) { + for (final Entry entry : entries.entrySet()) { final String key = entry.getKey(); // clusterId - final List hosts = entry.getValue(); + final List hosts = entry.getValue().getHosts(); final boolean isPrimaryCluster = primaryClusterIdCache.get(key, false, suggestedClusterIdRefreshRateNano); if (key.equals(url)) { @@ -324,14 +326,14 @@ protected void suggestPrimaryCluster(final @NonNull List primaryCluste primaryClusterHostUrls.add(hostSpec.getUrl()); } - Map> entries = storageService.getEntries(ItemCategory.TOPOLOGY); + Map entries = storageService.getEntries(ItemCategory.TOPOLOGY); if (entries == null) { return; } - for (final Entry> entry : entries.entrySet()) { + for (final Entry entry : entries.entrySet()) { final String clusterId = entry.getKey(); - final List clusterHosts = entry.getValue(); + final List clusterHosts = entry.getValue().getHosts(); final boolean isPrimaryCluster = primaryClusterIdCache.get(clusterId, false, suggestedClusterIdRefreshRateNano); final String suggestedPrimaryClusterId = suggestedPrimaryClusterIdCache.get(clusterId); @@ -507,7 +509,8 @@ protected String getHostEndpoint(final String nodeName) { * cached topology is outdated, it returns null. */ public @Nullable List getCachedTopology() { - return storageService.get(ItemCategory.TOPOLOGY, this.clusterId); + Topology topology = storageService.get(ItemCategory.TOPOLOGY, this.clusterId, Topology.class); + return topology == null ? null : topology.getHosts(); } /** @@ -600,20 +603,20 @@ private void validateHostPatternSetting(final String hostPattern) { public static void logCache() { LOGGER.finest(() -> { final StringBuilder sb = new StringBuilder(); - Map> entries = storageService.getEntries(ItemCategory.TOPOLOGY); + Map entries = storageService.getEntries(ItemCategory.TOPOLOGY); if (entries == null) { return ""; } - final Set>> cacheEntries = entries.entrySet(); + final Set> cacheEntries = entries.entrySet(); if (cacheEntries.isEmpty()) { sb.append("Cache is empty."); return sb.toString(); } - for (final Entry> entry : cacheEntries) { - final List hosts = entry.getValue(); + for (final Entry entry : cacheEntries) { + final List hosts = entry.getValue().getHosts(); final Boolean isPrimaryCluster = primaryClusterIdCache.get(entry.getKey()); final String suggestedPrimaryClusterId = suggestedPrimaryClusterIdCache.get(entry.getKey()); diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index f1c1f0cf5..a3e8fd642 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -56,6 +56,7 @@ import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.storage.Topology; public class ClusterTopologyMonitorImpl implements ClusterTopologyMonitor { @@ -185,7 +186,8 @@ public List forceRefresh(final boolean shouldVerifyWriter, final long && System.nanoTime() < this.ignoreNewTopologyRequestsEndTimeNano.get()) { // Previous failover has just completed. We can use results of it without triggering a new topology update. - List currentHosts = storageService.get(ItemCategory.TOPOLOGY, this.clusterId); + Topology topology = storageService.get(ItemCategory.TOPOLOGY, this.clusterId, Topology.class); + List currentHosts = topology == null ? null : topology.getHosts(); LOGGER.finest( Utils.logTopology(currentHosts, Messages.get("ClusterTopologyMonitorImpl.ignoringTopologyRequest"))); if (currentHosts != null) { @@ -217,8 +219,7 @@ public List forceRefresh(@Nullable Connection connection, final long t } protected List waitTillTopologyGetsUpdated(final long timeoutMs) throws TimeoutException { - - List currentHosts = storageService.get(ItemCategory.TOPOLOGY, this.clusterId); + List currentHosts = getLatestHosts(); List latestHosts; synchronized (this.requestToUpdateTopology) { @@ -238,7 +239,7 @@ protected List waitTillTopologyGetsUpdated(final long timeoutMs) throw // Note that we are checking reference equality instead of value equality here. We will break out of the loop if // there is a new entry in the topology map, even if the value of the hosts in latestHosts is the same as // currentHosts. - while (currentHosts == (latestHosts = storageService.get(ItemCategory.TOPOLOGY, this.clusterId)) + while (currentHosts == (latestHosts = getLatestHosts()) && System.nanoTime() < end) { try { synchronized (this.topologyUpdated) { @@ -260,6 +261,11 @@ protected List waitTillTopologyGetsUpdated(final long timeoutMs) throw return latestHosts; } + private List getLatestHosts() { + Topology topology = storageService.get(ItemCategory.TOPOLOGY, this.clusterId, Topology.class); + return topology == null ? null : topology.getHosts(); + } + @Override public void close() throws Exception { this.stop.set(true); @@ -300,7 +306,8 @@ public void run() { this.nodeThreadsWriterHostSpec.set(null); this.nodeThreadsLatestTopology.set(null); - List hosts = storageService.get(ItemCategory.TOPOLOGY, this.clusterId); + Topology topology = storageService.get(ItemCategory.TOPOLOGY, this.clusterId, Topology.class); + List hosts = topology == null ? null : topology.getHosts(); if (hosts == null) { // need any connection to get topology hosts = this.openAnyConnectionAndUpdateTopology(); @@ -396,7 +403,9 @@ public void run() { // Do not log topology while in high refresh rate. It's noisy! if (this.highRefreshRateEndTimeNano == 0) { - LOGGER.finest(Utils.logTopology(storageService.get(ItemCategory.TOPOLOGY, this.clusterId))); + Topology topology = storageService.get(ItemCategory.TOPOLOGY, this.clusterId, Topology.class); + List currentHosts = topology == null ? null : topology.getHosts(); + LOGGER.finest(Utils.logTopology(currentHosts)); } this.delay(false); @@ -592,7 +601,7 @@ protected void delay(boolean useHighRefreshRate) throws InterruptedException { protected void updateTopologyCache(final @NonNull List hosts) { synchronized (this.requestToUpdateTopology) { - storageService.set(ItemCategory.TOPOLOGY, this.clusterId, hosts); + storageService.set(ItemCategory.TOPOLOGY, this.clusterId, new Topology(hosts)); synchronized (this.topologyUpdated) { this.requestToUpdateTopology.set(false); @@ -831,8 +840,12 @@ public void run() { this.monitor.fetchTopologyAndUpdateCache(connection); this.monitor.nodeThreadsWriterHostSpec.set(hostSpec); this.monitor.nodeThreadsStop.set(true); - LOGGER.fine(Utils.logTopology( - this.monitor.storageService.get(ItemCategory.TOPOLOGY, this.monitor.clusterId))); + + Topology topology = this.monitor.storageService.get( + ItemCategory.TOPOLOGY, this.monitor.clusterId, Topology.class); + List currentHosts = topology == null ? null : topology.getHosts(); + + LOGGER.fine(Utils.logTopology(currentHosts)); } // Setting the connection to null here prevents the finally block diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index 5e418ba5f..beacd7417 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -33,6 +33,7 @@ import software.amazon.jdbc.hostlistprovider.RdsHostListProvider; import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.storage.ItemCategory; +import software.amazon.jdbc.util.storage.Topology; public class MonitoringRdsHostListProvider extends RdsHostListProvider implements BlockingHostListProvider, CanReleaseResources { @@ -140,9 +141,10 @@ protected void clusterIdChanged(final String oldClusterId) { monitors.remove(oldClusterId); } - final List existingHosts = storageService.get(ItemCategory.TOPOLOGY, oldClusterId); + final Topology existingTopology = storageService.get(ItemCategory.TOPOLOGY, oldClusterId, Topology.class); + final List existingHosts = existingTopology == null ? null : existingTopology.getHosts(); if (existingHosts != null) { - storageService.set(ItemCategory.TOPOLOGY, this.clusterId, existingHosts); + storageService.set(ItemCategory.TOPOLOGY, this.clusterId, new Topology(existingHosts)); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java index 723370c8a..3ed600e08 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java @@ -24,29 +24,29 @@ public interface StorageService { /** * Register a new item category with the storage service. This method needs to be called before adding new categories - * of items to the storage service, so that the storage service knows when and how to dispose of the item. Expected - * item categories ("topology" and "customEndpoint") will be added automatically during driver initialization, but - * this method can be called by users if they want to add a new category. + * of items to the service, so that the service knows when and how to dispose of the item. Expected item categories + * ("topology" and "customEndpoint") will be added automatically during driver initialization, but this method can be + * called by users if they want to add a new category. * * @param itemCategory a String representing the item category, eg "customEndpoint". - * @param itemClass the class of item that will be stored under this category, eg `CustomEndpointInfo - * .class`. + * @param itemClass the class of item that will be stored under this category, eg + * `CustomEndpointInfo.class`. * @param isRenewableExpiration controls whether the item's expiration should be renewed if the item is fetched, * regardless of whether it is already expired or not. * @param cleanupIntervalNanos how often the item category should be cleaned of expired entries, in nanoseconds. - * @param expirationTimeoutNanos how long an item should be stored before expiring, in nanoseconds. + * @param timeToLiveNanos how long an item should be stored before being considered expired, in nanoseconds. * @param shouldDisposeFunc a function defining whether an item should be disposed if expired. If null is passed, * the item will always be disposed if expired. * @param itemDisposalFunc a function defining how to dispose of an item when it is removed. If null is - * passed, the - * item will be removed without performing any additional operations. + * passed, the item will be removed without performing any additional operations. + * @param the type of item that will be stored under the item category. */ void registerItemCategoryIfAbsent( String itemCategory, Class itemClass, boolean isRenewableExpiration, long cleanupIntervalNanos, - long expirationTimeoutNanos, + long timeToLiveNanos, @Nullable ShouldDisposeFunc shouldDisposeFunc, @Nullable ItemDisposalFunc itemDisposalFunc); @@ -65,6 +65,7 @@ void registerItemCategoryIfAbsent( * * @param itemCategory a String representing the item category, eg "customEndpoint". * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". + * @param itemClass the expected class of the item being retrieved, eg `CustomEndpointInfo.class`. * @param the type of the item being retrieved. * @return the item stored at the given key under the given category. */ @@ -101,8 +102,9 @@ void registerItemCategoryIfAbsent( void clearAll(); // TODO: this is only called by the suggestedClusterId logic in RdsHostListProvider, which will be removed. This - // method should possibly be removed at that point as well. + // method should potentially be removed at that point as well. @Nullable Map getEntries(String itemCategory); + // TODO: this method is only called by tests. We should evaluate whether we want to keep it or not. int size(String itemCategory); } From 833b3844fcdf25b28d9a001b7c78d413640b3cc7 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 13 Feb 2025 13:13:48 -0800 Subject: [PATCH 020/149] Make MonitorStatus immutable, update StorageService API --- .../jdbc/util/monitoring/MonitorStatus.java | 21 ++---- .../jdbc/util/storage/StorageService.java | 70 +++++++++++-------- 2 files changed, 45 insertions(+), 46 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java index 8091f1ccb..2fb4a3c14 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java @@ -19,36 +19,25 @@ import org.checkerframework.checker.nullness.qual.Nullable; public class MonitorStatus { - private MonitorState state; - private long lastUsedTimeNs; - private @Nullable Throwable exception; + private final MonitorState state; + private final long lastUsedTimeNs; + private final @Nullable Throwable exception; - public MonitorStatus(MonitorState state, long lastUsedTimeNs) { + public MonitorStatus(MonitorState state, long lastUsedTimeNs, @Nullable Throwable exception) { this.state = state; this.lastUsedTimeNs = lastUsedTimeNs; + this.exception = exception; } public MonitorState getState() { return state; } - public void setState(MonitorState state) { - this.state = state; - } - public long getLastUsedTimeNs() { return lastUsedTimeNs; } - public void setLastUsedTimeNs(long lastUsedTimeNs) { - this.lastUsedTimeNs = lastUsedTimeNs; - } - public @Nullable Throwable getException() { return this.exception; } - - public void setException(@Nullable Throwable exception) { - this.exception = exception; - } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java index a2737fa3e..3ed600e08 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java @@ -16,35 +16,39 @@ package software.amazon.jdbc.util.storage; +import java.util.Map; import org.checkerframework.checker.nullness.qual.Nullable; -import software.amazon.jdbc.plugin.customendpoint.CustomEndpointInfo; import software.amazon.jdbc.util.ItemDisposalFunc; import software.amazon.jdbc.util.ShouldDisposeFunc; public interface StorageService { /** * Register a new item category with the storage service. This method needs to be called before adding new categories - * of items to the storage service, so that the storage service knows when and how to dispose of the item. Expected - * item categories ("topology" and "customEndpoint") will be added automatically during driver initialization, but - * this method can be called by users if they want to add a new category. + * of items to the service, so that the service knows when and how to dispose of the item. Expected item categories + * ("topology" and "customEndpoint") will be added automatically during driver initialization, but this method can be + * called by users if they want to add a new category. * - * @param itemCategory a String representing the item category, eg "customEndpoint". - * @param itemClass the class of item that will be stored under this category, eg `CustomEndpointInfo.class`. - * @param cleanupIntervalNs how often the item category should be cleaned of expired entries, in nanoseconds. - * @param expirationTimeNs how long an item should be stored before expiring, in nanoseconds. - * @param shouldDisposeFunc a function defining whether an item should be disposed if expired. If `null` is passed, - * the item will always be disposed if expired. - * @param itemDisposalFunc a function defining how to dispose of an item when it is removed. If `null` is passed, the - * item will be removed without performing any additional operations. - * @param the type of item that will be stored under this category, eg {@link CustomEndpointInfo}. + * @param itemCategory a String representing the item category, eg "customEndpoint". + * @param itemClass the class of item that will be stored under this category, eg + * `CustomEndpointInfo.class`. + * @param isRenewableExpiration controls whether the item's expiration should be renewed if the item is fetched, + * regardless of whether it is already expired or not. + * @param cleanupIntervalNanos how often the item category should be cleaned of expired entries, in nanoseconds. + * @param timeToLiveNanos how long an item should be stored before being considered expired, in nanoseconds. + * @param shouldDisposeFunc a function defining whether an item should be disposed if expired. If null is passed, + * the item will always be disposed if expired. + * @param itemDisposalFunc a function defining how to dispose of an item when it is removed. If null is + * passed, the item will be removed without performing any additional operations. + * @param the type of item that will be stored under the item category. */ - void registerItemCategoryIfAbsent( + void registerItemCategoryIfAbsent( String itemCategory, - Class itemClass, - long cleanupIntervalNs, - long expirationTimeNs, - @Nullable ShouldDisposeFunc shouldDisposeFunc, - @Nullable ItemDisposalFunc itemDisposalFunc); + Class itemClass, + boolean isRenewableExpiration, + long cleanupIntervalNanos, + long timeToLiveNanos, + @Nullable ShouldDisposeFunc shouldDisposeFunc, + @Nullable ItemDisposalFunc itemDisposalFunc); /** * Stores an item under the given category. @@ -52,38 +56,37 @@ void registerItemCategoryIfAbsent( * @param itemCategory a String representing the item category, eg "customEndpoint". * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". * @param item the item to store. - * @param the type of the item being stored. + * @param the type of the item being stored. */ - void set(String itemCategory, Object key, T item); + void set(String itemCategory, Object key, V item); /** * Gets an item stored under the given category. * * @param itemCategory a String representing the item category, eg "customEndpoint". * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". - * @param the type of the item being retrieved. + * @param itemClass the expected class of the item being retrieved, eg `CustomEndpointInfo.class`. + * @param the type of the item being retrieved. * @return the item stored at the given key under the given category. */ - @Nullable T get(String itemCategory, Object key); + @Nullable V get(String itemCategory, Object key, Class itemClass); /** - * Removes an item stored under the given category. + * Indicates whether an item exists under the given item category and key. * * @param itemCategory a String representing the item category, eg "customEndpoint". * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". - * @param the type of the item being removed. - * @return the item removed from the storage service. + * @return true if the item exists under the given item category and key, otherwise returns false. */ - @Nullable T remove(String itemCategory, Object key); + boolean exists(String itemCategory, Object key); /** - * Indicates whether an item exists under the given item category and key. + * Removes an item stored under the given category. * * @param itemCategory a String representing the item category, eg "customEndpoint". * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". - * @return true if the item exists under the given item category and key, otherwise returns false. */ - boolean exists(String itemCategory, Object key); + void remove(String itemCategory, Object key); /** * Clears all items from the given category. For example, storageService.clear("customEndpoint") will remove all @@ -97,4 +100,11 @@ void registerItemCategoryIfAbsent( * Clears all information from all categories of the storage service. */ void clearAll(); + + // TODO: this is only called by the suggestedClusterId logic in RdsHostListProvider, which will be removed. This + // method should potentially be removed at that point as well. + @Nullable Map getEntries(String itemCategory); + + // TODO: this method is only called by tests. We should evaluate whether we want to keep it or not. + int size(String itemCategory); } From 23c09679423b0ea48bb081658941f843a62cb3f4 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 13 Feb 2025 14:23:46 -0800 Subject: [PATCH 021/149] EventPublisher PR suggestions --- .../software/amazon/jdbc/util/events/EventPublisher.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java index e18f36a22..694d07895 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java @@ -16,6 +16,8 @@ package software.amazon.jdbc.util.events; +import java.util.Set; + public interface EventPublisher { /** * Register the given subscriber for the given event classes. @@ -23,7 +25,7 @@ public interface EventPublisher { * @param subscriber the subscriber to be notified when the given event classes occur. * @param eventClasses the classes of event that the subscriber should be notified of. */ - void subscribe(EventSubscriber subscriber, Class... eventClasses); + void subscribe(EventSubscriber subscriber, Set> eventClasses); /** * Unsubscribe the subscriber from the given event classes. @@ -31,7 +33,7 @@ public interface EventPublisher { * @param subscriber the subscriber to unsubscribe from the given event classes. * @param eventClasses the classes of events that the subscriber wants to unsubscribe from. */ - void unsubscribe(EventSubscriber subscriber, Class... eventClasses); + void unsubscribe(EventSubscriber subscriber, Set> eventClasses); /** * Publish an event. All subscribers to the given event class will be notified of the event. From 8b3e30ee600d326cd99d8df10fb3dbd3c1e5cbe0 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 13 Feb 2025 14:30:04 -0800 Subject: [PATCH 022/149] Remove MonitorGroup and MonitorServiceImpl --- .../jdbc/util/monitoring/MonitorGroup.java | 57 ------- .../util/monitoring/MonitorServiceImpl.java | 144 ------------------ 2 files changed, 201 deletions(-) delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorGroup.java delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorGroup.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorGroup.java deleted file mode 100644 index a2fd7cc93..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorGroup.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.util.monitoring; - -import java.util.Set; -import java.util.function.Supplier; -import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; - -public class MonitorGroup { - private final String groupName; - private final Supplier supplier; - private final Set exceptionResponses; - private final SlidingExpirationCacheWithCleanupThread cache; - - public MonitorGroup(String groupName, Supplier supplier, - Set exceptionResponses, - SlidingExpirationCacheWithCleanupThread cache) { - this.groupName = groupName; - this.supplier = supplier; - this.exceptionResponses = exceptionResponses; - this.cache = cache; - } - - public String getGroupName() { - return groupName; - } - - public Supplier getSupplier() { - return supplier; - } - - public Set getExceptionResponses() { - return exceptionResponses; - } - - public SlidingExpirationCacheWithCleanupThread getCache() { - return cache; - } - - public int size() { - return cache.size(); - } -} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java deleted file mode 100644 index e80d80fd6..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.util.monitoring; - -// public class MonitorServiceImpl implements MonitorService { -// private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); -// private final Map monitorGroups = new ConcurrentHashMap<>(); -// private static MonitorServiceImpl instance; -// -// private MonitorServiceImpl() { -// -// } -// -// public static MonitorService getInstance() { -// if (instance == null) { -// instance = new MonitorServiceImpl(); -// } -// -// return instance; -// } -// -// -// @Override -// public void registerMonitorTypeIfAbsent(String monitorType, -// Supplier> cacheSupplier, -// MonitorInitializer initializer, -// Set exceptionResponses) { -// monitorGroups.computeIfAbsent(monitorType, -// key -> new MonitorGroup(monitorType, initializer, exceptionResponses, cacheSupplier.get())); -// } -// -// @Override -// public void runIfAbsent( -// String monitorType, -// Object monitorKey, -// long timeToLiveNs, -// Object... initializerParams) { -// MonitorGroup group = monitorGroups.get(monitorType); -// if (group == null) { -// throw new RuntimeException( -// "A monitor with key '" + monitorKey + "' was requested, but the monitor type '" -// + monitorType + "' does not exist."); -// } -// -// SlidingExpirationCacheWithCleanupThread cache = group.getCache(); -// cache.computeIfAbsent( -// monitorKey, -// key -> { -// Monitor monitor = group.getInitializer().initialize(initializerParams); -// monitor.start(); -// return monitor; -// }, -// timeToLiveNs); -// } -// -// @Override -// public int numMonitors(String monitorType) { -// MonitorGroup group = monitorGroups.get(monitorType); -// if (group == null) { -// return 0; -// } -// -// return group.getCache().size(); -// } -// -// @Override -// public @Nullable MonitorStatus getStatus(String monitorType, Object monitorKey, long timeToLiveNs) { -// MonitorGroup group = monitorGroups.get(monitorType); -// if (group == null) { -// return null; -// } -// -// Monitor monitor = group.getCache().get(monitorKey, timeToLiveNs); -// if (monitor == null) { -// return null; -// } -// -// return monitor.getStatus(); -// } -// -// @Override -// public void stopAndRemoveMonitor(String monitorType, Object monitorKey) { -// MonitorGroup group = monitorGroups.get(monitorType); -// if (group == null) { -// return; -// } -// -// Monitor monitor = group.getCache().get(monitorKey, 0); -// if (monitor != null) { -// group.getCache().remove(monitorKey); -// monitor.stop(); -// } -// } -// -// @Override -// public void stopAndRemoveMonitors(String monitorType) { -// MonitorGroup group = monitorGroups.get(monitorType); -// if (group == null) { -// return; -// } -// -// for (Object monitorKey : group.getCache().getEntries().keySet()) { -// stopAndRemoveMonitor(monitorType, monitorKey); -// } -// } -// -// @Override -// public void deleteMonitorType(String monitorType) { -// MonitorGroup group = monitorGroups.remove(monitorType); -// if (group != null) { -// return; -// } -// -// stopAndRemoveMonitors(monitorType); -// } -// -// @Override -// public void stopAndRemoveAll() { -// for (String group : monitorGroups.keySet()) { -// stopAndRemoveMonitors(group); -// } -// } -// -// @Override -// public void deleteAll() { -// for (String group : monitorGroups.keySet()) { -// deleteMonitorType(group); -// } -// } -// } From a9041ae6f891b4ea2fb8cc7946ff38ce05f6c467 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 13 Feb 2025 18:28:22 -0800 Subject: [PATCH 023/149] Instantiate StorageService in Driver class --- .../java/software/amazon/jdbc/Driver.java | 4 +- .../amazon/jdbc/PluginServiceImpl.java | 13 ++++- .../jdbc/dialect/AuroraMysqlDialect.java | 3 +- .../amazon/jdbc/dialect/AuroraPgDialect.java | 3 +- .../dialect/HostListProviderSupplier.java | 4 +- .../AuroraHostListProvider.java | 3 ++ .../hostlistprovider/RdsHostListProvider.java | 48 ++----------------- .../jdbc/wrapper/ConnectionWrapper.java | 11 ++++- .../RdsHostListProviderTest.java | 1 + 9 files changed, 38 insertions(+), 52 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index d31f3e7f6..81d7f88a7 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -82,6 +82,8 @@ public class Driver implements java.sql.Driver { private static final Logger LOGGER = Logger.getLogger("software.amazon.jdbc.Driver"); private static @Nullable Driver registeredDriver; + private static final StorageService storageService = new StorageServiceImpl(); + private static final AtomicReference resetSessionStateOnCloseCallable = new AtomicReference<>(null); private static final AtomicReference transferSessionStateOnSwitchCallable = @@ -119,7 +121,6 @@ public static void register() throws SQLException { DriverManager.registerDriver(driver); registeredDriver = driver; - StorageService storageService = new StorageServiceImpl(); storageService.registerItemCategoryIfAbsent( ItemCategory.TOPOLOGY, Topology.class, @@ -403,6 +404,7 @@ public static void resetConnectionInitFunc() { } public static void clearCaches() { + storageService.clearAll(); RdsUtils.clearCache(); RdsHostListProvider.clearAll(); PluginServiceImpl.clearCache(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index a747ccf47..1514f310f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -54,6 +54,7 @@ import software.amazon.jdbc.util.CacheMap; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class PluginServiceImpl implements PluginService, CanReleaseResources, @@ -64,6 +65,7 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources, protected static final CacheMap hostAvailabilityExpiringCache = new CacheMap<>(); protected final ConnectionPluginManager pluginManager; + protected final StorageService storageService; private final Properties props; private final String originalUrl; private final String driverProtocol; @@ -91,7 +93,8 @@ public PluginServiceImpl( @NonNull final Properties props, @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, - @NonNull final TargetDriverDialect targetDriverDialect) + @NonNull final TargetDriverDialect targetDriverDialect, + @NonNull final StorageService storageService) throws SQLException { this(pluginManager, @@ -101,6 +104,7 @@ public PluginServiceImpl( targetDriverProtocol, null, targetDriverDialect, + storageService, null, null); } @@ -111,6 +115,7 @@ public PluginServiceImpl( @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, @NonNull final TargetDriverDialect targetDriverDialect, + @NonNull final StorageService storageService, @Nullable final ConfigurationProfile configurationProfile) throws SQLException { this(pluginManager, new ExceptionManager(), @@ -119,6 +124,7 @@ public PluginServiceImpl( targetDriverProtocol, null, targetDriverDialect, + storageService, configurationProfile, null); } @@ -131,6 +137,7 @@ public PluginServiceImpl( @NonNull final String targetDriverProtocol, @Nullable final DialectProvider dialectProvider, @NonNull final TargetDriverDialect targetDriverDialect, + @NonNull final StorageService storageService, @Nullable final ConfigurationProfile configurationProfile, @Nullable final SessionStateService sessionStateService) throws SQLException { this.pluginManager = pluginManager; @@ -141,6 +148,7 @@ public PluginServiceImpl( this.exceptionManager = exceptionManager; this.dialectProvider = dialectProvider != null ? dialectProvider : new DialectManager(this); this.targetDriverDialect = targetDriverDialect; + this.storageService = storageService; this.connectionProviderManager = new ConnectionProviderManager( this.pluginManager.getDefaultConnProvider(), this.pluginManager.getEffectiveConnProvider()); @@ -705,7 +713,8 @@ public void updateDialect(final @NonNull Connection connection) throws SQLExcept } final HostListProviderSupplier supplier = this.dialect.getHostListProvider(); - this.setHostListProvider(supplier.getProvider(props, this.originalUrl, this, this)); + this.setHostListProvider(supplier.getProvider( + this.props, this.originalUrl, this, this, this.storageService)); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java index 45a8ab8eb..694d7fcb3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java @@ -81,7 +81,7 @@ public boolean isDialect(final Connection connection) { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService) -> { + return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> { final FailoverConnectionPlugin failover2Plugin = pluginService.getPlugin(FailoverConnectionPlugin.class); @@ -100,6 +100,7 @@ public HostListProviderSupplier getHostListProvider() { properties, initialUrl, hostListProviderService, + storageService, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY); diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java index 2371b2905..6cefc0e09 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java @@ -127,7 +127,7 @@ public boolean isDialect(final Connection connection) { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService) -> { + return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> { final FailoverConnectionPlugin failover2Plugin = pluginService.getPlugin(FailoverConnectionPlugin.class); @@ -146,6 +146,7 @@ public HostListProviderSupplier getHostListProvider() { properties, initialUrl, hostListProviderService, + storageService, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY); diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java index ae10b389b..46fe0573f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java @@ -21,6 +21,7 @@ import software.amazon.jdbc.HostListProvider; import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.storage.StorageService; @FunctionalInterface public interface HostListProviderSupplier { @@ -28,5 +29,6 @@ public interface HostListProviderSupplier { final @NonNull Properties properties, final String initialUrl, final @NonNull HostListProviderService hostListProviderService, - final @NonNull PluginService pluginService); + final @NonNull PluginService pluginService, + final @NonNull StorageService storageService); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java index 1d872c026..f019b971e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java @@ -20,6 +20,7 @@ import java.util.Properties; import java.util.logging.Logger; import software.amazon.jdbc.HostListProviderService; +import software.amazon.jdbc.util.storage.StorageService; public class AuroraHostListProvider extends RdsHostListProvider { @@ -30,12 +31,14 @@ public AuroraHostListProvider( final Properties properties, final String originalUrl, final HostListProviderService hostListProviderService, + final StorageService storageService, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery) { super(properties, originalUrl, hostListProviderService, + storageService, topologyQuery, nodeIdQuery, isReaderQuery); diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index f55e1d8de..5663339d7 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -59,7 +59,6 @@ import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.storage.Topology; public class RdsHostListProvider implements DynamicHostListProvider { @@ -94,10 +93,10 @@ public class RdsHostListProvider implements DynamicHostListProvider { protected static final ConnectionUrlParser connectionUrlParser = new ConnectionUrlParser(); protected static final int defaultTopologyQueryTimeoutMs = 5000; protected static final long suggestedClusterIdRefreshRateNano = TimeUnit.MINUTES.toNanos(10); - protected static final StorageService storageService = new StorageServiceImpl(); protected static final CacheMap suggestedPrimaryClusterIdCache = new CacheMap<>(); protected static final CacheMap primaryClusterIdCache = new CacheMap<>(); + protected final StorageService storageService; protected final HostListProviderService hostListProviderService; protected final String originalUrl; protected final String topologyQuery; @@ -131,10 +130,12 @@ public RdsHostListProvider( final Properties properties, final String originalUrl, final HostListProviderService hostListProviderService, + final StorageService storageService, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery) { this.hostListProviderService = hostListProviderService; + this.storageService = storageService; this.properties = properties; this.originalUrl = originalUrl; this.topologyQuery = topologyQuery; @@ -517,7 +518,6 @@ protected String getHostEndpoint(final String nodeName) { * Clear topology cache for all clusters. */ public static void clearAll() { - storageService.clear(ItemCategory.TOPOLOGY); primaryClusterIdCache.clear(); suggestedPrimaryClusterIdCache.clear(); } @@ -600,48 +600,6 @@ private void validateHostPatternSetting(final String hostPattern) { } } - public static void logCache() { - LOGGER.finest(() -> { - final StringBuilder sb = new StringBuilder(); - Map entries = storageService.getEntries(ItemCategory.TOPOLOGY); - if (entries == null) { - return ""; - } - - final Set> cacheEntries = entries.entrySet(); - - if (cacheEntries.isEmpty()) { - sb.append("Cache is empty."); - return sb.toString(); - } - - for (final Entry entry : cacheEntries) { - final List hosts = entry.getValue().getHosts(); - final Boolean isPrimaryCluster = primaryClusterIdCache.get(entry.getKey()); - final String suggestedPrimaryClusterId = suggestedPrimaryClusterIdCache.get(entry.getKey()); - - if (sb.length() > 0) { - sb.append("\n"); - } - sb.append("[").append(entry.getKey()).append("]:\n") - .append("\tisPrimaryCluster: ") - .append(isPrimaryCluster != null && isPrimaryCluster).append("\n") - .append("\tsuggestedPrimaryCluster: ") - .append(suggestedPrimaryClusterId).append("\n") - .append("\tHosts: "); - - if (hosts == null) { - sb.append(""); - } else { - for (final HostSpec h : hosts) { - sb.append("\n\t").append(h); - } - } - } - return sb.toString(); - }); - } - static class FetchTopologyResult { public List hosts; diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index 33d90348b..7ab717dfd 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -55,6 +55,8 @@ import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class ConnectionWrapper implements Connection, CanReleaseResources { @@ -99,8 +101,15 @@ public ConnectionWrapper( effectiveConnectionProvider, this, telemetryFactory); + final StorageService storageService = new StorageServiceImpl(); final PluginServiceImpl pluginService = new PluginServiceImpl( - pluginManager, props, url, this.targetDriverProtocol, targetDriverDialect, this.configurationProfile); + pluginManager, + props, + url, + this.targetDriverProtocol, + targetDriverDialect, + storageService, + this.configurationProfile); init(props, pluginManager, telemetryFactory, pluginService, pluginService, pluginService); diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java index 4c1b5b260..851b413cc 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java @@ -115,6 +115,7 @@ private RdsHostListProvider getRdsHostListProvider( new Properties(), originalUrl, mockHostListProviderService, + storageService, "foo", "bar", "baz"); provider.init(); return provider; From afd7a56e9a2235b0d811ed447037d4aee12d8ffc Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 14 Feb 2025 12:53:31 -0800 Subject: [PATCH 024/149] cleanup --- .../jdbc/util/storage/ExpirationCache.java | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index e12ca9cd2..5972c8613 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -24,7 +24,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; @@ -47,7 +46,7 @@ public class ExpirationCache { protected final boolean isRenewableExpiration; protected final long cleanupIntervalNanos; protected final long timeToLiveNanos; - protected final AtomicReference> shouldDisposeFunc = new AtomicReference<>(null); + protected final ShouldDisposeFunc shouldDisposeFunc; protected final ItemDisposalFunc itemDisposalFunc; public ExpirationCache( @@ -61,7 +60,7 @@ public ExpirationCache( this.isRenewableExpiration = isRenewableExpiration; this.cleanupIntervalNanos = cleanupIntervalNanos; this.timeToLiveNanos = timeToLiveNanos; - this.shouldDisposeFunc.set(shouldDisposeFunc); + this.shouldDisposeFunc = shouldDisposeFunc; this.itemDisposalFunc = itemDisposalFunc; this.initCleanupThread(); } @@ -122,8 +121,8 @@ protected void initCleanupThread() { public @Nullable V put( final K key, final V value) { - final CacheItem - cacheItem = cache.put(key, new CacheItem(value, System.nanoTime() + this.timeToLiveNanos)); + final CacheItem cacheItem = + cache.put(key, new CacheItem(value, System.nanoTime() + this.timeToLiveNanos)); if (cacheItem == null) { return null; } @@ -143,7 +142,7 @@ protected void initCleanupThread() { return null; } - if (this.isRenewableExpiration) { + if (this.isRenewableExpiration) { cacheItem.extendExpiration(this.timeToLiveNanos); } else if (cacheItem.shouldCleanup()) { return null; @@ -249,7 +248,7 @@ protected class CacheItem { /** * CacheItem constructor. * - * @param item the item value + * @param item the item value * @param expirationTimeNanos the amount of time before a CacheItem should be marked as expired. */ public CacheItem(final V item, final long expirationTimeNanos) { @@ -267,9 +266,8 @@ public CacheItem(final V item, final long expirationTimeNanos) { */ boolean shouldCleanup() { final boolean isExpired = this.expirationTimeNanos != 0 && System.nanoTime() > this.expirationTimeNanos; - final ShouldDisposeFunc tempShouldDisposeFunc = shouldDisposeFunc.get(); - if (tempShouldDisposeFunc != null) { - return isExpired && tempShouldDisposeFunc.shouldDispose(this.item); + if (shouldDisposeFunc != null) { + return isExpired && shouldDisposeFunc.shouldDispose(this.item); } return isExpired; } From 98d6dff50515cb27ddab0a211cc4f38059017184 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 14 Feb 2025 15:08:51 -0800 Subject: [PATCH 025/149] ExpiringCache fixes --- .../hostlistprovider/RdsHostListProvider.java | 13 ++- .../ClusterTopologyMonitorImpl.java | 23 ++--- .../jdbc/util/storage/ExpirationCache.java | 95 +++++++++++++------ .../jdbc/util/storage/StorageServiceImpl.java | 2 +- .../RdsHostListProviderTest.java | 16 ++-- .../RdsMultiAzDbClusterListProviderTest.java | 8 +- 6 files changed, 90 insertions(+), 67 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index 5663339d7..8dfb68470 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -247,15 +247,14 @@ public FetchTopologyResult getTopology(final Connection conn, final boolean forc this.clusterIdChanged(oldClusterId); } - final Topology cachedTopology = storageService.get(ItemCategory.TOPOLOGY, this.clusterId, Topology.class); - final List cachedHosts = cachedTopology == null ? null : cachedTopology.getHosts(); + final List storedHosts = getStoredTopology(); // This clusterId is a primary one and is about to create a new entry in the cache. // When a primary entry is created it needs to be suggested for other (non-primary) entries. // Remember a flag to do suggestion after cache is updated. - final boolean needToSuggest = cachedTopology == null && this.isPrimaryClusterId; + final boolean needToSuggest = storedHosts == null && this.isPrimaryClusterId; - if (cachedTopology == null || forceUpdate) { + if (storedHosts == null || forceUpdate) { // need to re-fetch topology @@ -277,11 +276,11 @@ public FetchTopologyResult getTopology(final Connection conn, final boolean forc } } - if (cachedHosts == null) { + if (storedHosts == null) { return new FetchTopologyResult(false, this.initialHostList); } else { // use cached data - return new FetchTopologyResult(true, cachedHosts); + return new FetchTopologyResult(true, storedHosts); } } @@ -509,7 +508,7 @@ protected String getHostEndpoint(final String nodeName) { * @return list of hosts that represents topology. If there's no topology in the cache or the * cached topology is outdated, it returns null. */ - public @Nullable List getCachedTopology() { + public @Nullable List getStoredTopology() { Topology topology = storageService.get(ItemCategory.TOPOLOGY, this.clusterId, Topology.class); return topology == null ? null : topology.getHosts(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index a3e8fd642..584f62d74 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -186,8 +186,7 @@ public List forceRefresh(final boolean shouldVerifyWriter, final long && System.nanoTime() < this.ignoreNewTopologyRequestsEndTimeNano.get()) { // Previous failover has just completed. We can use results of it without triggering a new topology update. - Topology topology = storageService.get(ItemCategory.TOPOLOGY, this.clusterId, Topology.class); - List currentHosts = topology == null ? null : topology.getHosts(); + List currentHosts = getStoredHosts(); LOGGER.finest( Utils.logTopology(currentHosts, Messages.get("ClusterTopologyMonitorImpl.ignoringTopologyRequest"))); if (currentHosts != null) { @@ -219,7 +218,7 @@ public List forceRefresh(@Nullable Connection connection, final long t } protected List waitTillTopologyGetsUpdated(final long timeoutMs) throws TimeoutException { - List currentHosts = getLatestHosts(); + List currentHosts = getStoredHosts(); List latestHosts; synchronized (this.requestToUpdateTopology) { @@ -239,7 +238,7 @@ protected List waitTillTopologyGetsUpdated(final long timeoutMs) throw // Note that we are checking reference equality instead of value equality here. We will break out of the loop if // there is a new entry in the topology map, even if the value of the hosts in latestHosts is the same as // currentHosts. - while (currentHosts == (latestHosts = getLatestHosts()) + while (currentHosts == (latestHosts = getStoredHosts()) && System.nanoTime() < end) { try { synchronized (this.topologyUpdated) { @@ -261,7 +260,7 @@ protected List waitTillTopologyGetsUpdated(final long timeoutMs) throw return latestHosts; } - private List getLatestHosts() { + private List getStoredHosts() { Topology topology = storageService.get(ItemCategory.TOPOLOGY, this.clusterId, Topology.class); return topology == null ? null : topology.getHosts(); } @@ -306,8 +305,7 @@ public void run() { this.nodeThreadsWriterHostSpec.set(null); this.nodeThreadsLatestTopology.set(null); - Topology topology = storageService.get(ItemCategory.TOPOLOGY, this.clusterId, Topology.class); - List hosts = topology == null ? null : topology.getHosts(); + List hosts = getStoredHosts(); if (hosts == null) { // need any connection to get topology hosts = this.openAnyConnectionAndUpdateTopology(); @@ -403,9 +401,7 @@ public void run() { // Do not log topology while in high refresh rate. It's noisy! if (this.highRefreshRateEndTimeNano == 0) { - Topology topology = storageService.get(ItemCategory.TOPOLOGY, this.clusterId, Topology.class); - List currentHosts = topology == null ? null : topology.getHosts(); - LOGGER.finest(Utils.logTopology(currentHosts)); + LOGGER.finest(Utils.logTopology(getStoredHosts())); } this.delay(false); @@ -840,12 +836,7 @@ public void run() { this.monitor.fetchTopologyAndUpdateCache(connection); this.monitor.nodeThreadsWriterHostSpec.set(hostSpec); this.monitor.nodeThreadsStop.set(true); - - Topology topology = this.monitor.storageService.get( - ItemCategory.TOPOLOGY, this.monitor.clusterId, Topology.class); - List currentHosts = topology == null ? null : topology.getHosts(); - - LOGGER.fine(Utils.logTopology(currentHosts)); + LOGGER.fine(Utils.logTopology(this.monitor.getStoredHosts())); } // Setting the connection to null here prevents the finally block diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index 5972c8613..864534895 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -32,9 +32,7 @@ import software.amazon.jdbc.util.ShouldDisposeFunc; public class ExpirationCache { - private static final Logger LOGGER = - Logger.getLogger(ExpirationCache.class.getName()); - + private static final Logger LOGGER = Logger.getLogger(ExpirationCache.class.getName()); protected final ExecutorService cleanupThreadPool = Executors.newFixedThreadPool(1, runnableTarget -> { final Thread monitoringThread = new Thread(runnableTarget); monitoringThread.setDaemon(true); @@ -90,34 +88,62 @@ protected void initCleanupThread() { } /** - * In addition to performing the logic defined by {@link Map#computeIfAbsent}, cleans up expired - * entries if we have hit cleanup time. If an expired entry is requested and we have not hit - * cleanup time or {@link ShouldDisposeFunc} indicated the entry should not be closed, the entry - * will be marked as non-expired. + * If a value does not exist for the given key or the existing value is expired and non-renewable, stores the value + * returned by the given mapping function, unless the function returns null, in which case the key will be removed. * - * @param key the key with which the specified value is to be associated - * @param mappingFunction the function to compute a value - * @return the current (existing or computed) value associated with the specified key, or null if - * the computed value is null. + * @param key the key for the new or existing value + * @param mappingFunction the function to call to compute a new value + * @return the current (existing or computed) value associated with the specified key, or null if the computed value + * is null */ public @Nullable V computeIfAbsent( final K key, Function mappingFunction) { - final CacheItem cacheItem = cache.computeIfAbsent( + // A list is used to store the cached item for later disposal since lambdas require references to outer variables + // to be final. This allows us to dispose of the item after it has been removed and the cache has been unlocked, + // which is important because the disposal function may be long-running. + final List toDisposeList = new ArrayList<>(1); + final CacheItem cacheItem = cache.compute( key, - k -> new CacheItem( - mappingFunction.apply(k), - System.nanoTime() + this.timeToLiveNanos)); + (k, v) -> { + if (v == null) { + // The key is absent; compute and store the new value. + return new CacheItem( + mappingFunction.apply(k), + System.nanoTime() + this.timeToLiveNanos); + } + + if (v.shouldCleanup() && !this.isRenewableExpiration) { + // The existing value is expired and non-renewable. Mark it for disposal and store the new value. + toDisposeList.add(v.item); + return new CacheItem( + mappingFunction.apply(k), + System.nanoTime() + this.timeToLiveNanos); + } + + // The existing value is non-expired or renewable. Keep the existing value. + return v; + }); if (this.isRenewableExpiration) { - cacheItem.extendExpiration(this.timeToLiveNanos); - } else if (cacheItem.shouldCleanup()) { - return null; + cacheItem.extendExpiration(); + } + + if (this.itemDisposalFunc != null && !toDisposeList.isEmpty()) { + this.itemDisposalFunc.dispose(toDisposeList.get(0)); } return cacheItem.item; } + /** + * Store the given value at the given key. + * + * @param key the key at which the value should be stored + * @param value the value to be stored + * @return the previous value stored at the given key, or null if there was no previous value. If the previous value + * is expired it will be disposed and returned. + */ public @Nullable V put( final K key, final V value) { @@ -127,15 +153,20 @@ protected void initCleanupThread() { return null; } - // cacheItem is the previous value associated with the key. Since it has now been replaced with the new value, - // its expiration does not need to be extended. - if (cacheItem.shouldCleanup()) { - return null; + // cacheItem is the previous value associated with the key. + if (cacheItem.shouldCleanup() && this.itemDisposalFunc != null) { + this.itemDisposalFunc.dispose(cacheItem.item); } return cacheItem.item; } + /** + * Retrieves the value stored at the given key. + * + * @param key the key from which to retrieve the value + * @return the value stored at the given key, or null if there is no existing value + */ public @Nullable V get(final K key) { final CacheItem cacheItem = cache.get(key); if (cacheItem == null) { @@ -143,7 +174,7 @@ protected void initCleanupThread() { } if (this.isRenewableExpiration) { - cacheItem.extendExpiration(this.timeToLiveNanos); + cacheItem.extendExpiration(); } else if (cacheItem.shouldCleanup()) { return null; } @@ -157,10 +188,9 @@ public boolean exists(final K key) { } /** - * Cleanup expired entries if we have hit the cleanup time, then remove and dispose the value - * associated with the given key. + * Removes and disposes of the value stored at the given key. * - * @param key the key associated with the value to be removed/disposed + * @param key the key associated with the value to be removed and disposed * @return the value removed from the cache. If the value was expired, it will still be returned. */ public @Nullable V remove(final K key) { @@ -231,12 +261,17 @@ public Map getEntries() { /** * Get the current size of the cache, including expired entries. * - * @return the current size of the cache, including expired entries. + * @return the current size of the cache, including expired entries */ public int size() { return this.cache.size(); } + /** + * Get the class of the values stored in the cache. + * + * @return the class of the values stored in the cache + */ public Class getValueClass() { return this.valueClass; } @@ -273,11 +308,9 @@ boolean shouldCleanup() { } /** - * Renew a cache item's expiration time and return the value. - * - * @param timeToLiveNanos the new expiration duration for the item + * Renews a cache item's expiration time. */ - public void extendExpiration(final long timeToLiveNanos) { + public void extendExpiration() { this.expirationTimeNanos = System.nanoTime() + timeToLiveNanos; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 1e7ad7136..7e07ca94d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -137,7 +137,7 @@ public void clearAll() { return null; } - // TODO: fix this cast to be type safe, or remove this method after removing its callers + // TODO: fix this cast to be type safe, or remove this method after removing the suggestedClusterId logic return (Map) cache.getEntries(); } diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java index 851b413cc..58c3b01cc 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java @@ -227,21 +227,21 @@ void testQueryForTopology_queryResultsInException() throws SQLException { } @Test - void testGetCachedTopology_returnCachedTopology() throws SQLException { + void testGetCachedTopology_returnStoredTopology() throws SQLException { rdsHostListProvider = getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url"); final List expected = hosts; storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, expected); - final List result = rdsHostListProvider.getCachedTopology(); + final List result = rdsHostListProvider.getStoredTopology(); assertEquals(expected, result); } @Test - void testGetCachedTopology_returnNull() throws InterruptedException, SQLException { + void testGetStoredTopology_returnNull() throws InterruptedException, SQLException { rdsHostListProvider = getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url"); // Test getCachedTopology with empty topology. - assertNull(rdsHostListProvider.getCachedTopology()); + assertNull(rdsHostListProvider.getStoredTopology()); rdsHostListProvider.clear(); rdsHostListProvider = getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url"); @@ -249,7 +249,7 @@ void testGetCachedTopology_returnNull() throws InterruptedException, SQLExceptio TimeUnit.NANOSECONDS.sleep(1); // Test getCachedTopology with expired cache. - assertNull(rdsHostListProvider.getCachedTopology()); + assertNull(rdsHostListProvider.getStoredTopology()); } @Test @@ -280,7 +280,7 @@ void testTopologyCache_NoSuggestedClusterId() throws SQLException { getRdsHostListProvider(mockHostListProviderService, "jdbc:something://cluster-b.domain.com/")); provider2.init(); - assertNull(provider2.getCachedTopology()); + assertNull(provider2.getStoredTopology()); final List topologyClusterB = Arrays.asList( new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) @@ -660,14 +660,14 @@ void testClusterUrlUsedAsDefaultClusterId() throws SQLException { Collections.singletonList(new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("host").build()); doReturn(mockTopology).when(provider1).queryForTopology(any(Connection.class)); provider1.refresh(); - assertEquals(mockTopology, provider1.getCachedTopology()); + assertEquals(mockTopology, provider1.getStoredTopology()); verify(provider1, times(1)).queryForTopology(mockConnection); RdsHostListProvider provider2 = Mockito.spy(getRdsHostListProvider( mockHostListProviderService, connectionString)); assertEquals(expectedClusterId, provider2.getClusterId()); - assertEquals(mockTopology, provider2.getCachedTopology()); + assertEquals(mockTopology, provider2.getStoredTopology()); verify(provider2, never()).queryForTopology(mockConnection); } } diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java index 81e287565..b7a623bda 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java @@ -211,7 +211,7 @@ void testGetCachedTopology_returnCachedTopology() throws SQLException { storageService.set( rdsMazDbClusterHostListProvider.clusterId, expected, defaultRefreshRateNano); - final List result = rdsMazDbClusterHostListProvider.getCachedTopology(); + final List result = rdsMazDbClusterHostListProvider.getStoredTopology(); assertEquals(expected, result); } @@ -220,7 +220,7 @@ void testGetCachedTopology_returnNull() throws InterruptedException, SQLExceptio rdsMazDbClusterHostListProvider = getRdsMazDbClusterHostListProvider( mockHostListProviderService, "jdbc:someprotocol://url"); // Test getCachedTopology with empty topology. - assertNull(rdsMazDbClusterHostListProvider.getCachedTopology()); + assertNull(rdsMazDbClusterHostListProvider.getStoredTopology()); rdsMazDbClusterHostListProvider.clear(); rdsMazDbClusterHostListProvider = getRdsMazDbClusterHostListProvider( @@ -231,7 +231,7 @@ void testGetCachedTopology_returnNull() throws InterruptedException, SQLExceptio TimeUnit.NANOSECONDS.sleep(1); // Test getCachedTopology with expired cache. - assertNull(rdsMazDbClusterHostListProvider.getCachedTopology()); + assertNull(rdsMazDbClusterHostListProvider.getStoredTopology()); } @Test @@ -262,7 +262,7 @@ void testTopologyCache_NoSuggestedClusterId() throws SQLException { getRdsMazDbClusterHostListProvider(mockHostListProviderService, "jdbc:something://cluster-b.domain.com/")); provider2.init(); - assertNull(provider2.getCachedTopology()); + assertNull(provider2.getStoredTopology()); final List topologyClusterB = Arrays.asList( new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) From e0af969ad90cffdd4c7758d413dd9385314be7bc Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 14 Feb 2025 16:19:01 -0800 Subject: [PATCH 026/149] Fix build errors --- .../jdbc/benchmarks/PluginBenchmarks.java | 36 +++++++++++++------ .../testplugin/TestConnectionWrapper.java | 6 ++-- .../jdbc/dialect/AuroraMysqlDialect.java | 1 + .../amazon/jdbc/dialect/AuroraPgDialect.java | 1 + .../amazon/jdbc/dialect/MariaDbDialect.java | 2 +- .../amazon/jdbc/dialect/MysqlDialect.java | 2 +- .../amazon/jdbc/dialect/PgDialect.java | 2 +- .../RdsMultiAzDbClusterMysqlDialect.java | 4 ++- .../dialect/RdsMultiAzDbClusterPgDialect.java | 4 ++- .../amazon/jdbc/dialect/UnknownDialect.java | 2 +- .../RdsMultiAzDbClusterListProvider.java | 5 +-- .../MonitoringRdsHostListProvider.java | 4 ++- .../MonitoringRdsMultiAzHostListProvider.java | 14 ++++++-- .../jdbc/wrapper/ConnectionWrapper.java | 20 +++++++---- wrapper/src/test/build.gradle.kts | 2 +- .../aurora/TestAuroraHostListProvider.java | 5 +-- .../aurora/TestPluginServiceImpl.java | 6 ++-- .../amazon/jdbc/DialectDetectionTests.java | 4 +++ .../amazon/jdbc/PluginServiceImplTests.java | 26 ++++++++++++++ .../RdsMultiAzDbClusterListProviderTest.java | 1 + ...AwsSecretsManagerConnectionPluginTest.java | 11 ++++-- 21 files changed, 121 insertions(+), 37 deletions(-) diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java index 134977693..570cbd2da 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java @@ -62,6 +62,8 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.telemetry.GaugeCallable; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; @@ -85,6 +87,7 @@ public class PluginBenchmarks { "jdbc:aws-wrapper:postgresql://instance-0.XYZ.us-east-2.rds.amazonaws.com"; private static final String TEST_HOST = "instance-0"; private static final int TEST_PORT = 5432; + private static final StorageService storageService = new StorageServiceImpl(); private final HostSpec writerHostSpec = new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) .host(TEST_HOST).port(TEST_PORT).build(); @@ -164,7 +167,8 @@ public ConnectionWrapper initAndReleaseWithExecutionTimePlugin() throws SQLExcep mockTelemetryFactory, mockPluginService, mockHostListProviderService, - mockPluginManagerService)) { + mockPluginManagerService, + storageService)) { wrapper.releaseResources(); return wrapper; } @@ -179,7 +183,8 @@ public ConnectionWrapper initAndReleaseWithAuroraHostListPlugin() throws SQLExce mockTelemetryFactory, mockPluginService, mockHostListProviderService, - mockPluginManagerService)) { + mockPluginManagerService, + storageService)) { wrapper.releaseResources(); return wrapper; } @@ -194,7 +199,8 @@ public ConnectionWrapper initAndReleaseWithExecutionTimeAndAuroraHostListPlugins mockTelemetryFactory, mockPluginService, mockHostListProviderService, - mockPluginManagerService)) { + mockPluginManagerService, + storageService)) { wrapper.releaseResources(); return wrapper; } @@ -209,7 +215,8 @@ public ConnectionWrapper initAndReleaseWithReadWriteSplittingPlugin() throws SQL mockTelemetryFactory, mockPluginService, mockHostListProviderService, - mockPluginManagerService)) { + mockPluginManagerService, + storageService)) { wrapper.releaseResources(); return wrapper; } @@ -225,7 +232,8 @@ public ConnectionWrapper initAndReleaseWithAuroraHostListAndReadWriteSplittingPl mockTelemetryFactory, mockPluginService, mockHostListProviderService, - mockPluginManagerService)) { + mockPluginManagerService, + storageService)) { wrapper.releaseResources(); return wrapper; } @@ -243,7 +251,8 @@ public ConnectionWrapper initAndReleaseWithReadWriteSplittingPlugin_internalConn mockTelemetryFactory, mockPluginService, mockHostListProviderService, - mockPluginManagerService)) { + mockPluginManagerService, + storageService)) { wrapper.releaseResources(); ConnectionProviderManager.releaseResources(); Driver.resetCustomConnectionProvider(); @@ -264,7 +273,8 @@ public ConnectionWrapper initAndReleaseWithAuroraHostListAndReadWriteSplittingPl mockTelemetryFactory, mockPluginService, mockHostListProviderService, - mockPluginManagerService)) { + mockPluginManagerService, + storageService)) { wrapper.releaseResources(); ConnectionProviderManager.releaseResources(); Driver.resetCustomConnectionProvider(); @@ -281,7 +291,8 @@ public Statement executeStatementBaseline() throws SQLException { mockTelemetryFactory, mockPluginService, mockHostListProviderService, - mockPluginManagerService); + mockPluginManagerService, + storageService); Statement statement = wrapper.createStatement()) { return statement; } @@ -297,7 +308,8 @@ public ResultSet executeStatementWithExecutionTimePlugin() throws SQLException { mockTelemetryFactory, mockPluginService, mockHostListProviderService, - mockPluginManagerService); + mockPluginManagerService, + storageService); Statement statement = wrapper.createStatement(); ResultSet resultSet = statement.executeQuery("some sql")) { return resultSet; @@ -314,7 +326,8 @@ public ResultSet executeStatementWithTelemetryDisabled() throws SQLException { mockTelemetryFactory, mockPluginService, mockHostListProviderService, - mockPluginManagerService); + mockPluginManagerService, + storageService); Statement statement = wrapper.createStatement(); ResultSet resultSet = statement.executeQuery("some sql")) { return resultSet; @@ -331,7 +344,8 @@ public ResultSet executeStatementWithTelemetry() throws SQLException { mockTelemetryFactory, mockPluginService, mockHostListProviderService, - mockPluginManagerService); + mockPluginManagerService, + storageService); Statement statement = wrapper.createStatement(); ResultSet resultSet = statement.executeQuery("some sql")) { return resultSet; diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java index 66cfbd28f..aa0a09594 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java @@ -23,6 +23,7 @@ import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.wrapper.ConnectionWrapper; @@ -35,9 +36,10 @@ public TestConnectionWrapper(@NonNull Properties props, @NonNull final TelemetryFactory telemetryFactory, @NonNull PluginService pluginService, @NonNull HostListProviderService hostListProviderService, - @NonNull PluginManagerService pluginManagerService) + @NonNull PluginManagerService pluginManagerService, + @NonNull StorageService storageService) throws SQLException { super(props, url, connectionPluginManager, telemetryFactory, pluginService, hostListProviderService, - pluginManagerService); + pluginManagerService, storageService); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java index 694d7fcb3..530c94b3a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java @@ -90,6 +90,7 @@ public HostListProviderSupplier getHostListProvider() { properties, initialUrl, hostListProviderService, + storageService, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java index 6cefc0e09..328efd9d5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java @@ -136,6 +136,7 @@ public HostListProviderSupplier getHostListProvider() { properties, initialUrl, hostListProviderService, + storageService, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java index 9c27ff08f..2f18408f5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java @@ -104,7 +104,7 @@ public List getDialectUpdateCandidates() { } public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService) -> + return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> new ConnectionStringHostListProvider(properties, initialUrl, hostListProviderService); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java index b03fb86b4..9b21127ad 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java @@ -108,7 +108,7 @@ public List getDialectUpdateCandidates() { } public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService) -> + return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> new ConnectionStringHostListProvider(properties, initialUrl, hostListProviderService); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java index b3b89af41..8d3b40676 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java @@ -106,7 +106,7 @@ public List getDialectUpdateCandidates() { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService) -> + return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> new ConnectionStringHostListProvider(properties, initialUrl, hostListProviderService); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java index 91109c758..dba0b805e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java @@ -96,7 +96,7 @@ public boolean isDialect(final Connection connection) { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService) -> { + return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> { final FailoverConnectionPlugin failover2Plugin = pluginService.getPlugin(FailoverConnectionPlugin.class); @@ -105,6 +105,7 @@ public HostListProviderSupplier getHostListProvider() { properties, initialUrl, hostListProviderService, + storageService, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, @@ -117,6 +118,7 @@ public HostListProviderSupplier getHostListProvider() { properties, initialUrl, hostListProviderService, + storageService, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java index df4af9c10..50490529f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java @@ -112,7 +112,7 @@ public boolean isDialect(final Connection connection) { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService) -> { + return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> { final FailoverConnectionPlugin failover2Plugin = pluginService.getPlugin(FailoverConnectionPlugin.class); @@ -121,6 +121,7 @@ public HostListProviderSupplier getHostListProvider() { properties, initialUrl, hostListProviderService, + storageService, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, @@ -134,6 +135,7 @@ public HostListProviderSupplier getHostListProvider() { properties, initialUrl, hostListProviderService, + storageService, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java index cb5482936..85b8a9244 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java @@ -81,7 +81,7 @@ public List getDialectUpdateCandidates() { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService) -> + return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> new ConnectionStringHostListProvider(properties, initialUrl, hostListProviderService); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java index 60dca8bcf..89312811d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java @@ -24,17 +24,16 @@ import java.sql.Timestamp; import java.time.Instant; import java.util.ArrayList; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Properties; import java.util.logging.Logger; -import java.util.stream.Collectors; import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.HostRole; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.storage.StorageService; public class RdsMultiAzDbClusterListProvider extends RdsHostListProvider { private final String fetchWriterNodeQuery; @@ -45,6 +44,7 @@ public RdsMultiAzDbClusterListProvider( final Properties properties, final String originalUrl, final HostListProviderService hostListProviderService, + final StorageService storageService, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery, @@ -54,6 +54,7 @@ public RdsMultiAzDbClusterListProvider( super(properties, originalUrl, hostListProviderService, + storageService, topologyQuery, nodeIdQuery, isReaderQuery); diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index beacd7417..077faf447 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -33,6 +33,7 @@ import software.amazon.jdbc.hostlistprovider.RdsHostListProvider; import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.storage.ItemCategory; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.Topology; public class MonitoringRdsHostListProvider extends RdsHostListProvider @@ -73,12 +74,13 @@ public MonitoringRdsHostListProvider( final Properties properties, final String originalUrl, final HostListProviderService hostListProviderService, + final StorageService storageService, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery, final String writerTopologyQuery, final PluginService pluginService) { - super(properties, originalUrl, hostListProviderService, topologyQuery, nodeIdQuery, isReaderQuery); + super(properties, originalUrl, hostListProviderService, storageService, topologyQuery, nodeIdQuery, isReaderQuery); this.pluginService = pluginService; this.writerTopologyQuery = writerTopologyQuery; this.highRefreshRateNano = TimeUnit.MILLISECONDS.toNanos( diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java index a70b23334..7c4fd1c3d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java @@ -20,6 +20,7 @@ import java.util.logging.Logger; import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.storage.StorageService; public class MonitoringRdsMultiAzHostListProvider extends MonitoringRdsHostListProvider { @@ -32,14 +33,23 @@ public MonitoringRdsMultiAzHostListProvider( final Properties properties, final String originalUrl, final HostListProviderService hostListProviderService, + final StorageService storageService, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery, final PluginService pluginService, final String fetchWriterNodeQuery, final String fetchWriterNodeColumnName) { - super(properties, originalUrl, hostListProviderService, topologyQuery, nodeIdQuery, isReaderQuery, - "", pluginService); + super( + properties, + originalUrl, + hostListProviderService, + storageService, + topologyQuery, + nodeIdQuery, + isReaderQuery, + "", + pluginService); this.fetchWriterNodeQuery = fetchWriterNodeQuery; this.fetchWriterNodeColumnName = fetchWriterNodeColumnName; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index 7ab717dfd..26f18d109 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -111,7 +111,7 @@ public ConnectionWrapper( storageService, this.configurationProfile); - init(props, pluginManager, telemetryFactory, pluginService, pluginService, pluginService); + init(props, pluginManager, telemetryFactory, pluginService, pluginService, pluginService, storageService); if (PropertyDefinition.LOG_UNCLOSED_CONNECTIONS.getBoolean(props)) { this.openConnectionStacktrace = new Throwable(Messages.get("ConnectionWrapper.unclosedConnectionInstantiated")); @@ -126,15 +126,22 @@ protected ConnectionWrapper( @NonNull final TelemetryFactory telemetryFactory, @NonNull final PluginService pluginService, @NonNull final HostListProviderService hostListProviderService, - @NonNull final PluginManagerService pluginManagerService) + @NonNull final PluginManagerService pluginManagerService, + @NonNull final StorageService storageService) throws SQLException { if (StringUtils.isNullOrEmpty(url)) { throw new IllegalArgumentException("url"); } - init(props, - connectionPluginManager, telemetryFactory, pluginService, hostListProviderService, pluginManagerService); + init( + props, + connectionPluginManager, + telemetryFactory, + pluginService, + hostListProviderService, + pluginManagerService, + storageService); } protected void init( @@ -143,7 +150,8 @@ protected void init( final TelemetryFactory telemetryFactory, final PluginService pluginService, final HostListProviderService hostListProviderService, - final PluginManagerService pluginManagerService) throws SQLException { + final PluginManagerService pluginManagerService, + final StorageService storageService) throws SQLException { this.pluginManager = connectionPluginManager; this.telemetryFactory = telemetryFactory; this.pluginService = pluginService; @@ -156,7 +164,7 @@ protected void init( final HostListProviderSupplier supplier = this.pluginService.getDialect().getHostListProvider(); if (supplier != null) { final HostListProvider provider = supplier.getProvider( - props, this.originalUrl, this.hostListProviderService, this.pluginService); + props, this.originalUrl, this.hostListProviderService, this.pluginService, storageService); hostListProviderService.setHostListProvider(provider); } diff --git a/wrapper/src/test/build.gradle.kts b/wrapper/src/test/build.gradle.kts index 89e63ddbf..50d20d643 100644 --- a/wrapper/src/test/build.gradle.kts +++ b/wrapper/src/test/build.gradle.kts @@ -100,5 +100,5 @@ tasks.register("in-container") { // modify below filter to select specific integration tests // see https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/testing/TestFilter.html - filter.includeTestsMatching("integration.container.tests.*") + filter.includeTestsMatching("*.test_writerFailover_failOnConnectionInvocation") } diff --git a/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java b/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java index b67028810..d8b3d27ce 100644 --- a/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java +++ b/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java @@ -18,17 +18,18 @@ import java.util.Properties; import software.amazon.jdbc.HostListProviderService; -import software.amazon.jdbc.PluginService; import software.amazon.jdbc.hostlistprovider.AuroraHostListProvider; +import software.amazon.jdbc.util.storage.StorageService; public class TestAuroraHostListProvider extends AuroraHostListProvider { public TestAuroraHostListProvider( HostListProviderService hostListProviderService, + StorageService storageService, Properties properties, String originalUrl) { - super(properties, originalUrl, hostListProviderService, "", "", ""); + super(properties, originalUrl, hostListProviderService, storageService, "", "", ""); } public static void clearCache() { diff --git a/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java b/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java index 4a54ee984..2e7d081f0 100644 --- a/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java +++ b/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java @@ -22,6 +22,7 @@ import software.amazon.jdbc.ConnectionPluginManager; import software.amazon.jdbc.PluginServiceImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.storage.StorageService; public class TestPluginServiceImpl extends PluginServiceImpl { @@ -30,10 +31,11 @@ public TestPluginServiceImpl( @NonNull Properties props, @NonNull String originalUrl, String targetDriverProtocol, - @NonNull final TargetDriverDialect targetDriverDialect) + @NonNull final TargetDriverDialect targetDriverDialect, + @NonNull final StorageService storageService) throws SQLException { - super(pluginManager, props, originalUrl, targetDriverProtocol, targetDriverDialect); + super(pluginManager, props, originalUrl, targetDriverProtocol, targetDriverDialect, storageService); } public static void clearHostAvailabilityCache() { diff --git a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java index 3dd8b5a9b..b21a6b149 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java @@ -51,6 +51,8 @@ import software.amazon.jdbc.dialect.RdsPgDialect; import software.amazon.jdbc.exceptions.ExceptionManager; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.storage.StorageServiceImpl; public class DialectDetectionTests { private static final String LOCALHOST = "localhost"; @@ -69,6 +71,7 @@ public class DialectDetectionTests { @Mock private TargetDriverDialect mockTargetDriverDialect; @Mock private ResultSetMetaData mockResultSetMetaData; private final DialectManager dialectManager = new DialectManager(null); + private final StorageService storageService = new StorageServiceImpl(); private final Properties props = new Properties(); private AutoCloseable closeable; @@ -97,6 +100,7 @@ PluginServiceImpl getPluginService(String host, String protocol) throws SQLExcep protocol, null, mockTargetDriverDialect, + storageService, null, null)); } diff --git a/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java b/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java index d9af2a07f..95b6f7500 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java @@ -66,12 +66,15 @@ import software.amazon.jdbc.profile.ConfigurationProfileBuilder; import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.storage.StorageServiceImpl; public class PluginServiceImplTests { private static final Properties PROPERTIES = new Properties(); private static final String URL = "url"; private static final String DRIVER_PROTOCOL = "driverProtocol"; + private static final StorageService storageService = new StorageServiceImpl(); private AutoCloseable closeable; @Mock ConnectionPluginManager pluginManager; @@ -118,6 +121,7 @@ public void testOldConnectionNoSuggestion() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -147,6 +151,7 @@ public void testOldConnectionDisposeSuggestion() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -176,6 +181,7 @@ public void testOldConnectionPreserveSuggestion() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -209,6 +215,7 @@ public void testOldConnectionMixedSuggestion() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -239,6 +246,7 @@ public void testChangesNewConnectionNewHostNewPortNewRoleNewAvailability() throw DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -278,6 +286,7 @@ public void testChangesNewConnectionNewRoleNewAvailability() throws SQLException DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -317,6 +326,7 @@ public void testChangesNewConnection() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -356,6 +366,7 @@ public void testChangesNoChanges() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -387,6 +398,7 @@ public void testSetNodeListAdded() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.allHosts = new ArrayList<>(); @@ -421,6 +433,7 @@ public void testSetNodeListDeleted() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.allHosts = Arrays.asList( @@ -458,6 +471,7 @@ public void testSetNodeListChanged() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.allHosts = Collections.singletonList(new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) @@ -495,6 +509,7 @@ public void testSetNodeListNoChanges() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.allHosts = Collections.singletonList(new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) @@ -521,6 +536,7 @@ public void testNodeAvailabilityNotChanged() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.allHosts = Collections.singletonList( @@ -550,6 +566,7 @@ public void testNodeAvailabilityChanged_WentDown() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.allHosts = Collections.singletonList( @@ -586,6 +603,7 @@ public void testNodeAvailabilityChanged_WentUp() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.allHosts = Collections.singletonList( @@ -633,6 +651,7 @@ public void testNodeAvailabilityChanged_WentUp_ByAlias() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); @@ -678,6 +697,7 @@ public void testNodeAvailabilityChanged_WentUp_MultipleHostsByAlias() throws SQL DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); @@ -756,6 +776,7 @@ void testRefreshHostList_withCachedHostAvailability() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); when(target.getHostListProvider()).thenReturn(hostListProvider); @@ -813,6 +834,7 @@ void testForceRefreshHostList_withCachedHostAvailability() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); when(target.getHostListProvider()).thenReturn(hostListProvider); @@ -838,6 +860,7 @@ void testIdentifyConnectionWithNoAliases() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); when(target.getHostListProvider()).thenReturn(hostListProvider); @@ -859,6 +882,7 @@ void testIdentifyConnectionWithAliases() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.hostListProvider = hostListProvider; @@ -887,6 +911,7 @@ void testFillAliasesNonEmptyAliases() throws SQLException { DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); @@ -909,6 +934,7 @@ void testFillAliasesWithInstanceEndpoint(Dialect dialect, String[] expectedInsta DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, + storageService, configurationProfile, sessionStateService)); target.hostListProvider = hostListProvider; diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java index b7a623bda..1cacc65b3 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java @@ -111,6 +111,7 @@ private RdsMultiAzDbClusterListProvider getRdsMazDbClusterHostListProvider( new Properties(), originalUrl, mockHostListProviderService, + storageService, "foo", "bar", "baz", diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java index 647a41f37..d05b2a7c0 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java @@ -71,6 +71,8 @@ import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Pair; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.telemetry.GaugeCallable; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; @@ -102,6 +104,7 @@ public class AwsSecretsManagerConnectionPluginTest { private static final GetSecretValueResponse INVALID_GET_SECRET_VALUE_RESPONSE = GetSecretValueResponse.builder().secretString(INVALID_SECRET_STRING).build(); private static final Properties TEST_PROPS = new Properties(); + private static final StorageService storageService = new StorageServiceImpl(); private AwsSecretsManagerConnectionPlugin plugin; private AutoCloseable closeable; @@ -249,6 +252,7 @@ public void testConnectWithNewSecretsAfterTryingWithCachedSecrets( protocol, mockDialectManager, mockTargetDriverDialect, + storageService, configurationProfile, mockSessionStateService), TEST_PROPS, @@ -349,6 +353,7 @@ public void testFailedInitialConnectionWithWrappedGenericError(final String acce TEST_PG_PROTOCOL, mockDialectManager, mockTargetDriverDialect, + storageService, configurationProfile, mockSessionStateService), TEST_PROPS, @@ -391,6 +396,7 @@ public void testConnectWithWrappedMySQLException() throws SQLException { TEST_MYSQL_PROTOCOL, mockDialectManager, mockTargetDriverDialect, + storageService, configurationProfile, mockSessionStateService), TEST_PROPS, @@ -432,6 +438,7 @@ public void testConnectWithWrappedPostgreSQLException() throws SQLException { TEST_PG_PROTOCOL, mockDialectManager, mockTargetDriverDialect, + storageService, configurationProfile, mockSessionStateService), TEST_PROPS, @@ -471,7 +478,7 @@ public void testConnectViaARN(final String arn, final Region expectedRegionParse SECRET_ID_PROPERTY.set(props, arn); this.plugin = spy(new AwsSecretsManagerConnectionPlugin( - new PluginServiceImpl(mockConnectionPluginManager, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect), + new PluginServiceImpl(mockConnectionPluginManager, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect, storageService), props, (host, r) -> mockSecretsManagerClient, (id) -> mockGetValueRequest)); @@ -491,7 +498,7 @@ public void testConnectionWithRegionParameterAndARN(final String arn, final Regi REGION_PROPERTY.set(props, expectedRegion.toString()); this.plugin = spy(new AwsSecretsManagerConnectionPlugin( - new PluginServiceImpl(mockConnectionPluginManager, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect), + new PluginServiceImpl(mockConnectionPluginManager, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect, storageService), props, (host, r) -> mockSecretsManagerClient, (id) -> mockGetValueRequest)); From 472c7931cecd9514a5effdcfc86f11f18a08d1a6 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 18 Feb 2025 09:47:23 -0800 Subject: [PATCH 027/149] cleanup --- .../software/amazon/jdbc/util/storage/ExpirationCache.java | 3 ++- wrapper/src/test/build.gradle.kts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index 864534895..78615b53f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -191,7 +191,8 @@ public boolean exists(final K key) { * Removes and disposes of the value stored at the given key. * * @param key the key associated with the value to be removed and disposed - * @return the value removed from the cache. If the value was expired, it will still be returned. + * @return the value removed from the cache, or null if the key does not exist in the cache. If the value was expired, + * it will still be returned. */ public @Nullable V remove(final K key) { return removeAndDispose(key); diff --git a/wrapper/src/test/build.gradle.kts b/wrapper/src/test/build.gradle.kts index 50d20d643..89e63ddbf 100644 --- a/wrapper/src/test/build.gradle.kts +++ b/wrapper/src/test/build.gradle.kts @@ -100,5 +100,5 @@ tasks.register("in-container") { // modify below filter to select specific integration tests // see https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/testing/TestFilter.html - filter.includeTestsMatching("*.test_writerFailover_failOnConnectionInvocation") + filter.includeTestsMatching("integration.container.tests.*") } From a51649227f69e4995a56d9d1518dc7bef4d5434b Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 18 Feb 2025 10:11:52 -0800 Subject: [PATCH 028/149] cleanup --- .../jdbc/util/storage/StorageServiceImpl.java | 16 ++++++++-------- ...aws_advanced_jdbc_wrapper_messages.properties | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 7e07ca94d..9ba810400 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -53,23 +53,23 @@ public void registerItemCategoryIfAbsent( } @Override + @SuppressWarnings("unchecked") public void set(String itemCategory, Object key, V value) { final ExpirationCache cache = caches.get(itemCategory); if (cache == null) { - // TODO: what should we do if the item category isn't registered? - return; + throw new IllegalStateException( + Messages.get("StorageServiceImpl.itemCategoryNotRegistered", new Object[] { itemCategory })); } - Class expectedType = cache.getValueClass(); - if (!expectedType.isInstance(value)) { + try { + ExpirationCache typedCache = (ExpirationCache) cache; + typedCache.put(key, value); + } catch (ClassCastException e) { throw new IllegalArgumentException( Messages.get( "StorageServiceImpl.incorrectValueType", - new Object[]{itemCategory, expectedType, value.getClass(), value})); + new Object[]{itemCategory, cache.getValueClass(), value.getClass(), value})); } - - ExpirationCache typedCache = (ExpirationCache) cache; - typedCache.put(key, value); } @Override diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 5c8451b0c..9bfa8f5c4 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -353,6 +353,7 @@ SamlAuthPlugin.javaStsSdkNotInClasspath=Required dependency 'AWS Java SDK for AW SamlAuthPlugin.unhandledException=Unhandled exception: ''{0}'' StorageServiceImpl.incorrectValueType=Attempted to store an incorrect item type under item category {0}. Items under this category should have type {1} but the passed in item had a type of {2}. Item value: {3}. +StorageServiceImpl.itemCategoryNotRegistered=The given item category {0} is not registered. Please register the item category before storing items under the category. StorageServiceImpl.itemClassMismatch=The item stored at {0} under the category {1} did not have the expected type. The expected type was {2}, but the stored item had a type of {3}. Returning null. # Wrapper Utils From 2107add2062ed1450f3aeafd911780976c3d1b32 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 18 Feb 2025 14:30:57 -0800 Subject: [PATCH 029/149] PR comments --- .../amazon/jdbc/hostlistprovider/RdsHostListProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index 8dfb68470..c57385903 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -247,7 +247,7 @@ public FetchTopologyResult getTopology(final Connection conn, final boolean forc this.clusterIdChanged(oldClusterId); } - final List storedHosts = getStoredTopology(); + final List storedHosts = this.getStoredTopology(); // This clusterId is a primary one and is about to create a new entry in the cache. // When a primary entry is created it needs to be suggested for other (non-primary) entries. From 55e3eec6435a548cf1cbb21742aa7aaeec50cc5e Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 18 Feb 2025 16:33:32 -0800 Subject: [PATCH 030/149] Cleanup, gradle check passing --- .../java/software/amazon/jdbc/Driver.java | 1 + .../amazon/jdbc/ds/AwsWrapperDataSource.java | 5 +++ .../jdbc/util/storage/StorageServiceImpl.java | 7 ++++ .../jdbc/wrapper/ConnectionWrapper.java | 3 +- ..._advanced_jdbc_wrapper_messages.properties | 6 +-- .../RdsHostListProviderTest.java | 34 ++++++++--------- .../RdsMultiAzDbClusterListProviderTest.java | 38 ++++++++----------- ...AwsSecretsManagerConnectionPluginTest.java | 6 ++- .../dev/DeveloperConnectionPluginTest.java | 13 +++++++ 9 files changed, 64 insertions(+), 49 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index 81d7f88a7..0d846e767 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -239,6 +239,7 @@ public Connection connect(final String url, final Properties info) throws SQLExc effectiveConnectionProvider, targetDriverDialect, configurationProfile, + storageService, telemetryFactory); } catch (Exception ex) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index 82739c115..1946ee0e3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -52,6 +52,8 @@ import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -67,6 +69,8 @@ public class AwsWrapperDataSource implements DataSource, Referenceable, Serializ private static final String SERVER_NAME = "serverName"; private static final String SERVER_PORT = "serverPort"; + private static final StorageService storageService = new StorageServiceImpl(); + static { try { if (!Driver.isRegistered()) { @@ -259,6 +263,7 @@ ConnectionWrapper createConnectionWrapper( effectiveProvider, targetDriverDialect, configurationProfile, + storageService, telemetryFactory); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 9ba810400..f8a66e032 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -61,6 +61,13 @@ public void set(String itemCategory, Object key, V value) { Messages.get("StorageServiceImpl.itemCategoryNotRegistered", new Object[] { itemCategory })); } + if (!cache.getValueClass().isInstance(value)) { + throw new IllegalArgumentException( + Messages.get( + "StorageServiceImpl.incorrectValueType", + new Object[] {itemCategory, cache.getValueClass(), value.getClass(), value})); + } + try { ExpirationCache typedCache = (ExpirationCache) cache; typedCache.put(key, value); diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index 26f18d109..753804405 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -56,7 +56,6 @@ import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class ConnectionWrapper implements Connection, CanReleaseResources { @@ -84,6 +83,7 @@ public ConnectionWrapper( @Nullable final ConnectionProvider effectiveConnectionProvider, @NonNull final TargetDriverDialect targetDriverDialect, @Nullable final ConfigurationProfile configurationProfile, + @NonNull final StorageService storageService, @NonNull final TelemetryFactory telemetryFactory) throws SQLException { @@ -101,7 +101,6 @@ public ConnectionWrapper( effectiveConnectionProvider, this, telemetryFactory); - final StorageService storageService = new StorageServiceImpl(); final PluginServiceImpl pluginService = new PluginServiceImpl( pluginManager, props, diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 9bfa8f5c4..8790b7f50 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -352,9 +352,9 @@ SAMLCredentialsProviderFactory.getSamlAssertionFailed=Failed to get SAML Asserti SamlAuthPlugin.javaStsSdkNotInClasspath=Required dependency 'AWS Java SDK for AWS Secret Token Service' is not on the classpath. SamlAuthPlugin.unhandledException=Unhandled exception: ''{0}'' -StorageServiceImpl.incorrectValueType=Attempted to store an incorrect item type under item category {0}. Items under this category should have type {1} but the passed in item had a type of {2}. Item value: {3}. -StorageServiceImpl.itemCategoryNotRegistered=The given item category {0} is not registered. Please register the item category before storing items under the category. -StorageServiceImpl.itemClassMismatch=The item stored at {0} under the category {1} did not have the expected type. The expected type was {2}, but the stored item had a type of {3}. Returning null. +StorageServiceImpl.incorrectValueType=Attempted to store an incorrect item type under item category ''{0}''. Items under this category should have type ''{1}'' but the passed in item had a type of ''{2}''. Item value: ''{3}''. +StorageServiceImpl.itemCategoryNotRegistered=The given item category ''{0}'' is not registered. Please register the item category before storing items under the category. +StorageServiceImpl.itemClassMismatch=The item stored at ''{0}'' under the category ''{1}'' did not have the expected type. The expected type was ''{2}'', but the stored item had a type of ''{3}''. Returning null. # Wrapper Utils WrapperUtils.noWrapperClassExists=No wrapper class exists for ''{0}''. diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java index 58c3b01cc..24ff2a4df 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java @@ -68,6 +68,7 @@ import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; +import software.amazon.jdbc.util.storage.Topology; class RdsHostListProviderTest { private final StorageService storageService = new StorageServiceImpl(); @@ -100,11 +101,21 @@ void setUp() throws SQLException { when(mockHostListProviderService.getHostSpecBuilder()) .thenReturn(new HostSpecBuilder(new SimpleHostAvailabilityStrategy())); when(mockHostListProviderService.getCurrentConnection()).thenReturn(mockConnection); + + storageService.registerItemCategoryIfAbsent( + ItemCategory.TOPOLOGY, + Topology.class, + false, + TimeUnit.MINUTES.toNanos(10), + TimeUnit.MINUTES.toNanos(5), + null, + null); } @AfterEach void tearDown() throws Exception { RdsHostListProvider.clearAll(); + storageService.clearAll(); closeable.close(); } @@ -127,7 +138,7 @@ void testGetTopology_returnCachedTopology() throws SQLException { getRdsHostListProvider(mockHostListProviderService, "protocol://url/")); final List expected = hosts; - storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, expected); + storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, new Topology(expected)); final FetchTopologyResult result = rdsHostListProvider.getTopology(mockConnection, false); assertEquals(expected, result.hosts); @@ -141,7 +152,7 @@ void testGetTopology_withForceUpdate_returnsUpdatedTopology() throws SQLExceptio getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url")); rdsHostListProvider.isInitialized = true; - storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, hosts); + storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, new Topology(hosts)); final List newHosts = Collections.singletonList( new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("newHost").build()); @@ -161,7 +172,7 @@ void testGetTopology_noForceUpdate_queryReturnsEmptyHostList() throws SQLExcepti rdsHostListProvider.isInitialized = true; final List expected = hosts; - storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, expected); + storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, new Topology(expected)); doReturn(new ArrayList<>()).when(rdsHostListProvider).queryForTopology(mockConnection); @@ -231,27 +242,12 @@ void testGetCachedTopology_returnStoredTopology() throws SQLException { rdsHostListProvider = getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url"); final List expected = hosts; - storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, expected); + storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, new Topology(expected)); final List result = rdsHostListProvider.getStoredTopology(); assertEquals(expected, result); } - @Test - void testGetStoredTopology_returnNull() throws InterruptedException, SQLException { - rdsHostListProvider = getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url"); - // Test getCachedTopology with empty topology. - assertNull(rdsHostListProvider.getStoredTopology()); - rdsHostListProvider.clear(); - - rdsHostListProvider = getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url"); - storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, hosts); - TimeUnit.NANOSECONDS.sleep(1); - - // Test getCachedTopology with expired cache. - assertNull(rdsHostListProvider.getStoredTopology()); - } - @Test void testTopologyCache_NoSuggestedClusterId() throws SQLException { RdsHostListProvider.clearAll(); diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java index 1cacc65b3..70d1fcca3 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java @@ -63,6 +63,7 @@ import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; +import software.amazon.jdbc.util.storage.Topology; class RdsMultiAzDbClusterListProviderTest { @@ -96,11 +97,21 @@ void setUp() throws SQLException { when(mockHostListProviderService.getDialect()).thenReturn(mockTopologyAwareDialect); when(mockHostListProviderService.getHostSpecBuilder()) .thenReturn(new HostSpecBuilder(new SimpleHostAvailabilityStrategy())); + + storageService.registerItemCategoryIfAbsent( + ItemCategory.TOPOLOGY, + Topology.class, + false, + TimeUnit.MINUTES.toNanos(10), + TimeUnit.MINUTES.toNanos(5), + null, + null); } @AfterEach void tearDown() throws Exception { RdsMultiAzDbClusterListProvider.clearAll(); + storageService.clearAll(); closeable.close(); } @@ -130,7 +141,7 @@ void testGetTopology_returnCachedTopology() throws SQLException { final Instant lastUpdated = Instant.now(); final List expected = hosts; storageService.set( - rdsMazDbClusterHostListProvider.clusterId, expected, defaultRefreshRateNano); + ItemCategory.TOPOLOGY, rdsMazDbClusterHostListProvider.clusterId, new Topology(expected)); final FetchTopologyResult result = rdsMazDbClusterHostListProvider.getTopology(mockConnection, false); assertEquals(expected, result.hosts); @@ -145,7 +156,7 @@ void testGetTopology_withForceUpdate_returnsUpdatedTopology() throws SQLExceptio rdsMazDbClusterHostListProvider.isInitialized = true; storageService.set( - rdsMazDbClusterHostListProvider.clusterId, hosts, defaultRefreshRateNano); + ItemCategory.TOPOLOGY, rdsMazDbClusterHostListProvider.clusterId, new Topology(hosts)); final List newHosts = Collections.singletonList( new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("newHost").build()); @@ -166,7 +177,7 @@ void testGetTopology_noForceUpdate_queryReturnsEmptyHostList() throws SQLExcepti final List expected = hosts; storageService.set( - rdsMazDbClusterHostListProvider.clusterId, expected, defaultRefreshRateNano); + ItemCategory.TOPOLOGY, rdsMazDbClusterHostListProvider.clusterId, new Topology(expected)); doReturn(new ArrayList<>()).when(rdsMazDbClusterHostListProvider).queryForTopology(mockConnection); @@ -210,31 +221,12 @@ void testGetCachedTopology_returnCachedTopology() throws SQLException { final List expected = hosts; storageService.set( - rdsMazDbClusterHostListProvider.clusterId, expected, defaultRefreshRateNano); + ItemCategory.TOPOLOGY, rdsMazDbClusterHostListProvider.clusterId, new Topology(expected)); final List result = rdsMazDbClusterHostListProvider.getStoredTopology(); assertEquals(expected, result); } - @Test - void testGetCachedTopology_returnNull() throws InterruptedException, SQLException { - rdsMazDbClusterHostListProvider = getRdsMazDbClusterHostListProvider( - mockHostListProviderService, "jdbc:someprotocol://url"); - // Test getCachedTopology with empty topology. - assertNull(rdsMazDbClusterHostListProvider.getStoredTopology()); - rdsMazDbClusterHostListProvider.clear(); - - rdsMazDbClusterHostListProvider = getRdsMazDbClusterHostListProvider( - mockHostListProviderService, "jdbc:someprotocol://url"); - final long refreshRateOneNanosecond = 1; - storageService.set( - rdsMazDbClusterHostListProvider.clusterId, hosts, refreshRateOneNanosecond); - TimeUnit.NANOSECONDS.sleep(1); - - // Test getCachedTopology with expired cache. - assertNull(rdsMazDbClusterHostListProvider.getStoredTopology()); - } - @Test void testTopologyCache_NoSuggestedClusterId() throws SQLException { RdsMultiAzDbClusterListProvider.clearAll(); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java index d05b2a7c0..ebcdf595d 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java @@ -478,7 +478,8 @@ public void testConnectViaARN(final String arn, final Region expectedRegionParse SECRET_ID_PROPERTY.set(props, arn); this.plugin = spy(new AwsSecretsManagerConnectionPlugin( - new PluginServiceImpl(mockConnectionPluginManager, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect, storageService), + new PluginServiceImpl( + mockConnectionPluginManager, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect, storageService), props, (host, r) -> mockSecretsManagerClient, (id) -> mockGetValueRequest)); @@ -498,7 +499,8 @@ public void testConnectionWithRegionParameterAndARN(final String arn, final Regi REGION_PROPERTY.set(props, expectedRegion.toString()); this.plugin = spy(new AwsSecretsManagerConnectionPlugin( - new PluginServiceImpl(mockConnectionPluginManager, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect, storageService), + new PluginServiceImpl( + mockConnectionPluginManager, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect, storageService), props, (host, r) -> mockSecretsManagerClient, (id) -> mockGetValueRequest)); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java index a7bdfb54a..d141fb613 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java @@ -43,6 +43,7 @@ import software.amazon.jdbc.dialect.DialectCodes; import software.amazon.jdbc.dialect.DialectManager; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.wrapper.ConnectionWrapper; @@ -57,6 +58,7 @@ public class DeveloperConnectionPluginTest { @Mock private TelemetryFactory mockTelemetryFactory; @Mock TelemetryContext mockTelemetryContext; @Mock TargetDriverDialect mockTargetDriverDialect; + @Mock StorageService mockStorageService; private AutoCloseable closeable; @@ -91,6 +93,7 @@ public void test_RaiseException() throws SQLException { null, mockTargetDriverDialect, null, + mockStorageService, mockTelemetryFactory)) { ExceptionSimulator simulator = wrapper.unwrap(ExceptionSimulator.class); @@ -120,6 +123,7 @@ public void test_RaiseExceptionForMethodName() throws SQLException { null, mockTargetDriverDialect, null, + mockStorageService, mockTelemetryFactory)) { ExceptionSimulator simulator = wrapper.unwrap(ExceptionSimulator.class); @@ -149,6 +153,7 @@ public void test_RaiseExceptionForAnyMethodName() throws SQLException { null, mockTargetDriverDialect, null, + mockStorageService, mockTelemetryFactory)) { ExceptionSimulator simulator = wrapper.unwrap(ExceptionSimulator.class); @@ -178,6 +183,7 @@ public void test_RaiseExceptionForWrongMethodName() throws SQLException { null, mockTargetDriverDialect, null, + mockStorageService, mockTelemetryFactory)) { ExceptionSimulator simulator = wrapper.unwrap(ExceptionSimulator.class); @@ -209,6 +215,7 @@ public void test_RaiseExpectedExceptionClass() throws SQLException { null, mockTargetDriverDialect, null, + mockStorageService, mockTelemetryFactory)) { ExceptionSimulator simulator = wrapper.unwrap(ExceptionSimulator.class); @@ -238,6 +245,7 @@ public void test_RaiseUnexpectedExceptionClass() throws SQLException { null, mockTargetDriverDialect, null, + mockStorageService, mockTelemetryFactory)) { ExceptionSimulator simulator = wrapper.unwrap(ExceptionSimulator.class); @@ -276,6 +284,7 @@ public void test_RaiseExceptionOnConnect() { null, mockTargetDriverDialect, null, + mockStorageService, mockTelemetryFactory)); assertSame(exception, thrownException); @@ -286,6 +295,7 @@ public void test_RaiseExceptionOnConnect() { null, mockTargetDriverDialect, null, + mockStorageService, mockTelemetryFactory)); } @@ -305,6 +315,7 @@ public void test_NoExceptionOnConnectWithCallback() { null, mockTargetDriverDialect, null, + mockStorageService, mockTelemetryFactory)); } @@ -329,6 +340,7 @@ public void test_RaiseExceptionOnConnectWithCallback() { null, mockTargetDriverDialect, null, + mockStorageService, mockTelemetryFactory)); assertSame(exception, thrownException); @@ -339,6 +351,7 @@ public void test_RaiseExceptionOnConnectWithCallback() { null, mockTargetDriverDialect, null, + mockStorageService, mockTelemetryFactory)); } } From b12c2006f4803fdc3b4e3de8c7df5529ca88cd99 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 19 Feb 2025 10:13:22 -0800 Subject: [PATCH 031/149] Lazily auto-register default caches --- .../java/software/amazon/jdbc/Driver.java | 12 ---- .../jdbc/util/storage/ExpirationCache.java | 2 +- .../util/storage/ExpirationCacheBuilder.java | 71 +++++++++++++++++++ .../jdbc/util/storage/StorageServiceImpl.java | 25 +++++-- 4 files changed, 93 insertions(+), 17 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCacheBuilder.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index 0d846e767..b0f36d3ac 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -25,7 +25,6 @@ import java.util.Collections; import java.util.List; import java.util.Properties; -import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.logging.ConsoleHandler; @@ -65,10 +64,8 @@ import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; import software.amazon.jdbc.util.StringUtils; -import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; -import software.amazon.jdbc.util.storage.Topology; import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -120,15 +117,6 @@ public static void register() throws SQLException { final Driver driver = new Driver(); DriverManager.registerDriver(driver); registeredDriver = driver; - - storageService.registerItemCategoryIfAbsent( - ItemCategory.TOPOLOGY, - Topology.class, - false, - TimeUnit.MINUTES.toNanos(10), - TimeUnit.MINUTES.toNanos(5), - null, - null); } public static void deregister() throws SQLException { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index 78615b53f..c62202210 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -47,7 +47,7 @@ public class ExpirationCache { protected final ShouldDisposeFunc shouldDisposeFunc; protected final ItemDisposalFunc itemDisposalFunc; - public ExpirationCache( + ExpirationCache( final Class valueClass, final boolean isRenewableExpiration, final long cleanupIntervalNanos, diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCacheBuilder.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCacheBuilder.java new file mode 100644 index 000000000..5afc781a6 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCacheBuilder.java @@ -0,0 +1,71 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.storage; + +import java.util.concurrent.TimeUnit; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import software.amazon.jdbc.util.ItemDisposalFunc; +import software.amazon.jdbc.util.ShouldDisposeFunc; + +public class ExpirationCacheBuilder { + protected @NonNull Class valueClass; + protected boolean isRenewableExpiration = false; + protected long cleanupIntervalNanos = TimeUnit.MINUTES.toNanos(10); + protected long timeToLiveNanos = TimeUnit.MINUTES.toNanos(5); + protected @Nullable ShouldDisposeFunc shouldDisposeFunc; + protected @Nullable ItemDisposalFunc itemDisposalFunc; + + public ExpirationCacheBuilder(@NonNull Class valueClass) { + this.valueClass = valueClass; + } + + public ExpirationCacheBuilder withIsRenewableExpiration(boolean isRenewableExpiration) { + this.isRenewableExpiration = isRenewableExpiration; + return this; + } + + public ExpirationCacheBuilder withCleanupIntervalNanos(long cleanupIntervalNanos) { + this.cleanupIntervalNanos = cleanupIntervalNanos; + return this; + } + + public ExpirationCacheBuilder withTimeToLiveNanos(long timeToLiveNanos) { + this.timeToLiveNanos = timeToLiveNanos; + return this; + } + + public ExpirationCacheBuilder withShouldDisposeFunc(ShouldDisposeFunc shouldDisposeFunc) { + this.shouldDisposeFunc = shouldDisposeFunc; + return this; + } + + public ExpirationCacheBuilder withItemDisposalFunc(ItemDisposalFunc itemDisposalFunc) { + this.itemDisposalFunc = itemDisposalFunc; + return this; + } + + public ExpirationCache build() { + return new ExpirationCache<>( + this.valueClass, + this.isRenewableExpiration, + this.cleanupIntervalNanos, + this.timeToLiveNanos, + this.shouldDisposeFunc, + this.itemDisposalFunc); + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index f8a66e032..5f30d7689 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -16,17 +16,29 @@ package software.amazon.jdbc.util.storage; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; +import software.amazon.jdbc.plugin.customendpoint.CustomEndpointInfo; import software.amazon.jdbc.util.ItemDisposalFunc; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ShouldDisposeFunc; public class StorageServiceImpl implements StorageService { private static final Logger LOGGER = Logger.getLogger(StorageServiceImpl.class.getName()); - protected static Map> caches = new ConcurrentHashMap<>(); + protected static final Map> caches = new ConcurrentHashMap<>(); + protected static final Map>> defaultCacheSuppliers; + + static { + Map>> suppliers = new HashMap<>(); + suppliers.put(ItemCategory.TOPOLOGY, () -> new ExpirationCacheBuilder<>(Topology.class).build()); + suppliers.put(ItemCategory.CUSTOM_ENDPOINT, () -> new ExpirationCacheBuilder<>(CustomEndpointInfo.class).build()); + defaultCacheSuppliers = Collections.unmodifiableMap(suppliers); + } public StorageServiceImpl() { @@ -55,10 +67,15 @@ public void registerItemCategoryIfAbsent( @Override @SuppressWarnings("unchecked") public void set(String itemCategory, Object key, V value) { - final ExpirationCache cache = caches.get(itemCategory); + ExpirationCache cache = caches.get(itemCategory); if (cache == null) { - throw new IllegalStateException( - Messages.get("StorageServiceImpl.itemCategoryNotRegistered", new Object[] { itemCategory })); + Supplier> supplier = defaultCacheSuppliers.get(itemCategory); + if (supplier == null) { + throw new IllegalStateException( + Messages.get("StorageServiceImpl.itemCategoryNotRegistered", new Object[] {itemCategory})); + } else { + cache = caches.computeIfAbsent(itemCategory, c -> supplier.get()); + } } if (!cache.getValueClass().isInstance(value)) { From 34aaadbc14c56f062254059cfd245c41afd32f7a Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 20 Feb 2025 16:36:33 -0800 Subject: [PATCH 032/149] Move cleanup thread to StorageServiceImpl --- .../jdbc/util/storage/ExpirationCache.java | 44 +++++----------- .../jdbc/util/storage/StorageServiceImpl.java | 51 +++++++++++++++++++ 2 files changed, 63 insertions(+), 32 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index c62202210..1278d4a05 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -21,23 +21,14 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.util.ItemDisposalFunc; -import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ShouldDisposeFunc; public class ExpirationCache { private static final Logger LOGGER = Logger.getLogger(ExpirationCache.class.getName()); - protected final ExecutorService cleanupThreadPool = Executors.newFixedThreadPool(1, runnableTarget -> { - final Thread monitoringThread = new Thread(runnableTarget); - monitoringThread.setDaemon(true); - return monitoringThread; - }); protected final Map cache = new ConcurrentHashMap<>(); protected final Class valueClass; @@ -60,31 +51,10 @@ public class ExpirationCache { this.timeToLiveNanos = timeToLiveNanos; this.shouldDisposeFunc = shouldDisposeFunc; this.itemDisposalFunc = itemDisposalFunc; - this.initCleanupThread(); } - protected void initCleanupThread() { - cleanupThreadPool.submit(() -> { - while (!Thread.currentThread().isInterrupted()) { - try { - TimeUnit.NANOSECONDS.sleep(this.cleanupIntervalNanos); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - LOGGER.fine(Messages.get("ExpirationCache.cleanupThreadInterrupted")); - break; - } - - LOGGER.finest("ExpirationCache.cleaningUpCache"); - cache.forEach((key, value) -> { - try { - removeIfExpired(key); - } catch (Exception ex) { - // ignore - } - }); - } - }); - cleanupThreadPool.shutdown(); + public long getCleanupIntervalNanos() { + return cleanupIntervalNanos; } /** @@ -211,6 +181,16 @@ public boolean exists(final K key) { return cacheItem.item; } + public void removeExpiredEntries() { + cache.forEach((key, value) -> { + try { + removeIfExpired(key); + } catch (Exception ex) { + // ignore + } + }); + } + protected void removeIfExpired(K key) { // A list is used to store the cached item for later disposal since lambdas require references to outer variables // to be final. This allows us to dispose of the item after it has been removed and the cache has been unlocked, diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 5f30d7689..d2916c865 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -20,6 +20,11 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; @@ -30,8 +35,18 @@ public class StorageServiceImpl implements StorageService { private static final Logger LOGGER = Logger.getLogger(StorageServiceImpl.class.getName()); + protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(5); protected static final Map> caches = new ConcurrentHashMap<>(); protected static final Map>> defaultCacheSuppliers; + protected static final AtomicBoolean isInitialized = new AtomicBoolean(false); + protected static final ReentrantLock initLock = new ReentrantLock(); + protected static final Map cleanupTimes = new ConcurrentHashMap<>(); + protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { + final Thread thread = new Thread(r); + thread.setDaemon(true); + return thread; + })); + static { Map>> suppliers = new HashMap<>(); @@ -41,7 +56,42 @@ public class StorageServiceImpl implements StorageService { } public StorageServiceImpl() { + initCleanupThread(DEFAULT_CLEANUP_INTERVAL_NANOS); + } + + public StorageServiceImpl(long cleanupIntervalNanos) { + initCleanupThread(cleanupIntervalNanos); + } + + protected void initCleanupThread(long cleanupIntervalNanos) { + if (isInitialized.get()) { + return; + } + + initLock.lock(); + try { + if (isInitialized.get()) { + return; + } + + cleanupExecutor.scheduleAtFixedRate( + this::cleanAll, cleanupIntervalNanos, cleanupIntervalNanos, TimeUnit.NANOSECONDS); + } finally { + initLock.unlock(); + } + } + + protected void cleanAll() { + for (Map.Entry> entry : caches.entrySet()) { + String category = entry.getKey(); + ExpirationCache cache = entry.getValue(); + if (System.nanoTime() < cleanupTimes.get(category)) { + continue; + } + cache.removeExpiredEntries(); + cleanupTimes.put(category, System.nanoTime() + cache.getCleanupIntervalNanos()); + } } @Override @@ -62,6 +112,7 @@ public void registerItemCategoryIfAbsent( cleanupIntervalNanos, shouldDisposeFunc, itemDisposalFunc)); + cleanupTimes.put(itemCategory, System.nanoTime() + cleanupIntervalNanos); } @Override From 5e039732225e273c31d356128a25a19e80fde78e Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 20 Feb 2025 17:11:20 -0800 Subject: [PATCH 033/149] Minor fixes, cleanup --- .../jdbc/util/storage/StorageServiceImpl.java | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index d2916c865..7fbba4c2d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -47,7 +47,6 @@ public class StorageServiceImpl implements StorageService { return thread; })); - static { Map>> suppliers = new HashMap<>(); suppliers.put(ItemCategory.TOPOLOGY, () -> new ExpirationCacheBuilder<>(Topology.class).build()); @@ -76,6 +75,8 @@ protected void initCleanupThread(long cleanupIntervalNanos) { cleanupExecutor.scheduleAtFixedRate( this::cleanAll, cleanupIntervalNanos, cleanupIntervalNanos, TimeUnit.NANOSECONDS); + cleanupExecutor.shutdown(); + isInitialized.set(true); } finally { initLock.unlock(); } @@ -105,14 +106,16 @@ public void registerItemCategoryIfAbsent( @Nullable ItemDisposalFunc itemDisposalFunc) { caches.computeIfAbsent( itemCategory, - category -> new ExpirationCache<>( - itemClass, - isRenewableExpiration, - timeToLiveNanos, - cleanupIntervalNanos, - shouldDisposeFunc, - itemDisposalFunc)); - cleanupTimes.put(itemCategory, System.nanoTime() + cleanupIntervalNanos); + category -> { + cleanupTimes.put(category, System.nanoTime() + cleanupIntervalNanos); + return new ExpirationCache<>( + itemClass, + isRenewableExpiration, + timeToLiveNanos, + cleanupIntervalNanos, + shouldDisposeFunc, + itemDisposalFunc); + }); } @Override From 4cd7117174df517e41a9ed631cc2b1fe9db53241 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 20 Feb 2025 17:21:43 -0800 Subject: [PATCH 034/149] Add log to cleanup thread --- .../amazon/jdbc/util/storage/StorageServiceImpl.java | 5 +++-- .../resources/aws_advanced_jdbc_wrapper_messages.properties | 4 +--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 7fbba4c2d..b10df0045 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -74,7 +74,7 @@ protected void initCleanupThread(long cleanupIntervalNanos) { } cleanupExecutor.scheduleAtFixedRate( - this::cleanAll, cleanupIntervalNanos, cleanupIntervalNanos, TimeUnit.NANOSECONDS); + this::removeExpiredItems, cleanupIntervalNanos, cleanupIntervalNanos, TimeUnit.NANOSECONDS); cleanupExecutor.shutdown(); isInitialized.set(true); } finally { @@ -82,7 +82,8 @@ protected void initCleanupThread(long cleanupIntervalNanos) { } } - protected void cleanAll() { + protected void removeExpiredItems() { + LOGGER.finest(Messages.get("StorageServiceImpl.removeExpiredItems")); for (Map.Entry> entry : caches.entrySet()) { String category = entry.getKey(); ExpirationCache cache = entry.getValue(); diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 8790b7f50..8e052b00f 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -345,9 +345,6 @@ ReadWriteSplittingPlugin.failedToConnectToReader=Failed to connect to reader hos ReadWriteSplittingPlugin.unsupportedHostSpecSelectorStrategy=Unsupported host selection strategy ''{0}'' specified in plugin configuration parameter ''readerHostSelectorStrategy''. Please visit the Read/Write Splitting Plugin documentation for all supported strategies. ReadWriteSplittingPlugin.errorVerifyingInitialHostSpecRole=An error occurred while obtaining the connected host's role. This could occur if the connection is broken or if you are not connected to an Aurora database. -ExpirationCache.cleaningUpCache=Cleaning up cache... -ExpirationCache.cleanupThreadInterrupted=The cleanup thread was interrupted. - SAMLCredentialsProviderFactory.getSamlAssertionFailed=Failed to get SAML Assertion due to exception: ''{0}'' SamlAuthPlugin.javaStsSdkNotInClasspath=Required dependency 'AWS Java SDK for AWS Secret Token Service' is not on the classpath. SamlAuthPlugin.unhandledException=Unhandled exception: ''{0}'' @@ -355,6 +352,7 @@ SamlAuthPlugin.unhandledException=Unhandled exception: ''{0}'' StorageServiceImpl.incorrectValueType=Attempted to store an incorrect item type under item category ''{0}''. Items under this category should have type ''{1}'' but the passed in item had a type of ''{2}''. Item value: ''{3}''. StorageServiceImpl.itemCategoryNotRegistered=The given item category ''{0}'' is not registered. Please register the item category before storing items under the category. StorageServiceImpl.itemClassMismatch=The item stored at ''{0}'' under the category ''{1}'' did not have the expected type. The expected type was ''{2}'', but the stored item had a type of ''{3}''. Returning null. +StorageServiceImpl.removeExpiredItems=Removing expired items from the storage service... # Wrapper Utils WrapperUtils.noWrapperClassExists=No wrapper class exists for ''{0}''. From da8e93957057accde431606406fa6f0657f52a3c Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 20 Feb 2025 17:27:34 -0800 Subject: [PATCH 035/149] Adjust default cleanup time --- .../software/amazon/jdbc/util/storage/StorageServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index b10df0045..e5a0acb33 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -35,7 +35,7 @@ public class StorageServiceImpl implements StorageService { private static final Logger LOGGER = Logger.getLogger(StorageServiceImpl.class.getName()); - protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(5); + protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(1); protected static final Map> caches = new ConcurrentHashMap<>(); protected static final Map>> defaultCacheSuppliers; protected static final AtomicBoolean isInitialized = new AtomicBoolean(false); From a1794ecef1f97f2c11e5983429993d8d34c8f16e Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 24 Feb 2025 10:24:50 -0800 Subject: [PATCH 036/149] Replace custom endpoint storage --- .../software/amazon/jdbc/PluginService.java | 6 ++++++ .../amazon/jdbc/PluginServiceImpl.java | 15 +++++++++++---- .../CustomEndpointMonitorImpl.java | 19 +++++++++++-------- .../customendpoint/CustomEndpointPlugin.java | 3 ++- .../jdbc/util/storage/ItemCategory.java | 2 +- .../jdbc/util/storage/StorageServiceImpl.java | 6 ++++-- .../CustomEndpointMonitorImplTest.java | 18 +++++++++++++----- .../jdbc/plugin/efm/ConcurrencyTests.java | 6 ++++++ 8 files changed, 54 insertions(+), 21 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java index 531abdfb5..1b8e7dcfc 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java @@ -29,6 +29,7 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; /** @@ -80,11 +81,16 @@ EnumSet setCurrentConnection( HostSpec getInitialConnectionHostSpec(); + // TODO: evaluate if there is a better way to pass the storage service to the monitors that need it. + StorageService getStorageService(); + /** * Set the collection of hosts that should be allowed and/or blocked for connections. * * @param allowedAndBlockedHosts An object defining the allowed and blocked sets of hosts. + * @deprecated use StorageService#set(ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, key, allowedAndBlockedHosts) instead. */ + @Deprecated void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHosts); /** diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index 1514f310f..5c516329c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -31,7 +31,6 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -54,6 +53,7 @@ import software.amazon.jdbc.util.CacheMap; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -71,7 +71,6 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources, private final String driverProtocol; protected volatile HostListProvider hostListProvider; protected List allHosts = new ArrayList<>(); - protected AtomicReference allowedAndBlockedHosts = new AtomicReference<>(); protected Connection currentConnection; protected HostSpec currentHostSpec; protected HostSpec initialConnectionHostSpec; @@ -214,8 +213,15 @@ public HostSpec getInitialConnectionHostSpec() { } @Override + public StorageService getStorageService() { + return this.storageService; + } + + @Override + @Deprecated public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHosts) { - this.allowedAndBlockedHosts.set(allowedAndBlockedHosts); + this.storageService.set( + ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, this.initialConnectionHostSpec.getHost(), allowedAndBlockedHosts); } @Override @@ -409,7 +415,8 @@ public List getAllHosts() { @Override public List getHosts() { - AllowedAndBlockedHosts hostPermissions = this.allowedAndBlockedHosts.get(); + AllowedAndBlockedHosts hostPermissions = this.storageService.get( + ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, this.initialConnectionHostSpec.getHost(), AllowedAndBlockedHosts.class); if (hostPermissions == null) { return this.allHosts; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index 2a097ff9e..d7f2b8986 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -34,10 +34,11 @@ import software.amazon.awssdk.services.rds.model.Filter; import software.amazon.jdbc.AllowedAndBlockedHosts; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.CacheMap; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.storage.ItemCategory; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -60,7 +61,7 @@ public class CustomEndpointMonitorImpl implements CustomEndpointMonitor { protected final Region region; protected final long refreshRateNano; - protected final PluginService pluginService; + protected final StorageService storageService; protected final ExecutorService monitorExecutor = Executors.newSingleThreadExecutor(runnableTarget -> { final Thread monitoringThread = new Thread(runnableTarget); monitoringThread.setDaemon(true); @@ -75,8 +76,9 @@ public class CustomEndpointMonitorImpl implements CustomEndpointMonitor { /** * Constructs a CustomEndpointMonitorImpl instance for the host specified by {@code customEndpointHostSpec}. * - * @param pluginService The plugin service to use to update the set of allowed/blocked hosts according to - * the custom endpoint info. + * @param storageService The storage service used to store the set of allowed/blocked hosts according to the + * custom endpoint info. + * @param telemetryFactory The telemetry factory * @param customEndpointHostSpec The host information for the custom endpoint to be monitored. * @param region The region of the custom endpoint to be monitored. * @param refreshRateNano Controls how often the custom endpoint information should be fetched and analyzed for @@ -85,20 +87,20 @@ public class CustomEndpointMonitorImpl implements CustomEndpointMonitor { * information. */ public CustomEndpointMonitorImpl( - PluginService pluginService, + StorageService storageService, + TelemetryFactory telemetryFactory, HostSpec customEndpointHostSpec, String endpointIdentifier, Region region, long refreshRateNano, BiFunction rdsClientFunc) { - this.pluginService = pluginService; + this.storageService = storageService; this.customEndpointHostSpec = customEndpointHostSpec; this.endpointIdentifier = endpointIdentifier; this.region = region; this.refreshRateNano = refreshRateNano; this.rdsClient = rdsClientFunc.apply(customEndpointHostSpec, this.region); - TelemetryFactory telemetryFactory = this.pluginService.getTelemetryFactory(); this.infoChangedCounter = telemetryFactory.createCounter(TELEMETRY_ENDPOINT_INFO_CHANGED); this.monitorExecutor.submit(this); @@ -167,7 +169,8 @@ public void run() { allowedAndBlockedHosts = new AllowedAndBlockedHosts(null, endpointInfo.getExcludedMembers()); } - this.pluginService.setAllowedAndBlockedHosts(allowedAndBlockedHosts); + this.storageService.set( + ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, this.customEndpointHostSpec.getHost(), allowedAndBlockedHosts); customEndpointInfoCache.put( this.customEndpointHostSpec.getHost(), endpointInfo, CUSTOM_ENDPOINT_INFO_EXPIRATION_NANO); this.infoChangedCounter.inc(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java index a039c6aa6..dfc7c534d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java @@ -212,7 +212,8 @@ protected CustomEndpointMonitor createMonitorIfAbsent(Properties props) { return monitors.computeIfAbsent( this.customEndpointHostSpec.getHost(), (customEndpoint) -> new CustomEndpointMonitorImpl( - this.pluginService, + this.pluginService.getStorageService(), + this.pluginService.getTelemetryFactory(), this.customEndpointHostSpec, this.customEndpointId, this.region, diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemCategory.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemCategory.java index e92b871e2..117b3680c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemCategory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemCategory.java @@ -18,7 +18,7 @@ public class ItemCategory { public static final String TOPOLOGY = "topology"; - public static final String CUSTOM_ENDPOINT = "customEndpoint"; + public static final String ALLOWED_AND_BLOCKED_HOSTS = "allowedAndBlockedHosts"; private ItemCategory() { throw new UnsupportedOperationException( diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index e5a0acb33..2e56ac86d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -28,7 +28,7 @@ import java.util.function.Supplier; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; -import software.amazon.jdbc.plugin.customendpoint.CustomEndpointInfo; +import software.amazon.jdbc.AllowedAndBlockedHosts; import software.amazon.jdbc.util.ItemDisposalFunc; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ShouldDisposeFunc; @@ -50,7 +50,9 @@ public class StorageServiceImpl implements StorageService { static { Map>> suppliers = new HashMap<>(); suppliers.put(ItemCategory.TOPOLOGY, () -> new ExpirationCacheBuilder<>(Topology.class).build()); - suppliers.put(ItemCategory.CUSTOM_ENDPOINT, () -> new ExpirationCacheBuilder<>(CustomEndpointInfo.class).build()); + suppliers.put( + ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, + () -> new ExpirationCacheBuilder<>(AllowedAndBlockedHosts.class).build()); defaultCacheSuppliers = Collections.unmodifiableMap(suppliers); } diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java index 668f92e8e..1e5f1c39a 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java @@ -20,6 +20,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -46,14 +47,15 @@ import software.amazon.jdbc.AllowedAndBlockedHosts; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.HostSpecBuilder; -import software.amazon.jdbc.PluginService; import software.amazon.jdbc.hostavailability.HostAvailabilityStrategy; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; +import software.amazon.jdbc.util.storage.ItemCategory; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class CustomEndpointMonitorImplTest { - @Mock private PluginService mockPluginService; + @Mock private StorageService mockStorageService; @Mock private BiFunction mockRdsClientFunc; @Mock private RdsClient mockRdsClient; @Mock private DescribeDbClusterEndpointsResponse mockDescribeResponse; @@ -91,7 +93,6 @@ public void init() throws SQLException { twoEndpointList = Arrays.asList(mockClusterEndpoint1, mockClusterEndpoint2); oneEndpointList = Collections.singletonList(mockClusterEndpoint1); - when(mockPluginService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockTelemetryFactory.createCounter(any(String.class))).thenReturn(mockTelemetryCounter); when(mockRdsClientFunc.apply(any(HostSpec.class), any(Region.class))).thenReturn(mockRdsClient); when(mockRdsClient.describeDBClusterEndpoints(any(Consumer.class))).thenReturn(mockDescribeResponse); @@ -114,7 +115,14 @@ void cleanUp() throws Exception { @Test public void testRun() throws InterruptedException { CustomEndpointMonitorImpl monitor = new CustomEndpointMonitorImpl( - mockPluginService, host, endpointId, Region.US_EAST_1, TimeUnit.MILLISECONDS.toNanos(50), mockRdsClientFunc); + mockStorageService, + mockTelemetryFactory, + host, + endpointId, + Region.US_EAST_1, + TimeUnit.MILLISECONDS.toNanos(50), + mockRdsClientFunc); + // Wait for 2 run cycles. The first will return an unexpected number of endpoints in the API response, the second // will return the expected number of endpoints (one). TimeUnit.MILLISECONDS.sleep(100); @@ -122,7 +130,7 @@ public void testRun() throws InterruptedException { monitor.close(); ArgumentCaptor captor = ArgumentCaptor.forClass(AllowedAndBlockedHosts.class); - verify(mockPluginService).setAllowedAndBlockedHosts(captor.capture()); + verify(mockStorageService).set(eq(ItemCategory.ALLOWED_AND_BLOCKED_HOSTS), eq(host.getHost()), captor.capture()); assertEquals(staticMembersSet, captor.getValue().getAllowedHostIds()); assertNull(captor.getValue().getBlockedHostIds()); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java index 24bec0f8e..d8b7f80ff 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java @@ -74,6 +74,7 @@ import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.targetdriverdialect.PgTargetDriverDialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @Disabled @@ -496,6 +497,11 @@ public HostSpec getInitialConnectionHostSpec() { return null; } + @Override + public StorageService getStorageService() { + return null; + } + @Override public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHosts) { } From dcad5c3dccd1c2ff019cb8afeba2c3cce05965e7 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 26 Feb 2025 09:43:04 -0800 Subject: [PATCH 037/149] Implement ServiceContainer --- .../amazon/jdbc/PluginServiceImpl.java | 33 ++--- .../jdbc/dialect/AuroraMysqlDialect.java | 14 +- .../amazon/jdbc/dialect/AuroraPgDialect.java | 14 +- .../dialect/HostListProviderSupplier.java | 8 +- .../amazon/jdbc/dialect/MariaDbDialect.java | 4 +- .../amazon/jdbc/dialect/MysqlDialect.java | 4 +- .../amazon/jdbc/dialect/PgDialect.java | 4 +- .../RdsMultiAzDbClusterMysqlDialect.java | 12 +- .../dialect/RdsMultiAzDbClusterPgDialect.java | 12 +- .../amazon/jdbc/dialect/UnknownDialect.java | 4 +- .../AuroraHostListProvider.java | 9 +- .../hostlistprovider/RdsHostListProvider.java | 8 +- .../RdsMultiAzDbClusterListProvider.java | 9 +- .../MonitoringRdsHostListProvider.java | 13 +- .../MonitoringRdsMultiAzHostListProvider.java | 14 +- .../amazon/jdbc/util/ServiceContainer.java | 51 +++++++ .../jdbc/util/ServiceContainerImpl.java | 109 +++++++++++++++ .../jdbc/wrapper/ConnectionWrapper.java | 43 +++--- .../aurora/TestAuroraHostListProvider.java | 12 +- .../aurora/TestPluginServiceImpl.java | 10 +- .../amazon/jdbc/DialectDetectionTests.java | 127 +++++++++--------- .../amazon/jdbc/PluginServiceImplTests.java | 73 ++++------ .../RdsHostListProviderTest.java | 115 +++++----------- .../RdsMultiAzDbClusterListProviderTest.java | 89 +++++------- ...AwsSecretsManagerConnectionPluginTest.java | 73 ++++------ 25 files changed, 447 insertions(+), 417 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index 5c516329c..a39ba2907 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -52,6 +52,7 @@ import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.CacheMap; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; @@ -64,6 +65,7 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources, protected static final long DEFAULT_HOST_AVAILABILITY_CACHE_EXPIRE_NANO = TimeUnit.MINUTES.toNanos(5); protected static final CacheMap hostAvailabilityExpiringCache = new CacheMap<>(); + protected final ServiceContainer serviceContainer; protected final ConnectionPluginManager pluginManager; protected final StorageService storageService; private final Properties props; @@ -88,58 +90,60 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources, protected final ReentrantLock connectionSwitchLock = new ReentrantLock(); public PluginServiceImpl( - @NonNull final ConnectionPluginManager pluginManager, + @NonNull final ServiceContainer serviceContainer, @NonNull final Properties props, @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, - @NonNull final TargetDriverDialect targetDriverDialect, - @NonNull final StorageService storageService) + @NonNull final TargetDriverDialect targetDriverDialect) throws SQLException { - this(pluginManager, + this( + serviceContainer, new ExceptionManager(), props, originalUrl, targetDriverProtocol, null, targetDriverDialect, - storageService, null, null); } public PluginServiceImpl( - @NonNull final ConnectionPluginManager pluginManager, + @NonNull final ServiceContainer serviceContainer, @NonNull final Properties props, @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, @NonNull final TargetDriverDialect targetDriverDialect, - @NonNull final StorageService storageService, @Nullable final ConfigurationProfile configurationProfile) throws SQLException { - this(pluginManager, + this( + serviceContainer, new ExceptionManager(), props, originalUrl, targetDriverProtocol, null, targetDriverDialect, - storageService, configurationProfile, null); } public PluginServiceImpl( - @NonNull final ConnectionPluginManager pluginManager, + @NonNull final ServiceContainer serviceContainer, @NonNull final ExceptionManager exceptionManager, @NonNull final Properties props, @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, @Nullable final DialectProvider dialectProvider, @NonNull final TargetDriverDialect targetDriverDialect, - @NonNull final StorageService storageService, @Nullable final ConfigurationProfile configurationProfile, @Nullable final SessionStateService sessionStateService) throws SQLException { - this.pluginManager = pluginManager; + this.serviceContainer = serviceContainer; + this.serviceContainer.setHostListProviderService(this); + this.serviceContainer.setPluginService(this); + this.serviceContainer.setPluginManagerService(this); + + this.pluginManager = serviceContainer.getConnectionPluginManager(); this.props = props; this.originalUrl = originalUrl; this.driverProtocol = targetDriverProtocol; @@ -147,7 +151,7 @@ public PluginServiceImpl( this.exceptionManager = exceptionManager; this.dialectProvider = dialectProvider != null ? dialectProvider : new DialectManager(this); this.targetDriverDialect = targetDriverDialect; - this.storageService = storageService; + this.storageService = serviceContainer.getStorageService(); this.connectionProviderManager = new ConnectionProviderManager( this.pluginManager.getDefaultConnProvider(), this.pluginManager.getEffectiveConnProvider()); @@ -720,8 +724,7 @@ public void updateDialect(final @NonNull Connection connection) throws SQLExcept } final HostListProviderSupplier supplier = this.dialect.getHostListProvider(); - this.setHostListProvider(supplier.getProvider( - this.props, this.originalUrl, this, this, this.storageService)); + this.setHostListProvider(supplier.getProvider(this.props, this.originalUrl, this.serviceContainer)); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java index 530c94b3a..8279502e9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java @@ -22,6 +22,7 @@ import java.sql.Statement; import java.util.Collections; import java.util.List; +import software.amazon.jdbc.PluginService; import software.amazon.jdbc.hostlistprovider.AuroraHostListProvider; import software.amazon.jdbc.hostlistprovider.monitoring.MonitoringRdsHostListProvider; import software.amazon.jdbc.plugin.failover2.FailoverConnectionPlugin; @@ -81,27 +82,24 @@ public boolean isDialect(final Connection connection) { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> { - + return (properties, initialUrl, serviceContainer) -> { + final PluginService pluginService = serviceContainer.getPluginService(); final FailoverConnectionPlugin failover2Plugin = pluginService.getPlugin(FailoverConnectionPlugin.class); if (failover2Plugin != null) { return new MonitoringRdsHostListProvider( properties, initialUrl, - hostListProviderService, - storageService, + serviceContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, - IS_WRITER_QUERY, - pluginService); + IS_WRITER_QUERY); } return new AuroraHostListProvider( properties, initialUrl, - hostListProviderService, - storageService, + serviceContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY); diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java index 328efd9d5..5322d9f15 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java @@ -21,6 +21,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.logging.Logger; +import software.amazon.jdbc.PluginService; import software.amazon.jdbc.hostlistprovider.AuroraHostListProvider; import software.amazon.jdbc.hostlistprovider.monitoring.MonitoringRdsHostListProvider; import software.amazon.jdbc.plugin.failover2.FailoverConnectionPlugin; @@ -127,27 +128,24 @@ public boolean isDialect(final Connection connection) { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> { - + return (properties, initialUrl, serviceContainer) -> { + final PluginService pluginService = serviceContainer.getPluginService(); final FailoverConnectionPlugin failover2Plugin = pluginService.getPlugin(FailoverConnectionPlugin.class); if (failover2Plugin != null) { return new MonitoringRdsHostListProvider( properties, initialUrl, - hostListProviderService, - storageService, + serviceContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, - IS_WRITER_QUERY, - pluginService); + IS_WRITER_QUERY); } return new AuroraHostListProvider( properties, initialUrl, - hostListProviderService, - storageService, + serviceContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY); diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java index 46fe0573f..a3d9a8d22 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java @@ -19,16 +19,12 @@ import java.util.Properties; import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.HostListProvider; -import software.amazon.jdbc.HostListProviderService; -import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.ServiceContainer; @FunctionalInterface public interface HostListProviderSupplier { @NonNull HostListProvider getProvider( final @NonNull Properties properties, final String initialUrl, - final @NonNull HostListProviderService hostListProviderService, - final @NonNull PluginService pluginService, - final @NonNull StorageService storageService); + final @NonNull ServiceContainer serviceContainer); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java index 2f18408f5..1e0711d8c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java @@ -104,8 +104,8 @@ public List getDialectUpdateCandidates() { } public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> - new ConnectionStringHostListProvider(properties, initialUrl, hostListProviderService); + return (properties, initialUrl, serviceContainer) -> + new ConnectionStringHostListProvider(properties, initialUrl, serviceContainer.getHostListProviderService()); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java index 9b21127ad..964bd565d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java @@ -108,8 +108,8 @@ public List getDialectUpdateCandidates() { } public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> - new ConnectionStringHostListProvider(properties, initialUrl, hostListProviderService); + return (properties, initialUrl, serviceContainer) -> + new ConnectionStringHostListProvider(properties, initialUrl, serviceContainer.getHostListProviderService()); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java index 8d3b40676..43b60d12b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java @@ -106,8 +106,8 @@ public List getDialectUpdateCandidates() { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> - new ConnectionStringHostListProvider(properties, initialUrl, hostListProviderService); + return (properties, initialUrl, serviceContainer) -> + new ConnectionStringHostListProvider(properties, initialUrl, serviceContainer.getHostListProviderService()); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java index dba0b805e..f1e15d56b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java @@ -25,6 +25,7 @@ import java.util.Properties; import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.HostSpec; +import software.amazon.jdbc.PluginService; import software.amazon.jdbc.hostlistprovider.RdsMultiAzDbClusterListProvider; import software.amazon.jdbc.hostlistprovider.monitoring.MonitoringRdsMultiAzHostListProvider; import software.amazon.jdbc.plugin.failover.FailoverRestriction; @@ -96,20 +97,18 @@ public boolean isDialect(final Connection connection) { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> { - + return (properties, initialUrl, serviceContainer) -> { + final PluginService pluginService = serviceContainer.getPluginService(); final FailoverConnectionPlugin failover2Plugin = pluginService.getPlugin(FailoverConnectionPlugin.class); if (failover2Plugin != null) { return new MonitoringRdsMultiAzHostListProvider( properties, initialUrl, - hostListProviderService, - storageService, + serviceContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, - pluginService, FETCH_WRITER_NODE_QUERY, FETCH_WRITER_NODE_QUERY_COLUMN_NAME); @@ -117,8 +116,7 @@ public HostListProviderSupplier getHostListProvider() { return new RdsMultiAzDbClusterListProvider( properties, initialUrl, - hostListProviderService, - storageService, + serviceContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java index 50490529f..92abd7a9f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java @@ -22,6 +22,7 @@ import java.sql.Statement; import java.util.List; import java.util.logging.Logger; +import software.amazon.jdbc.PluginService; import software.amazon.jdbc.exceptions.ExceptionHandler; import software.amazon.jdbc.exceptions.MultiAzDbClusterPgExceptionHandler; import software.amazon.jdbc.hostlistprovider.RdsMultiAzDbClusterListProvider; @@ -112,20 +113,18 @@ public boolean isDialect(final Connection connection) { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> { - + return (properties, initialUrl, serviceContainer) -> { + final PluginService pluginService = serviceContainer.getPluginService(); final FailoverConnectionPlugin failover2Plugin = pluginService.getPlugin(FailoverConnectionPlugin.class); if (failover2Plugin != null) { return new MonitoringRdsMultiAzHostListProvider( properties, initialUrl, - hostListProviderService, - storageService, + serviceContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, - pluginService, FETCH_WRITER_NODE_QUERY, FETCH_WRITER_NODE_QUERY_COLUMN_NAME); @@ -134,8 +133,7 @@ public HostListProviderSupplier getHostListProvider() { return new RdsMultiAzDbClusterListProvider( properties, initialUrl, - hostListProviderService, - storageService, + serviceContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java index 85b8a9244..94a97ddfe 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java @@ -81,8 +81,8 @@ public List getDialectUpdateCandidates() { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, hostListProviderService, pluginService, storageService) -> - new ConnectionStringHostListProvider(properties, initialUrl, hostListProviderService); + return (properties, initialUrl, serviceContainer) -> + new ConnectionStringHostListProvider(properties, initialUrl, serviceContainer.getHostListProviderService()); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java index f019b971e..38b7324f9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java @@ -19,8 +19,7 @@ import java.util.Properties; import java.util.logging.Logger; -import software.amazon.jdbc.HostListProviderService; -import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.ServiceContainer; public class AuroraHostListProvider extends RdsHostListProvider { @@ -30,15 +29,13 @@ public class AuroraHostListProvider extends RdsHostListProvider { public AuroraHostListProvider( final Properties properties, final String originalUrl, - final HostListProviderService hostListProviderService, - final StorageService storageService, + final ServiceContainer serviceContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery) { super(properties, originalUrl, - hostListProviderService, - storageService, + serviceContainer, topologyQuery, nodeIdQuery, isReaderQuery); diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index c57385903..ba2c745c2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -54,6 +54,7 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SynchronousExecutor; import software.amazon.jdbc.util.Utils; @@ -129,13 +130,12 @@ public class RdsHostListProvider implements DynamicHostListProvider { public RdsHostListProvider( final Properties properties, final String originalUrl, - final HostListProviderService hostListProviderService, - final StorageService storageService, + final ServiceContainer serviceContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery) { - this.hostListProviderService = hostListProviderService; - this.storageService = storageService; + this.hostListProviderService = serviceContainer.getHostListProviderService(); + this.storageService = serviceContainer.getStorageService(); this.properties = properties; this.originalUrl = originalUrl; this.topologyQuery = topologyQuery; diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java index 89312811d..e10c11722 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java @@ -28,12 +28,11 @@ import java.util.List; import java.util.Properties; import java.util.logging.Logger; -import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.HostRole; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.ServiceContainer; public class RdsMultiAzDbClusterListProvider extends RdsHostListProvider { private final String fetchWriterNodeQuery; @@ -43,8 +42,7 @@ public class RdsMultiAzDbClusterListProvider extends RdsHostListProvider { public RdsMultiAzDbClusterListProvider( final Properties properties, final String originalUrl, - final HostListProviderService hostListProviderService, - final StorageService storageService, + final ServiceContainer serviceContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery, @@ -53,8 +51,7 @@ public RdsMultiAzDbClusterListProvider( ) { super(properties, originalUrl, - hostListProviderService, - storageService, + serviceContainer, topologyQuery, nodeIdQuery, isReaderQuery); diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index 077faf447..927f05ed6 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -25,15 +25,14 @@ import java.util.logging.Logger; import software.amazon.jdbc.AwsWrapperProperty; import software.amazon.jdbc.BlockingHostListProvider; -import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PluginService; import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.cleanup.CanReleaseResources; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.storage.ItemCategory; -import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.Topology; public class MonitoringRdsHostListProvider extends RdsHostListProvider @@ -73,15 +72,13 @@ public class MonitoringRdsHostListProvider extends RdsHostListProvider public MonitoringRdsHostListProvider( final Properties properties, final String originalUrl, - final HostListProviderService hostListProviderService, - final StorageService storageService, + final ServiceContainer serviceContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery, - final String writerTopologyQuery, - final PluginService pluginService) { - super(properties, originalUrl, hostListProviderService, storageService, topologyQuery, nodeIdQuery, isReaderQuery); - this.pluginService = pluginService; + final String writerTopologyQuery) { + super(properties, originalUrl, serviceContainer, topologyQuery, nodeIdQuery, isReaderQuery); + this.pluginService = serviceContainer.getPluginService(); this.writerTopologyQuery = writerTopologyQuery; this.highRefreshRateNano = TimeUnit.MILLISECONDS.toNanos( CLUSTER_TOPOLOGY_HIGH_REFRESH_RATE_MS.getLong(this.properties)); diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java index 7c4fd1c3d..4f3f2295b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java @@ -18,9 +18,7 @@ import java.util.Properties; import java.util.logging.Logger; -import software.amazon.jdbc.HostListProviderService; -import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.ServiceContainer; public class MonitoringRdsMultiAzHostListProvider extends MonitoringRdsHostListProvider { @@ -32,24 +30,20 @@ public class MonitoringRdsMultiAzHostListProvider extends MonitoringRdsHostListP public MonitoringRdsMultiAzHostListProvider( final Properties properties, final String originalUrl, - final HostListProviderService hostListProviderService, - final StorageService storageService, + final ServiceContainer serviceContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery, - final PluginService pluginService, final String fetchWriterNodeQuery, final String fetchWriterNodeColumnName) { super( properties, originalUrl, - hostListProviderService, - storageService, + serviceContainer, topologyQuery, nodeIdQuery, isReaderQuery, - "", - pluginService); + ""); this.fetchWriterNodeQuery = fetchWriterNodeQuery; this.fetchWriterNodeColumnName = fetchWriterNodeColumnName; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java new file mode 100644 index 000000000..7b1bf64d3 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java @@ -0,0 +1,51 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util; + +import software.amazon.jdbc.ConnectionPluginManager; +import software.amazon.jdbc.ConnectionProviderManager; +import software.amazon.jdbc.HostListProviderService; +import software.amazon.jdbc.PluginManagerService; +import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.telemetry.TelemetryFactory; + +public interface ServiceContainer { + StorageService getStorageService(); + + ConnectionPluginManager getConnectionPluginManager(); + + TelemetryFactory getTelemetryFactory(); + + HostListProviderService getHostListProviderService(); + + PluginService getPluginService(); + + PluginManagerService getPluginManagerService(); + + StorageService setStorageService(StorageService storageService); + + ConnectionPluginManager setConnectionPluginManager(ConnectionProviderManager connectionPluginManager); + + TelemetryFactory setTelemetryFactory(TelemetryFactory telemetryFactory); + + void setHostListProviderService(HostListProviderService hostListProviderService); + + void setPluginService(PluginService pluginService); + + void setPluginManagerService(PluginManagerService pluginManagerService); +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java new file mode 100644 index 000000000..464e7545b --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java @@ -0,0 +1,109 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util; + +import software.amazon.jdbc.ConnectionPluginManager; +import software.amazon.jdbc.ConnectionProviderManager; +import software.amazon.jdbc.HostListProviderService; +import software.amazon.jdbc.PluginManagerService; +import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.telemetry.TelemetryFactory; + +public class ServiceContainerImpl implements ServiceContainer { + private final StorageService storageService; + private final ConnectionPluginManager connectionPluginManager; + private final TelemetryFactory telemetryFactory; + + private HostListProviderService hostListProviderService; + private PluginService pluginService; + private PluginManagerService pluginManagerService; + + public ServiceContainerImpl( + StorageService storageService, + ConnectionPluginManager connectionPluginManager, + TelemetryFactory telemetryFactory, + HostListProviderService hostListProviderService, + PluginService pluginService, + PluginManagerService pluginManagerService) { + this(storageService, connectionPluginManager, telemetryFactory); + this.hostListProviderService = hostListProviderService; + this.pluginService = pluginService; + this.pluginManagerService = pluginManagerService; + } + + public ServiceContainerImpl( + StorageService storageService, + ConnectionPluginManager connectionPluginManager, + TelemetryFactory telemetryFactory) { + this.storageService = storageService; + this.connectionPluginManager = connectionPluginManager; + this.telemetryFactory = telemetryFactory; + } + + public StorageService getStorageService() { + return storageService; + } + + public ConnectionPluginManager getConnectionPluginManager() { + return connectionPluginManager; + } + + public TelemetryFactory getTelemetryFactory() { + return telemetryFactory; + } + + public HostListProviderService getHostListProviderService() { + return hostListProviderService; + } + + public PluginService getPluginService() { + return pluginService; + } + + public PluginManagerService getPluginManagerService() { + return pluginManagerService; + } + + @Override + public StorageService setStorageService(StorageService storageService) { + return null; + } + + @Override + public ConnectionPluginManager setConnectionPluginManager(ConnectionProviderManager connectionPluginManager) { + return null; + } + + @Override + public TelemetryFactory setTelemetryFactory(TelemetryFactory telemetryFactory) { + return null; + } + + + public void setHostListProviderService(HostListProviderService hostListProviderService) { + this.hostListProviderService = hostListProviderService; + } + + public void setPluginService(PluginService pluginService) { + this.pluginService = pluginService; + } + + public void setPluginManagerService(PluginManagerService pluginManagerService) { + this.pluginManagerService = pluginManagerService; + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index 753804405..d3b62e135 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -52,6 +52,8 @@ import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.ServiceContainerImpl; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; @@ -101,16 +103,19 @@ public ConnectionWrapper( effectiveConnectionProvider, this, telemetryFactory); + ServiceContainer serviceContainer = new ServiceContainerImpl(storageService, pluginManager, telemetryFactory); final PluginServiceImpl pluginService = new PluginServiceImpl( - pluginManager, + serviceContainer, props, url, this.targetDriverProtocol, targetDriverDialect, - storageService, this.configurationProfile); + serviceContainer.setHostListProviderService(pluginService); + serviceContainer.setPluginService(pluginService); + serviceContainer.setPluginManagerService(pluginService); - init(props, pluginManager, telemetryFactory, pluginService, pluginService, pluginService, storageService); + init(props, serviceContainer); if (PropertyDefinition.LOG_UNCLOSED_CONNECTIONS.getBoolean(props)) { this.openConnectionStacktrace = new Throwable(Messages.get("ConnectionWrapper.unclosedConnectionInstantiated")); @@ -133,37 +138,33 @@ protected ConnectionWrapper( throw new IllegalArgumentException("url"); } - init( - props, + ServiceContainer serviceContainer = new ServiceContainerImpl( + storageService, connectionPluginManager, telemetryFactory, - pluginService, hostListProviderService, - pluginManagerService, - storageService); + pluginService, + pluginManagerService + ); + + init(props, serviceContainer); } protected void init( final Properties props, - final ConnectionPluginManager connectionPluginManager, - final TelemetryFactory telemetryFactory, - final PluginService pluginService, - final HostListProviderService hostListProviderService, - final PluginManagerService pluginManagerService, - final StorageService storageService) throws SQLException { - this.pluginManager = connectionPluginManager; - this.telemetryFactory = telemetryFactory; - this.pluginService = pluginService; - this.hostListProviderService = hostListProviderService; - this.pluginManagerService = pluginManagerService; + final ServiceContainer serviceContainer) throws SQLException { + this.pluginManager = serviceContainer.getConnectionPluginManager(); + this.telemetryFactory = serviceContainer.getTelemetryFactory(); + this.pluginService = serviceContainer.getPluginService(); + this.hostListProviderService = serviceContainer.getHostListProviderService(); + this.pluginManagerService = serviceContainer.getPluginManagerService(); this.pluginManager.init( this.pluginService, props, pluginManagerService, this.configurationProfile); final HostListProviderSupplier supplier = this.pluginService.getDialect().getHostListProvider(); if (supplier != null) { - final HostListProvider provider = supplier.getProvider( - props, this.originalUrl, this.hostListProviderService, this.pluginService, storageService); + final HostListProvider provider = supplier.getProvider(props, this.originalUrl, serviceContainer); hostListProviderService.setHostListProvider(provider); } diff --git a/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java b/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java index d8b3d27ce..bb7352f0d 100644 --- a/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java +++ b/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java @@ -17,19 +17,13 @@ package integration.container.aurora; import java.util.Properties; -import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.hostlistprovider.AuroraHostListProvider; -import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.ServiceContainer; public class TestAuroraHostListProvider extends AuroraHostListProvider { - public TestAuroraHostListProvider( - HostListProviderService hostListProviderService, - StorageService storageService, - Properties properties, - String originalUrl) { - - super(properties, originalUrl, hostListProviderService, storageService, "", "", ""); + public TestAuroraHostListProvider(ServiceContainer serviceContainer, Properties properties, String originalUrl) { + super(properties, originalUrl, serviceContainer, "", "", ""); } public static void clearCache() { diff --git a/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java b/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java index 2e7d081f0..f8666d265 100644 --- a/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java +++ b/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java @@ -19,23 +19,21 @@ import java.sql.SQLException; import java.util.Properties; import org.checkerframework.checker.nullness.qual.NonNull; -import software.amazon.jdbc.ConnectionPluginManager; import software.amazon.jdbc.PluginServiceImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.ServiceContainer; public class TestPluginServiceImpl extends PluginServiceImpl { public TestPluginServiceImpl( - @NonNull ConnectionPluginManager pluginManager, + @NonNull ServiceContainer serviceContainer, @NonNull Properties props, @NonNull String originalUrl, String targetDriverProtocol, - @NonNull final TargetDriverDialect targetDriverDialect, - @NonNull final StorageService storageService) + @NonNull final TargetDriverDialect targetDriverDialect) throws SQLException { - super(pluginManager, props, originalUrl, targetDriverProtocol, targetDriverDialect, storageService); + super(serviceContainer, props, originalUrl, targetDriverProtocol, targetDriverDialect); } public static void clearHostAvailabilityCache() { diff --git a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java index b21a6b149..d50bd58ce 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java @@ -51,8 +51,11 @@ import software.amazon.jdbc.dialect.RdsPgDialect; import software.amazon.jdbc.exceptions.ExceptionManager; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.ServiceContainerImpl; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; +import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class DialectDetectionTests { private static final String LOCALHOST = "localhost"; @@ -61,27 +64,30 @@ public class DialectDetectionTests { private static final String MYSQL_PROTOCOL = "jdbc:mysql://"; private static final String PG_PROTOCOL = "jdbc:postgresql://"; private static final String MARIA_PROTOCOL = "jdbc:mariadb://"; + private final DialectManager dialectManager = new DialectManager(null); + private final StorageService storageService = new StorageServiceImpl(); + private final Properties props = new Properties(); + private ServiceContainer serviceContainer; + private AutoCloseable closeable; @Mock private HostListProvider mockHostListProvider; @Mock private Connection mockConnection; @Mock private Statement mockStatement; - @Mock private ResultSet successResultSet; - @Mock private ResultSet failResultSet; + @Mock private ResultSet mockSuccessResultSet; + @Mock private ResultSet mockFailResultSet; @Mock private HostSpec mockHost; - @Mock private ConnectionPluginManager pluginManager; + @Mock private ConnectionPluginManager mockPluginManager; @Mock private TargetDriverDialect mockTargetDriverDialect; + @Mock private TelemetryFactory mockTelemetryFactory; @Mock private ResultSetMetaData mockResultSetMetaData; - private final DialectManager dialectManager = new DialectManager(null); - private final StorageService storageService = new StorageServiceImpl(); - private final Properties props = new Properties(); - private AutoCloseable closeable; @BeforeEach void setUp() throws SQLException { closeable = MockitoAnnotations.openMocks(this); when(this.mockConnection.createStatement()).thenReturn(this.mockStatement); when(this.mockHost.getUrl()).thenReturn("url"); - when(this.failResultSet.next()).thenReturn(false); - pluginManager.plugins = new ArrayList<>(); + when(this.mockFailResultSet.next()).thenReturn(false); + serviceContainer = new ServiceContainerImpl(storageService, mockPluginManager, mockTelemetryFactory); + mockPluginManager.plugins = new ArrayList<>(); } @AfterEach @@ -90,17 +96,16 @@ void cleanUp() throws Exception { DialectManager.resetEndpointCache(); } - PluginServiceImpl getPluginService(String host, String protocol) throws SQLException { + PluginServiceImpl getPluginService(String protocol) throws SQLException { return spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), props, - protocol + host, + protocol + DialectDetectionTests.LOCALHOST, protocol, null, mockTargetDriverDialect, - storageService, null, null)); } @@ -128,8 +133,8 @@ static Stream getInitialDialectArguments() { @Test void testUpdateDialectMysqlUnchanged() throws SQLException { - when(mockStatement.executeQuery(any())).thenReturn(failResultSet); - final PluginServiceImpl target = getPluginService(LOCALHOST, MYSQL_PROTOCOL); + when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet); + final PluginServiceImpl target = getPluginService(MYSQL_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(MysqlDialect.class, target.dialect.getClass()); @@ -137,14 +142,14 @@ void testUpdateDialectMysqlUnchanged() throws SQLException { @Test void testUpdateDialectMysqlToRds() throws SQLException { - when(mockStatement.executeQuery(any())).thenReturn(failResultSet); - when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'version_comment'")).thenReturn(successResultSet); - when(successResultSet.getString(1)).thenReturn("Source distribution"); - when(successResultSet.next()).thenReturn(true, false, true, false); - when(successResultSet.getMetaData()).thenReturn(mockResultSetMetaData); - when(failResultSet.next()).thenReturn(false); + when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet); + when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'version_comment'")).thenReturn(mockSuccessResultSet); + when(mockSuccessResultSet.getString(1)).thenReturn("Source distribution"); + when(mockSuccessResultSet.next()).thenReturn(true, false, true, false); + when(mockSuccessResultSet.getMetaData()).thenReturn(mockResultSetMetaData); + when(mockFailResultSet.next()).thenReturn(false); when(mockResultSetMetaData.getColumnCount()).thenReturn(1); - final PluginServiceImpl target = getPluginService(LOCALHOST, MYSQL_PROTOCOL); + final PluginServiceImpl target = getPluginService(MYSQL_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(RdsMysqlDialect.class, target.dialect.getClass()); @@ -156,9 +161,9 @@ void testUpdateDialectMysqlToRds() throws SQLException { // 1) test DialectManager.getDialect() to return RdsMultiAzDbClusterMysqlDialect // 2) test PluginServiceImpl.updateDialect() with mocked DialectManager.getDialect() void testUpdateDialectMysqlToTaz() throws SQLException { - when(mockStatement.executeQuery(any())).thenReturn(failResultSet, successResultSet); - when(successResultSet.next()).thenReturn(true); - final PluginServiceImpl target = getPluginService(LOCALHOST, MYSQL_PROTOCOL); + when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet, mockSuccessResultSet); + when(mockSuccessResultSet.next()).thenReturn(true); + final PluginServiceImpl target = getPluginService(MYSQL_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(AuroraMysqlDialect.class, target.dialect.getClass()); @@ -166,10 +171,10 @@ void testUpdateDialectMysqlToTaz() throws SQLException { @Test void testUpdateDialectMysqlToAurora() throws SQLException { - when(mockStatement.executeQuery(any())).thenReturn(failResultSet); - when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'aurora_version'")).thenReturn(successResultSet); - when(successResultSet.next()).thenReturn(true, false); - final PluginServiceImpl target = getPluginService(LOCALHOST, MYSQL_PROTOCOL); + when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet); + when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'aurora_version'")).thenReturn(mockSuccessResultSet); + when(mockSuccessResultSet.next()).thenReturn(true, false); + final PluginServiceImpl target = getPluginService(MYSQL_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(AuroraMysqlDialect.class, target.dialect.getClass()); @@ -177,8 +182,8 @@ void testUpdateDialectMysqlToAurora() throws SQLException { @Test void testUpdateDialectPgUnchanged() throws SQLException { - when(mockStatement.executeQuery(any())).thenReturn(failResultSet); - final PluginServiceImpl target = getPluginService(LOCALHOST, PG_PROTOCOL); + when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet); + final PluginServiceImpl target = getPluginService(PG_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(PgDialect.class, target.dialect.getClass()); @@ -187,13 +192,13 @@ void testUpdateDialectPgUnchanged() throws SQLException { @Test void testUpdateDialectPgToRds() throws SQLException { when(mockStatement.executeQuery(any())) - .thenReturn(successResultSet, failResultSet, failResultSet, successResultSet); - when(successResultSet.getBoolean(any())).thenReturn(false); - when(successResultSet.getBoolean("rds_tools")).thenReturn(true); - when(successResultSet.getBoolean("aurora_stat_utils")).thenReturn(false); - when(successResultSet.next()).thenReturn(true); - when(failResultSet.next()).thenReturn(false); - final PluginServiceImpl target = getPluginService(LOCALHOST, PG_PROTOCOL); + .thenReturn(mockSuccessResultSet, mockFailResultSet, mockFailResultSet, mockSuccessResultSet); + when(mockSuccessResultSet.getBoolean(any())).thenReturn(false); + when(mockSuccessResultSet.getBoolean("rds_tools")).thenReturn(true); + when(mockSuccessResultSet.getBoolean("aurora_stat_utils")).thenReturn(false); + when(mockSuccessResultSet.next()).thenReturn(true); + when(mockFailResultSet.next()).thenReturn(false); + final PluginServiceImpl target = getPluginService(PG_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(RdsPgDialect.class, target.dialect.getClass()); @@ -205,10 +210,10 @@ void testUpdateDialectPgToRds() throws SQLException { // 1) test DialectManager.getDialect() to return RdsMultiAzDbClusterMysqlDialect // 2) test PluginServiceImpl.updateDialect() with mocked DialectManager.getDialect() void testUpdateDialectPgToTaz() throws SQLException { - when(mockStatement.executeQuery(any())).thenReturn(successResultSet); - when(successResultSet.getBoolean(any())).thenReturn(false); - when(successResultSet.next()).thenReturn(true); - final PluginServiceImpl target = getPluginService(LOCALHOST, PG_PROTOCOL); + when(mockStatement.executeQuery(any())).thenReturn(mockSuccessResultSet); + when(mockSuccessResultSet.getBoolean(any())).thenReturn(false); + when(mockSuccessResultSet.next()).thenReturn(true); + final PluginServiceImpl target = getPluginService(PG_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(RdsMultiAzDbClusterPgDialect.class, target.dialect.getClass()); @@ -220,10 +225,10 @@ void testUpdateDialectPgToTaz() throws SQLException { // 1) test DialectManager.getDialect() to return RdsMultiAzDbClusterMysqlDialect // 2) test PluginServiceImpl.updateDialect() with mocked DialectManager.getDialect() void testUpdateDialectPgToAurora() throws SQLException { - when(mockStatement.executeQuery(any())).thenReturn(successResultSet); - when(successResultSet.next()).thenReturn(true); - when(successResultSet.getBoolean(any())).thenReturn(true); - final PluginServiceImpl target = getPluginService(LOCALHOST, PG_PROTOCOL); + when(mockStatement.executeQuery(any())).thenReturn(mockSuccessResultSet); + when(mockSuccessResultSet.next()).thenReturn(true); + when(mockSuccessResultSet.getBoolean(any())).thenReturn(true); + final PluginServiceImpl target = getPluginService(PG_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(AuroraPgDialect.class, target.dialect.getClass()); @@ -231,8 +236,8 @@ void testUpdateDialectPgToAurora() throws SQLException { @Test void testUpdateDialectMariaUnchanged() throws SQLException { - when(mockStatement.executeQuery(any())).thenReturn(failResultSet); - final PluginServiceImpl target = getPluginService(LOCALHOST, MARIA_PROTOCOL); + when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet); + final PluginServiceImpl target = getPluginService(MARIA_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(MariaDbDialect.class, target.dialect.getClass()); @@ -240,14 +245,14 @@ void testUpdateDialectMariaUnchanged() throws SQLException { @Test void testUpdateDialectMariaToMysqlRds() throws SQLException { - when(mockStatement.executeQuery(any())).thenReturn(failResultSet); - when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'version_comment'")).thenReturn(successResultSet); - when(successResultSet.getString(1)).thenReturn("Source distribution"); - when(successResultSet.next()).thenReturn(true, false, true, false); - when(successResultSet.getMetaData()).thenReturn(mockResultSetMetaData); - when(failResultSet.next()).thenReturn(false); + when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet); + when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'version_comment'")).thenReturn(mockSuccessResultSet); + when(mockSuccessResultSet.getString(1)).thenReturn("Source distribution"); + when(mockSuccessResultSet.next()).thenReturn(true, false, true, false); + when(mockSuccessResultSet.getMetaData()).thenReturn(mockResultSetMetaData); + when(mockFailResultSet.next()).thenReturn(false); when(mockResultSetMetaData.getColumnCount()).thenReturn(1); - final PluginServiceImpl target = getPluginService(LOCALHOST, MARIA_PROTOCOL); + final PluginServiceImpl target = getPluginService(MARIA_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(RdsMysqlDialect.class, target.dialect.getClass()); @@ -259,8 +264,8 @@ void testUpdateDialectMariaToMysqlRds() throws SQLException { // 1) test DialectManager.getDialect() to return RdsMultiAzDbClusterMysqlDialect // 2) test PluginServiceImpl.updateDialect() with mocked DialectManager.getDialect() void testUpdateDialectMariaToMysqlTaz() throws SQLException { - when(mockStatement.executeQuery(any())).thenReturn(failResultSet, successResultSet); - final PluginServiceImpl target = getPluginService(LOCALHOST, MARIA_PROTOCOL); + when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet, mockSuccessResultSet); + final PluginServiceImpl target = getPluginService(MARIA_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(RdsMultiAzDbClusterMysqlDialect.class, target.dialect.getClass()); @@ -268,10 +273,10 @@ void testUpdateDialectMariaToMysqlTaz() throws SQLException { @Test void testUpdateDialectMariaToMysqlAurora() throws SQLException { - when(mockStatement.executeQuery(any())).thenReturn(failResultSet); - when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'aurora_version'")).thenReturn(successResultSet); - when(successResultSet.next()).thenReturn(true, false); - final PluginServiceImpl target = getPluginService(LOCALHOST, MARIA_PROTOCOL); + when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet); + when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'aurora_version'")).thenReturn(mockSuccessResultSet); + when(mockSuccessResultSet.next()).thenReturn(true, false); + final PluginServiceImpl target = getPluginService(MARIA_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(AuroraMysqlDialect.class, target.dialect.getClass()); diff --git a/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java b/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java index 95b6f7500..5299c3afa 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java @@ -66,6 +66,7 @@ import software.amazon.jdbc.profile.ConfigurationProfileBuilder; import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; @@ -77,6 +78,7 @@ public class PluginServiceImplTests { private static final StorageService storageService = new StorageServiceImpl(); private AutoCloseable closeable; + @Mock ServiceContainer serviceContainer; @Mock ConnectionPluginManager pluginManager; @Mock Connection newConnection; @Mock Connection oldConnection; @@ -98,6 +100,8 @@ void setUp() throws SQLException { when(oldConnection.isClosed()).thenReturn(false); when(newConnection.createStatement()).thenReturn(statement); when(statement.executeQuery(any())).thenReturn(resultSet); + when(serviceContainer.getConnectionPluginManager()).thenReturn(pluginManager); + when(serviceContainer.getStorageService()).thenReturn(storageService); PluginServiceImpl.hostAvailabilityExpiringCache.clear(); } @@ -114,14 +118,13 @@ public void testOldConnectionNoSuggestion() throws SQLException { PluginServiceImpl target = spy(new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -144,14 +147,13 @@ public void testOldConnectionDisposeSuggestion() throws SQLException { PluginServiceImpl target = spy(new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -174,14 +176,13 @@ public void testOldConnectionPreserveSuggestion() throws SQLException { PluginServiceImpl target = spy(new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -208,14 +209,13 @@ public void testOldConnectionMixedSuggestion() throws SQLException { PluginServiceImpl target = spy(new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -239,14 +239,13 @@ public void testChangesNewConnectionNewHostNewPortNewRoleNewAvailability() throw PluginServiceImpl target = spy(new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -279,14 +278,13 @@ public void testChangesNewConnectionNewRoleNewAvailability() throws SQLException PluginServiceImpl target = spy(new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -319,14 +317,13 @@ public void testChangesNewConnection() throws SQLException { PluginServiceImpl target = spy(new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -359,14 +356,13 @@ public void testChangesNoChanges() throws SQLException { PluginServiceImpl target = spy(new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.currentConnection = oldConnection; @@ -391,14 +387,13 @@ public void testSetNodeListAdded() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.allHosts = new ArrayList<>(); @@ -426,14 +421,13 @@ public void testSetNodeListDeleted() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.allHosts = Arrays.asList( @@ -464,14 +458,13 @@ public void testSetNodeListChanged() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.allHosts = Collections.singletonList(new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) @@ -502,14 +495,13 @@ public void testSetNodeListNoChanges() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.allHosts = Collections.singletonList(new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) @@ -529,14 +521,13 @@ public void testNodeAvailabilityNotChanged() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.allHosts = Collections.singletonList( @@ -559,14 +550,13 @@ public void testNodeAvailabilityChanged_WentDown() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.allHosts = Collections.singletonList( @@ -596,14 +586,13 @@ public void testNodeAvailabilityChanged_WentUp() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.allHosts = Collections.singletonList( @@ -644,14 +633,13 @@ public void testNodeAvailabilityChanged_WentUp_ByAlias() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); @@ -690,14 +678,13 @@ public void testNodeAvailabilityChanged_WentUp_MultipleHostsByAlias() throws SQL PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); @@ -769,14 +756,13 @@ void testRefreshHostList_withCachedHostAvailability() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); when(target.getHostListProvider()).thenReturn(hostListProvider); @@ -827,14 +813,13 @@ void testForceRefreshHostList_withCachedHostAvailability() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); when(target.getHostListProvider()).thenReturn(hostListProvider); @@ -853,14 +838,13 @@ void testForceRefreshHostList_withCachedHostAvailability() throws SQLException { void testIdentifyConnectionWithNoAliases() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); when(target.getHostListProvider()).thenReturn(hostListProvider); @@ -875,14 +859,13 @@ void testIdentifyConnectionWithAliases() throws SQLException { .build(); PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.hostListProvider = hostListProvider; @@ -904,14 +887,13 @@ void testFillAliasesNonEmptyAliases() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); @@ -927,14 +909,13 @@ void testFillAliasesWithInstanceEndpoint(Dialect dialect, String[] expectedInsta final HostSpec empty = new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("foo").build(); PluginServiceImpl target = spy( new PluginServiceImpl( - pluginManager, + serviceContainer, new ExceptionManager(), PROPERTIES, URL, DRIVER_PROTOCOL, dialectManager, mockTargetDriverDialect, - storageService, configurationProfile, sessionStateService)); target.hostListProvider = hostListProvider; diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java index 24ff2a4df..89fecc192 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java @@ -47,7 +47,6 @@ import java.util.Collections; import java.util.List; import java.util.Properties; -import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -65,6 +64,7 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider.FetchTopologyResult; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; @@ -77,6 +77,7 @@ class RdsHostListProviderTest { @Mock private Connection mockConnection; @Mock private Statement mockStatement; @Mock private ResultSet mockResultSet; + @Mock private ServiceContainer mockServiceContainer; @Mock private PluginService mockPluginService; @Mock private HostListProviderService mockHostListProviderService; @Mock Dialect mockTopologyAwareDialect; @@ -92,6 +93,8 @@ class RdsHostListProviderTest { @BeforeEach void setUp() throws SQLException { closeable = MockitoAnnotations.openMocks(this); + when(mockServiceContainer.getHostListProviderService()).thenReturn(mockHostListProviderService); + when(mockServiceContainer.getStorageService()).thenReturn(storageService); when(mockPluginService.getCurrentConnection()).thenReturn(mockConnection); when(mockPluginService.connect(any(HostSpec.class), any(Properties.class))).thenReturn(mockConnection); when(mockPluginService.getCurrentHostSpec()).thenReturn(currentHostSpec); @@ -101,15 +104,6 @@ void setUp() throws SQLException { when(mockHostListProviderService.getHostSpecBuilder()) .thenReturn(new HostSpecBuilder(new SimpleHostAvailabilityStrategy())); when(mockHostListProviderService.getCurrentConnection()).thenReturn(mockConnection); - - storageService.registerItemCategoryIfAbsent( - ItemCategory.TOPOLOGY, - Topology.class, - false, - TimeUnit.MINUTES.toNanos(10), - TimeUnit.MINUTES.toNanos(5), - null, - null); } @AfterEach @@ -119,14 +113,11 @@ void tearDown() throws Exception { closeable.close(); } - private RdsHostListProvider getRdsHostListProvider( - HostListProviderService mockHostListProviderService, - String originalUrl) throws SQLException { + private RdsHostListProvider getRdsHostListProvider(String originalUrl) throws SQLException { RdsHostListProvider provider = new RdsHostListProvider( new Properties(), originalUrl, - mockHostListProviderService, - storageService, + mockServiceContainer, "foo", "bar", "baz"); provider.init(); return provider; @@ -134,8 +125,7 @@ private RdsHostListProvider getRdsHostListProvider( @Test void testGetTopology_returnCachedTopology() throws SQLException { - rdsHostListProvider = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, "protocol://url/")); + rdsHostListProvider = Mockito.spy(getRdsHostListProvider("protocol://url/")); final List expected = hosts; storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, new Topology(expected)); @@ -148,8 +138,7 @@ void testGetTopology_returnCachedTopology() throws SQLException { @Test void testGetTopology_withForceUpdate_returnsUpdatedTopology() throws SQLException { - rdsHostListProvider = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url")); + rdsHostListProvider = Mockito.spy(getRdsHostListProvider("jdbc:someprotocol://url")); rdsHostListProvider.isInitialized = true; storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, new Topology(hosts)); @@ -166,8 +155,7 @@ void testGetTopology_withForceUpdate_returnsUpdatedTopology() throws SQLExceptio @Test void testGetTopology_noForceUpdate_queryReturnsEmptyHostList() throws SQLException { - rdsHostListProvider = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url")); + rdsHostListProvider = Mockito.spy(getRdsHostListProvider("jdbc:someprotocol://url")); rdsHostListProvider.clusterId = "cluster-id"; rdsHostListProvider.isInitialized = true; @@ -184,8 +172,7 @@ void testGetTopology_noForceUpdate_queryReturnsEmptyHostList() throws SQLExcepti @Test void testGetTopology_withForceUpdate_returnsInitialHostList() throws SQLException { - rdsHostListProvider = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url")); + rdsHostListProvider = Mockito.spy(getRdsHostListProvider("jdbc:someprotocol://url")); rdsHostListProvider.clear(); doReturn(new ArrayList<>()).when(rdsHostListProvider).queryForTopology(mockConnection); @@ -211,8 +198,7 @@ void testQueryForTopology_withDifferentDriverProtocol() throws SQLException { when(mockResultSet.getString(eq(1))).thenReturn("mysql"); - rdsHostListProvider = - getRdsHostListProvider(mockHostListProviderService, "mysql://url/"); + rdsHostListProvider = getRdsHostListProvider("mysql://url/"); List hosts = rdsHostListProvider.queryForTopology(mockConnection); assertEquals(expectedMySQL, hosts); @@ -220,16 +206,14 @@ void testQueryForTopology_withDifferentDriverProtocol() throws SQLException { when(mockResultSet.next()).thenReturn(true, false); when(mockResultSet.getString(eq(1))).thenReturn("postgresql"); - rdsHostListProvider = - getRdsHostListProvider(mockHostListProviderService, "postgresql://url/"); + rdsHostListProvider = getRdsHostListProvider("postgresql://url/"); hosts = rdsHostListProvider.queryForTopology(mockConnection); assertEquals(expectedPostgres, hosts); } @Test void testQueryForTopology_queryResultsInException() throws SQLException { - rdsHostListProvider = - getRdsHostListProvider(mockHostListProviderService, "protocol://url/"); + rdsHostListProvider = getRdsHostListProvider("protocol://url/"); when(mockStatement.executeQuery(queryCaptor.capture())).thenThrow(new SQLSyntaxErrorException()); assertThrows( @@ -239,7 +223,7 @@ void testQueryForTopology_queryResultsInException() throws SQLException { @Test void testGetCachedTopology_returnStoredTopology() throws SQLException { - rdsHostListProvider = getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url"); + rdsHostListProvider = getRdsHostListProvider("jdbc:someprotocol://url"); final List expected = hosts; storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, new Topology(expected)); @@ -252,9 +236,7 @@ void testGetCachedTopology_returnStoredTopology() throws SQLException { void testTopologyCache_NoSuggestedClusterId() throws SQLException { RdsHostListProvider.clearAll(); - RdsHostListProvider provider1 = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, - "jdbc:something://cluster-a.domain.com/")); + RdsHostListProvider provider1 = Mockito.spy(getRdsHostListProvider("jdbc:something://cluster-a.domain.com/")); provider1.init(); final List topologyClusterA = Arrays.asList( new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) @@ -272,9 +254,7 @@ void testTopologyCache_NoSuggestedClusterId() throws SQLException { final List topologyProvider1 = provider1.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); - RdsHostListProvider provider2 = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, - "jdbc:something://cluster-b.domain.com/")); + RdsHostListProvider provider2 = Mockito.spy(getRdsHostListProvider("jdbc:something://cluster-b.domain.com/")); provider2.init(); assertNull(provider2.getStoredTopology()); @@ -297,9 +277,8 @@ void testTopologyCache_NoSuggestedClusterId() throws SQLException { void testTopologyCache_SuggestedClusterIdForRds() throws SQLException { RdsHostListProvider.clearAll(); - RdsHostListProvider provider1 = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, - "jdbc:something://cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com/")); + RdsHostListProvider provider1 = + Mockito.spy(getRdsHostListProvider("jdbc:something://cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com/")); provider1.init(); final List topologyClusterA = Arrays.asList( new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) @@ -325,9 +304,8 @@ void testTopologyCache_SuggestedClusterIdForRds() throws SQLException { final List topologyProvider1 = provider1.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); - RdsHostListProvider provider2 = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, - "jdbc:something://cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com/")); + RdsHostListProvider provider2 = + Mockito.spy(getRdsHostListProvider("jdbc:something://cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com/")); provider2.init(); assertEquals(provider1.clusterId, provider2.clusterId); @@ -344,9 +322,8 @@ void testTopologyCache_SuggestedClusterIdForRds() throws SQLException { void testTopologyCache_SuggestedClusterIdForInstance() throws SQLException { RdsHostListProvider.clearAll(); - RdsHostListProvider provider1 = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, - "jdbc:something://cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com/")); + RdsHostListProvider provider1 = + Mockito.spy(getRdsHostListProvider("jdbc:something://cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com/")); provider1.init(); final List topologyClusterA = Arrays.asList( new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) @@ -372,9 +349,8 @@ void testTopologyCache_SuggestedClusterIdForInstance() throws SQLException { final List topologyProvider1 = provider1.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); - RdsHostListProvider provider2 = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, - "jdbc:something://instance-a-3.xyz.us-east-2.rds.amazonaws.com/")); + RdsHostListProvider provider2 = + Mockito.spy(getRdsHostListProvider("jdbc:something://instance-a-3.xyz.us-east-2.rds.amazonaws.com/")); provider2.init(); assertEquals(provider1.clusterId, provider2.clusterId); @@ -391,9 +367,8 @@ void testTopologyCache_SuggestedClusterIdForInstance() throws SQLException { void testTopologyCache_AcceptSuggestion() throws SQLException { RdsHostListProvider.clearAll(); - RdsHostListProvider provider1 = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, - "jdbc:something://instance-a-2.xyz.us-east-2.rds.amazonaws.com/")); + RdsHostListProvider provider1 = + Mockito.spy(getRdsHostListProvider("jdbc:something://instance-a-2.xyz.us-east-2.rds.amazonaws.com/")); provider1.init(); final List topologyClusterA = Arrays.asList( new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) @@ -421,9 +396,8 @@ void testTopologyCache_AcceptSuggestion() throws SQLException { // RdsHostListProvider.logCache(); - RdsHostListProvider provider2 = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, - "jdbc:something://cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com/")); + RdsHostListProvider provider2 = + Mockito.spy(getRdsHostListProvider("jdbc:something://cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com/")); provider2.init(); doAnswer(a -> topologyClusterA).when(provider2).queryForTopology(any(Connection.class)); @@ -451,9 +425,7 @@ void testTopologyCache_AcceptSuggestion() throws SQLException { @Test void testIdentifyConnectionWithInvalidNodeIdQuery() throws SQLException { - rdsHostListProvider = Mockito.spy(getRdsHostListProvider( - mockHostListProviderService, - "jdbc:someprotocol://url")); + rdsHostListProvider = Mockito.spy(getRdsHostListProvider("jdbc:someprotocol://url")); when(mockResultSet.next()).thenReturn(false); assertThrows(SQLException.class, () -> rdsHostListProvider.identifyConnection(mockConnection)); @@ -464,9 +436,7 @@ void testIdentifyConnectionWithInvalidNodeIdQuery() throws SQLException { @Test void testIdentifyConnectionNullTopology() throws SQLException { - rdsHostListProvider = Mockito.spy(getRdsHostListProvider( - mockHostListProviderService, - "jdbc:someprotocol://url")); + rdsHostListProvider = Mockito.spy(getRdsHostListProvider("jdbc:someprotocol://url")); rdsHostListProvider.clusterInstanceTemplate = new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) .host("?.pattern").build(); @@ -487,9 +457,7 @@ void testIdentifyConnectionHostNotInTopology() throws SQLException { .role(HostRole.WRITER) .build()); - rdsHostListProvider = Mockito.spy(getRdsHostListProvider( - mockHostListProviderService, - "jdbc:someprotocol://url")); + rdsHostListProvider = Mockito.spy(getRdsHostListProvider("jdbc:someprotocol://url")); when(mockResultSet.next()).thenReturn(true); when(mockResultSet.getString(eq(1))).thenReturn("instance-1"); doReturn(cachedTopology).when(rdsHostListProvider).refresh(mockConnection); @@ -508,9 +476,7 @@ void testIdentifyConnectionHostInTopology() throws SQLException { expectedHost.setHostId("instance-a-1"); final List cachedTopology = Collections.singletonList(expectedHost); - rdsHostListProvider = Mockito.spy(getRdsHostListProvider( - mockHostListProviderService, - "jdbc:someprotocol://url")); + rdsHostListProvider = Mockito.spy(getRdsHostListProvider("jdbc:someprotocol://url")); when(mockResultSet.next()).thenReturn(true); when(mockResultSet.getString(eq(1))).thenReturn("instance-a-1"); doReturn(cachedTopology).when(rdsHostListProvider).refresh(mockConnection); @@ -523,8 +489,7 @@ void testIdentifyConnectionHostInTopology() throws SQLException { @Test void testGetTopology_StaleRecord() throws SQLException { - rdsHostListProvider = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url")); + rdsHostListProvider = Mockito.spy(getRdsHostListProvider("jdbc:someprotocol://url")); rdsHostListProvider.isInitialized = true; final String hostName1 = "hostName1"; @@ -557,8 +522,7 @@ void testGetTopology_StaleRecord() throws SQLException { @Test void testGetTopology_InvalidLastUpdatedTimestamp() throws SQLException { - rdsHostListProvider = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url")); + rdsHostListProvider = Mockito.spy(getRdsHostListProvider("jdbc:someprotocol://url")); rdsHostListProvider.isInitialized = true; final String hostName = "hostName"; @@ -583,8 +547,7 @@ void testGetTopology_InvalidLastUpdatedTimestamp() throws SQLException { @Test void testGetTopology_returnsLatestWriter() throws SQLException { - rdsHostListProvider = Mockito.spy( - getRdsHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url")); + rdsHostListProvider = Mockito.spy(getRdsHostListProvider("jdbc:someprotocol://url")); rdsHostListProvider.isInitialized = true; HostSpec expectedWriterHost = new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) @@ -647,9 +610,7 @@ void testClusterUrlUsedAsDefaultClusterId() throws SQLException { String readerClusterUrl = "mycluster.cluster-ro-XYZ.us-east-1.rds.amazonaws.com"; String expectedClusterId = "mycluster.cluster-XYZ.us-east-1.rds.amazonaws.com:1234"; String connectionString = "jdbc:someprotocol://" + readerClusterUrl + ":1234/test"; - RdsHostListProvider provider1 = Mockito.spy(getRdsHostListProvider( - mockHostListProviderService, - connectionString)); + RdsHostListProvider provider1 = Mockito.spy(getRdsHostListProvider(connectionString)); assertEquals(expectedClusterId, provider1.getClusterId()); List mockTopology = @@ -659,9 +620,7 @@ void testClusterUrlUsedAsDefaultClusterId() throws SQLException { assertEquals(mockTopology, provider1.getStoredTopology()); verify(provider1, times(1)).queryForTopology(mockConnection); - RdsHostListProvider provider2 = Mockito.spy(getRdsHostListProvider( - mockHostListProviderService, - connectionString)); + RdsHostListProvider provider2 = Mockito.spy(getRdsHostListProvider(connectionString)); assertEquals(expectedClusterId, provider2.getClusterId()); assertEquals(mockTopology, provider2.getStoredTopology()); verify(provider2, never()).queryForTopology(mockConnection); diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java index 70d1fcca3..af9cc64f3 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java @@ -37,7 +37,6 @@ import java.sql.SQLException; import java.sql.SQLSyntaxErrorException; import java.sql.Statement; -import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -60,20 +59,20 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider.FetchTopologyResult; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.storage.Topology; class RdsMultiAzDbClusterListProviderTest { - - private final long defaultRefreshRateNano = TimeUnit.SECONDS.toNanos(5); private final StorageService storageService = new StorageServiceImpl(); private RdsMultiAzDbClusterListProvider rdsMazDbClusterHostListProvider; @Mock private Connection mockConnection; @Mock private Statement mockStatement; @Mock private ResultSet mockResultSet; + @Mock private ServiceContainer mockServiceContainer; @Mock private PluginService mockPluginService; @Mock private HostListProviderService mockHostListProviderService; @Mock Dialect mockTopologyAwareDialect; @@ -89,6 +88,8 @@ class RdsMultiAzDbClusterListProviderTest { @BeforeEach void setUp() throws SQLException { closeable = MockitoAnnotations.openMocks(this); + when(mockServiceContainer.getHostListProviderService()).thenReturn(mockHostListProviderService); + when(mockServiceContainer.getStorageService()).thenReturn(storageService); when(mockPluginService.getCurrentConnection()).thenReturn(mockConnection); when(mockPluginService.connect(any(HostSpec.class), any(Properties.class))).thenReturn(mockConnection); when(mockPluginService.getCurrentHostSpec()).thenReturn(currentHostSpec); @@ -115,14 +116,11 @@ void tearDown() throws Exception { closeable.close(); } - private RdsMultiAzDbClusterListProvider getRdsMazDbClusterHostListProvider( - HostListProviderService mockHostListProviderService, - String originalUrl) throws SQLException { + private RdsMultiAzDbClusterListProvider getRdsMazDbClusterHostListProvider(String originalUrl) throws SQLException { RdsMultiAzDbClusterListProvider provider = new RdsMultiAzDbClusterListProvider( new Properties(), originalUrl, - mockHostListProviderService, - storageService, + mockServiceContainer, "foo", "bar", "baz", @@ -135,13 +133,9 @@ private RdsMultiAzDbClusterListProvider getRdsMazDbClusterHostListProvider( @Test void testGetTopology_returnCachedTopology() throws SQLException { - rdsMazDbClusterHostListProvider = Mockito.spy( - getRdsMazDbClusterHostListProvider(mockHostListProviderService, "protocol://url/")); - - final Instant lastUpdated = Instant.now(); + rdsMazDbClusterHostListProvider = Mockito.spy(getRdsMazDbClusterHostListProvider("protocol://url/")); final List expected = hosts; - storageService.set( - ItemCategory.TOPOLOGY, rdsMazDbClusterHostListProvider.clusterId, new Topology(expected)); + storageService.set(ItemCategory.TOPOLOGY, rdsMazDbClusterHostListProvider.clusterId, new Topology(expected)); final FetchTopologyResult result = rdsMazDbClusterHostListProvider.getTopology(mockConnection, false); assertEquals(expected, result.hosts); @@ -151,8 +145,7 @@ void testGetTopology_returnCachedTopology() throws SQLException { @Test void testGetTopology_withForceUpdate_returnsUpdatedTopology() throws SQLException { - rdsMazDbClusterHostListProvider = Mockito.spy( - getRdsMazDbClusterHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url")); + rdsMazDbClusterHostListProvider = Mockito.spy(getRdsMazDbClusterHostListProvider("jdbc:someprotocol://url")); rdsMazDbClusterHostListProvider.isInitialized = true; storageService.set( @@ -170,8 +163,7 @@ void testGetTopology_withForceUpdate_returnsUpdatedTopology() throws SQLExceptio @Test void testGetTopology_noForceUpdate_queryReturnsEmptyHostList() throws SQLException { - rdsMazDbClusterHostListProvider = Mockito.spy( - getRdsMazDbClusterHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url")); + rdsMazDbClusterHostListProvider = Mockito.spy(getRdsMazDbClusterHostListProvider("jdbc:someprotocol://url")); rdsMazDbClusterHostListProvider.clusterId = "cluster-id"; rdsMazDbClusterHostListProvider.isInitialized = true; @@ -189,8 +181,7 @@ void testGetTopology_noForceUpdate_queryReturnsEmptyHostList() throws SQLExcepti @Test void testGetTopology_withForceUpdate_returnsInitialHostList() throws SQLException { - rdsMazDbClusterHostListProvider = Mockito.spy( - getRdsMazDbClusterHostListProvider(mockHostListProviderService, "jdbc:someprotocol://url")); + rdsMazDbClusterHostListProvider = Mockito.spy(getRdsMazDbClusterHostListProvider("jdbc:someprotocol://url")); rdsMazDbClusterHostListProvider.clear(); doReturn(new ArrayList<>()).when(rdsMazDbClusterHostListProvider).queryForTopology(mockConnection); @@ -199,14 +190,13 @@ void testGetTopology_withForceUpdate_returnsInitialHostList() throws SQLExceptio verify(rdsMazDbClusterHostListProvider, atMostOnce()).queryForTopology(mockConnection); assertNotNull(result.hosts); assertEquals( - Arrays.asList(new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("url").build()), + Collections.singletonList(new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("url").build()), result.hosts); } @Test void testQueryForTopology_queryResultsInException() throws SQLException { - rdsMazDbClusterHostListProvider = - getRdsMazDbClusterHostListProvider(mockHostListProviderService, "protocol://url/"); + rdsMazDbClusterHostListProvider = getRdsMazDbClusterHostListProvider("protocol://url/"); when(mockStatement.executeQuery(queryCaptor.capture())).thenThrow(new SQLSyntaxErrorException()); assertThrows( @@ -216,8 +206,7 @@ void testQueryForTopology_queryResultsInException() throws SQLException { @Test void testGetCachedTopology_returnCachedTopology() throws SQLException { - rdsMazDbClusterHostListProvider = getRdsMazDbClusterHostListProvider( - mockHostListProviderService, "jdbc:someprotocol://url"); + rdsMazDbClusterHostListProvider = getRdsMazDbClusterHostListProvider("jdbc:someprotocol://url"); final List expected = hosts; storageService.set( @@ -231,9 +220,8 @@ void testGetCachedTopology_returnCachedTopology() throws SQLException { void testTopologyCache_NoSuggestedClusterId() throws SQLException { RdsMultiAzDbClusterListProvider.clearAll(); - RdsMultiAzDbClusterListProvider provider1 = Mockito.spy( - getRdsMazDbClusterHostListProvider(mockHostListProviderService, - "jdbc:something://cluster-a.domain.com/")); + RdsMultiAzDbClusterListProvider provider1 = + Mockito.spy(getRdsMazDbClusterHostListProvider("jdbc:something://cluster-a.domain.com/")); provider1.init(); final List topologyClusterA = Arrays.asList( new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) @@ -251,9 +239,8 @@ void testTopologyCache_NoSuggestedClusterId() throws SQLException { final List topologyProvider1 = provider1.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); - RdsMultiAzDbClusterListProvider provider2 = Mockito.spy( - getRdsMazDbClusterHostListProvider(mockHostListProviderService, - "jdbc:something://cluster-b.domain.com/")); + RdsMultiAzDbClusterListProvider provider2 = + Mockito.spy(getRdsMazDbClusterHostListProvider("jdbc:something://cluster-b.domain.com/")); provider2.init(); assertNull(provider2.getStoredTopology()); @@ -276,8 +263,8 @@ void testTopologyCache_NoSuggestedClusterId() throws SQLException { void testTopologyCache_SuggestedClusterIdForRds() throws SQLException { RdsMultiAzDbClusterListProvider.clearAll(); - RdsMultiAzDbClusterListProvider provider1 = Mockito.spy( - getRdsMazDbClusterHostListProvider(mockHostListProviderService, + RdsMultiAzDbClusterListProvider provider1 = + Mockito.spy(getRdsMazDbClusterHostListProvider( "jdbc:something://cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com/")); provider1.init(); final List topologyClusterA = Arrays.asList( @@ -304,8 +291,8 @@ void testTopologyCache_SuggestedClusterIdForRds() throws SQLException { final List topologyProvider1 = provider1.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); - RdsMultiAzDbClusterListProvider provider2 = Mockito.spy( - getRdsMazDbClusterHostListProvider(mockHostListProviderService, + RdsMultiAzDbClusterListProvider provider2 = + Mockito.spy(getRdsMazDbClusterHostListProvider( "jdbc:something://cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com/")); provider2.init(); @@ -323,8 +310,8 @@ void testTopologyCache_SuggestedClusterIdForRds() throws SQLException { void testTopologyCache_SuggestedClusterIdForInstance() throws SQLException { RdsMultiAzDbClusterListProvider.clearAll(); - RdsMultiAzDbClusterListProvider provider1 = Mockito.spy( - getRdsMazDbClusterHostListProvider(mockHostListProviderService, + RdsMultiAzDbClusterListProvider provider1 = + Mockito.spy(getRdsMazDbClusterHostListProvider( "jdbc:something://cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com/")); provider1.init(); final List topologyClusterA = Arrays.asList( @@ -351,8 +338,8 @@ void testTopologyCache_SuggestedClusterIdForInstance() throws SQLException { final List topologyProvider1 = provider1.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); - RdsMultiAzDbClusterListProvider provider2 = Mockito.spy( - getRdsMazDbClusterHostListProvider(mockHostListProviderService, + RdsMultiAzDbClusterListProvider provider2 = + Mockito.spy(getRdsMazDbClusterHostListProvider( "jdbc:something://instance-a-3.xyz.us-east-2.rds.amazonaws.com/")); provider2.init(); @@ -370,8 +357,8 @@ void testTopologyCache_SuggestedClusterIdForInstance() throws SQLException { void testTopologyCache_AcceptSuggestion() throws SQLException { RdsMultiAzDbClusterListProvider.clearAll(); - RdsMultiAzDbClusterListProvider provider1 = Mockito.spy( - getRdsMazDbClusterHostListProvider(mockHostListProviderService, + RdsMultiAzDbClusterListProvider provider1 = + Mockito.spy(getRdsMazDbClusterHostListProvider( "jdbc:something://instance-a-2.xyz.us-east-2.rds.amazonaws.com/")); provider1.init(); final List topologyClusterA = Arrays.asList( @@ -400,8 +387,8 @@ void testTopologyCache_AcceptSuggestion() throws SQLException { // RdsMultiAzDbClusterListProvider.logCache(); - RdsMultiAzDbClusterListProvider provider2 = Mockito.spy( - getRdsMazDbClusterHostListProvider(mockHostListProviderService, + RdsMultiAzDbClusterListProvider provider2 = + Mockito.spy(getRdsMazDbClusterHostListProvider( "jdbc:something://cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com/")); provider2.init(); @@ -430,9 +417,7 @@ void testTopologyCache_AcceptSuggestion() throws SQLException { @Test void testIdentifyConnectionWithInvalidNodeIdQuery() throws SQLException { - rdsMazDbClusterHostListProvider = Mockito.spy(getRdsMazDbClusterHostListProvider( - mockHostListProviderService, - "jdbc:someprotocol://url")); + rdsMazDbClusterHostListProvider = Mockito.spy(getRdsMazDbClusterHostListProvider("jdbc:someprotocol://url")); when(mockResultSet.next()).thenReturn(false); assertThrows(SQLException.class, () -> rdsMazDbClusterHostListProvider.identifyConnection(mockConnection)); @@ -443,9 +428,7 @@ void testIdentifyConnectionWithInvalidNodeIdQuery() throws SQLException { @Test void testIdentifyConnectionNullTopology() throws SQLException { - rdsMazDbClusterHostListProvider = Mockito.spy(getRdsMazDbClusterHostListProvider( - mockHostListProviderService, - "jdbc:someprotocol://url")); + rdsMazDbClusterHostListProvider = Mockito.spy(getRdsMazDbClusterHostListProvider("jdbc:someprotocol://url")); rdsMazDbClusterHostListProvider.clusterInstanceTemplate = new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) .host("?.pattern").build(); @@ -466,9 +449,7 @@ void testIdentifyConnectionHostNotInTopology() throws SQLException { .role(HostRole.WRITER) .build()); - rdsMazDbClusterHostListProvider = Mockito.spy(getRdsMazDbClusterHostListProvider( - mockHostListProviderService, - "jdbc:someprotocol://url")); + rdsMazDbClusterHostListProvider = Mockito.spy(getRdsMazDbClusterHostListProvider("jdbc:someprotocol://url")); when(mockResultSet.next()).thenReturn(true); when(mockResultSet.getString(eq(1))).thenReturn("instance-1"); doReturn(cachedTopology).when(rdsMazDbClusterHostListProvider).refresh(mockConnection); @@ -487,9 +468,7 @@ void testIdentifyConnectionHostInTopology() throws SQLException { .build(); final List cachedTopology = Collections.singletonList(expectedHost); - rdsMazDbClusterHostListProvider = Mockito.spy(getRdsMazDbClusterHostListProvider( - mockHostListProviderService, - "jdbc:someprotocol://url")); + rdsMazDbClusterHostListProvider = Mockito.spy(getRdsMazDbClusterHostListProvider("jdbc:someprotocol://url")); when(mockResultSet.next()).thenReturn(true); when(mockResultSet.getString(eq(1))).thenReturn("instance-a-1"); doReturn(cachedTopology).when(rdsMazDbClusterHostListProvider).refresh(mockConnection); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java index ebcdf595d..70e84b26b 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java @@ -36,6 +36,7 @@ import java.sql.SQLException; import java.util.Properties; import java.util.stream.Stream; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -71,6 +72,8 @@ import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Pair; +import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.ServiceContainerImpl; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.telemetry.GaugeCallable; @@ -79,6 +82,7 @@ import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryGauge; +@SuppressWarnings("resource") public class AwsSecretsManagerConnectionPluginTest { private static final String TEST_PG_PROTOCOL = "jdbc:aws-wrapper:postgresql:"; @@ -105,6 +109,7 @@ public class AwsSecretsManagerConnectionPluginTest { GetSecretValueResponse.builder().secretString(INVALID_SECRET_STRING).build(); private static final Properties TEST_PROPS = new Properties(); private static final StorageService storageService = new StorageServiceImpl(); + private static ServiceContainer serviceContainer; private AwsSecretsManagerConnectionPlugin plugin; private AutoCloseable closeable; @@ -143,6 +148,7 @@ public void init() throws SQLException { // noinspection unchecked when(mockTelemetryFactory.createGauge(anyString(), any(GaugeCallable.class))).thenReturn(mockTelemetryGauge); + serviceContainer = new ServiceContainerImpl(storageService, mockConnectionPluginManager, mockTelemetryFactory); this.plugin = new AwsSecretsManagerConnectionPlugin( mockService, TEST_PROPS, @@ -244,17 +250,7 @@ public void testConnectWithNewSecretsAfterTryingWithCachedSecrets( String protocol, ExceptionHandler exceptionHandler) throws SQLException { this.plugin = new AwsSecretsManagerConnectionPlugin( - new PluginServiceImpl( - mockConnectionPluginManager, - new ExceptionManager(), - TEST_PROPS, - "url", - protocol, - mockDialectManager, - mockTargetDriverDialect, - storageService, - configurationProfile, - mockSessionStateService), + getPluginService(protocol), TEST_PROPS, (host, r) -> mockSecretsManagerClient, (id) -> mockGetValueRequest); @@ -286,6 +282,19 @@ public void testConnectWithNewSecretsAfterTryingWithCachedSecrets( assertEquals(TEST_PASSWORD, TEST_PROPS.get(PropertyDefinition.PASSWORD.name)); } + private @NotNull PluginServiceImpl getPluginService(String protocol) throws SQLException { + return new PluginServiceImpl( + serviceContainer, + new ExceptionManager(), + TEST_PROPS, + "url", + protocol, + mockDialectManager, + mockTargetDriverDialect, + configurationProfile, + mockSessionStateService); + } + /** * The plugin will attempt to open a connection after fetching a secret, but it will fail because the returned secret * could not be parsed. @@ -345,17 +354,7 @@ public void testFailedToGetSecrets() throws SQLException { @ValueSource(strings = {"28000", "28P01"}) public void testFailedInitialConnectionWithWrappedGenericError(final String accessError) throws SQLException { this.plugin = new AwsSecretsManagerConnectionPlugin( - new PluginServiceImpl( - mockConnectionPluginManager, - new ExceptionManager(), - TEST_PROPS, - "url", - TEST_PG_PROTOCOL, - mockDialectManager, - mockTargetDriverDialect, - storageService, - configurationProfile, - mockSessionStateService), + getPluginService(TEST_PG_PROTOCOL), TEST_PROPS, (host, r) -> mockSecretsManagerClient, (id) -> mockGetValueRequest); @@ -388,17 +387,7 @@ public void testFailedInitialConnectionWithWrappedGenericError(final String acce @Test public void testConnectWithWrappedMySQLException() throws SQLException { this.plugin = new AwsSecretsManagerConnectionPlugin( - new PluginServiceImpl( - mockConnectionPluginManager, - new ExceptionManager(), - TEST_PROPS, - "url", - TEST_MYSQL_PROTOCOL, - mockDialectManager, - mockTargetDriverDialect, - storageService, - configurationProfile, - mockSessionStateService), + getPluginService(TEST_MYSQL_PROTOCOL), TEST_PROPS, (host, r) -> mockSecretsManagerClient, (id) -> mockGetValueRequest); @@ -430,17 +419,7 @@ public void testConnectWithWrappedMySQLException() throws SQLException { @Test public void testConnectWithWrappedPostgreSQLException() throws SQLException { this.plugin = new AwsSecretsManagerConnectionPlugin( - new PluginServiceImpl( - mockConnectionPluginManager, - new ExceptionManager(), - TEST_PROPS, - "url", - TEST_PG_PROTOCOL, - mockDialectManager, - mockTargetDriverDialect, - storageService, - configurationProfile, - mockSessionStateService), + getPluginService(TEST_PG_PROTOCOL), TEST_PROPS, (host, r) -> mockSecretsManagerClient, (id) -> mockGetValueRequest); @@ -478,8 +457,7 @@ public void testConnectViaARN(final String arn, final Region expectedRegionParse SECRET_ID_PROPERTY.set(props, arn); this.plugin = spy(new AwsSecretsManagerConnectionPlugin( - new PluginServiceImpl( - mockConnectionPluginManager, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect, storageService), + new PluginServiceImpl(serviceContainer, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect), props, (host, r) -> mockSecretsManagerClient, (id) -> mockGetValueRequest)); @@ -499,8 +477,7 @@ public void testConnectionWithRegionParameterAndARN(final String arn, final Regi REGION_PROPERTY.set(props, expectedRegion.toString()); this.plugin = spy(new AwsSecretsManagerConnectionPlugin( - new PluginServiceImpl( - mockConnectionPluginManager, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect, storageService), + new PluginServiceImpl(serviceContainer, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect), props, (host, r) -> mockSecretsManagerClient, (id) -> mockGetValueRequest)); From bd92538cb307de1d738f00c16e7d5539b63cdcf9 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 26 Feb 2025 10:01:21 -0800 Subject: [PATCH 038/149] Cleanup --- .../src/main/java/software/amazon/jdbc/PluginServiceImpl.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index a39ba2907..827491540 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -139,10 +139,6 @@ public PluginServiceImpl( @Nullable final ConfigurationProfile configurationProfile, @Nullable final SessionStateService sessionStateService) throws SQLException { this.serviceContainer = serviceContainer; - this.serviceContainer.setHostListProviderService(this); - this.serviceContainer.setPluginService(this); - this.serviceContainer.setPluginManagerService(this); - this.pluginManager = serviceContainer.getConnectionPluginManager(); this.props = props; this.originalUrl = originalUrl; From 0c2b54847d0b9480c08c4bf1f0cbbfaedbe27310 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 27 Feb 2025 13:25:30 -0800 Subject: [PATCH 039/149] MonitorServiceImpl wip --- .../jdbc/util/monitoring/MonitorService.java | 42 ++++--- .../util/monitoring/MonitorServiceImpl.java | 114 ++++++++++++++++++ .../jdbc/util/storage/ExpirationCache.java | 2 +- .../jdbc/util/storage/StorageServiceImpl.java | 13 +- ..._advanced_jdbc_wrapper_messages.properties | 5 +- 5 files changed, 144 insertions(+), 32 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 464d8a339..fd8a4cecd 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -21,55 +21,57 @@ import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.util.ShouldDisposeFunc; +// TODO: fix javadocs (eg monitorType -> monitorClass) public interface MonitorService { /** * Register a new monitor type with the monitor service. This method needs to be called before adding new types of - * monitors to the monitor service, so that the monitor service knows when a running monitor should be stopped. + * monitors to the monitor service, so that the monitor service knows when to dispose of a monitor. * Expected monitor types ("topology" and "customEndpoint") will be added automatically during driver initialization, * but this method can be called by users if they want to add a new monitor type. * - * @param monitorType a String representing the monitor type, eg "customEndpoint". - * @param errorResponses a Set defining actions to take if the monitor is in an error state. - * @param expirationTimeNs how long a monitor should be stored before expiring, in nanoseconds. If the monitor is - * expired and shouldDisposeFunc returns `true`, the monitor will be stopped. - * @param shouldDisposeFunc a function defining whether an item should be stopped if expired. If `null` is passed, the - * monitor will always be stopped if the monitor is expired. + * @param monitorClass the class of the monitor, eg `CustomEndpointMonitorImpl.class`. + * @param errorResponses a Set defining actions to take if the monitor is in an error state. + * @param timeToLiveNanos how long a monitor should be stored before expiring, in nanoseconds. If the monitor is + * expired and shouldDisposeFunc returns `true`, the monitor will be stopped. + * @param shouldDisposeFunc a function defining whether an item should be stopped if expired. If `null` is passed, the + * monitor will always be stopped if the monitor is expired. */ - void registerMonitorTypeIfAbsent( - String monitorType, + void registerMonitorTypeIfAbsent( + Class monitorClass, Set errorResponses, - long expirationTimeNs, - @Nullable ShouldDisposeFunc shouldDisposeFunc); + long cleanupIntervalNanos, + long timeToLiveNanos, + @Nullable ShouldDisposeFunc shouldDisposeFunc); /** * Creates and starts the given monitor if it does not already exist and stores it under the given monitor type and * key. * - * @param monitorType a String representing the monitor type, eg "customEndpoint". + * @param monitorClass a String representing the monitor type, eg "customEndpoint". * @param key the key for the monitor, eg * "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". - * @param monitorSupplier an initialization lambda that can be used to create the monitor if it is absent. + * @param monitorSupplier a supplier lambda that can be used to create the monitor if it is absent. */ - void runIfAbsent( - String monitorType, + void runIfAbsent( + Class monitorClass, Object key, - Supplier monitorSupplier); + Supplier monitorSupplier); /** * Stops the given monitor and removes it from the monitor service. * - * @param monitorType a String representing the monitor type, eg "customEndpoint". + * @param monitorClass a String representing the monitor type, eg "customEndpoint". * @param key the key for the monitor, eg * "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". */ - void stopAndRemove(String monitorType, Object key); + void stopAndRemove(Class monitorClass, Object key); /** * Stops all monitors for the given type and removes them from the monitor service. * - * @param monitorType a String representing the monitor type, eg "customEndpoint". + * @param monitorClass a String representing the monitor type, eg "customEndpoint". */ - void stopAndRemoveMonitors(String monitorType); + void stopAndRemoveMonitors(Class monitorClass); /** * Stops all monitors and removes them from the monitor service. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java new file mode 100644 index 000000000..cb60b482f --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -0,0 +1,114 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import java.util.logging.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; +import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ShouldDisposeFunc; +import software.amazon.jdbc.util.storage.ExpirationCache; + +public class MonitorServiceImpl implements MonitorService { + private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); + protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(1); + protected static final Map, Set> errorResponsesByType = new ConcurrentHashMap<>(); + protected static final Map, ExpirationCache> monitorCaches = new ConcurrentHashMap<>(); + + @Override + public void registerMonitorTypeIfAbsent( + Class monitorClass, + Set errorResponses, + long cleanupIntervalNanos, long timeToLiveNanos, + @Nullable ShouldDisposeFunc shouldDisposeFunc) { + monitorCaches.computeIfAbsent( + monitorClass, + mc -> { + errorResponsesByType.putIfAbsent(monitorClass, errorResponses); + return new ExpirationCache<>( + monitorClass, + true, + DEFAULT_CLEANUP_INTERVAL_NANOS, + timeToLiveNanos, + null, + null + ); + }); + } + + @Override + public void runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier) { + ExpirationCache cache = monitorCaches.get(monitorClass); + if (cache.getValueClass() != monitorClass) { + throw new IllegalArgumentException( + Messages.get( + "MonitorServiceImpl.incorrectValueType", + new Object[] {cache.getValueClass(), monitorClass})); + } + + ExpirationCache typedCache = (ExpirationCache) cache; + typedCache.computeIfAbsent( + key, + k -> { + T monitor = monitorSupplier.get(); + monitor.start(); + return monitor; + }); + } + + @Override + public void stopAndRemove(Class monitorClass, Object key) { + ExpirationCache cache = monitorCaches.get(monitorClass); + if (cache == null) { + return; + } + + Object result = cache.remove(key); + if (result instanceof Monitor) { + Monitor monitor = (Monitor) result; + monitor.stop(); + } + } + + @Override + public void stopAndRemoveMonitors(Class monitorClass) { + ExpirationCache cache = monitorCaches.get(monitorClass); + if (cache == null) { + return; + } + + for (Object value : cache.getEntries().values()) { + if (value instanceof Monitor) { + Monitor monitor = (Monitor) value; + monitor.stop(); + } + } + + cache.clear(); + } + + @Override + public void stopAndRemoveAll() { + for (Class monitorClass : errorResponsesByType.keySet()) { + stopAndRemoveMonitors(monitorClass); + } + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index 1278d4a05..02a9783e5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -38,7 +38,7 @@ public class ExpirationCache { protected final ShouldDisposeFunc shouldDisposeFunc; protected final ItemDisposalFunc itemDisposalFunc; - ExpirationCache( + public ExpirationCache( final Class valueClass, final boolean isRenewableExpiration, final long cleanupIntervalNanos, diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 2e56ac86d..dc82a2a25 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -104,6 +104,7 @@ public void registerItemCategoryIfAbsent( Class itemClass, boolean isRenewableExpiration, long timeToLiveNanos, + // TODO: should we make all caches use a default cleanup interval to simplify the API/implementation? long cleanupIntervalNanos, @Nullable ShouldDisposeFunc shouldDisposeFunc, @Nullable ItemDisposalFunc itemDisposalFunc) { @@ -122,7 +123,6 @@ public void registerItemCategoryIfAbsent( } @Override - @SuppressWarnings("unchecked") public void set(String itemCategory, Object key, V value) { ExpirationCache cache = caches.get(itemCategory); if (cache == null) { @@ -142,15 +142,8 @@ public void set(String itemCategory, Object key, V value) { new Object[] {itemCategory, cache.getValueClass(), value.getClass(), value})); } - try { - ExpirationCache typedCache = (ExpirationCache) cache; - typedCache.put(key, value); - } catch (ClassCastException e) { - throw new IllegalArgumentException( - Messages.get( - "StorageServiceImpl.incorrectValueType", - new Object[]{itemCategory, cache.getValueClass(), value.getClass(), value})); - } + ExpirationCache typedCache = (ExpirationCache) cache; + typedCache.put(key, value); } @Override diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 8e052b00f..649424be8 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -288,9 +288,12 @@ MonitorImpl.stopMonitoringThreadNewContext=Stop monitoring thread for checking n MonitorImpl.startMonitoringThread=Start monitoring thread for {0}. MonitorImpl.stopMonitoringThread=Stop monitoring thread for {0}. -# Monitor Service Impl +# efm.MonitorServiceImpl MonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should not be empty. +# monitoring.MonitorServiceImpl +MonitorServiceImpl.incorrectValueType= + NodeMonitoringThread.detectedWriter=Writer detected by node monitoring thread: ''{0}''. NodeMonitoringThread.invalidWriterQuery=The writer topology query is invalid: {0} NodeMonitoringThread.threadCompleted=Node monitoring thread completed in {0} ms. From e7f0ae2c3b4f6961516db9c6f6e84fff8ece7f82 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 28 Feb 2025 13:09:41 -0800 Subject: [PATCH 040/149] Make CacheItem protected --- .../java/software/amazon/jdbc/util/storage/ExpirationCache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index 02a9783e5..fd396df62 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -267,7 +267,7 @@ protected class CacheItem { * @param item the item value * @param expirationTimeNanos the amount of time before a CacheItem should be marked as expired. */ - public CacheItem(final V item, final long expirationTimeNanos) { + protected CacheItem(final V item, final long expirationTimeNanos) { this.item = item; this.expirationTimeNanos = expirationTimeNanos; } From 1ece19d49ca3a96b96f5580cbb5171cefe997d7d Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 28 Feb 2025 13:21:57 -0800 Subject: [PATCH 041/149] Remove cleanupInterval from ExpirationCache --- .../util/monitoring/MonitorServiceImpl.java | 1 - .../jdbc/util/storage/ExpirationCache.java | 7 ----- .../util/storage/ExpirationCacheBuilder.java | 7 ----- .../jdbc/util/storage/StorageService.java | 2 -- .../jdbc/util/storage/StorageServiceImpl.java | 30 +++++-------------- .../RdsMultiAzDbClusterListProviderTest.java | 1 - 6 files changed, 8 insertions(+), 40 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index cb60b482f..084fbd915 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -46,7 +46,6 @@ public void registerMonitorTypeIfAbsent( return new ExpirationCache<>( monitorClass, true, - DEFAULT_CLEANUP_INTERVAL_NANOS, timeToLiveNanos, null, null diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index fd396df62..473d4c624 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -33,7 +33,6 @@ public class ExpirationCache { protected final Map cache = new ConcurrentHashMap<>(); protected final Class valueClass; protected final boolean isRenewableExpiration; - protected final long cleanupIntervalNanos; protected final long timeToLiveNanos; protected final ShouldDisposeFunc shouldDisposeFunc; protected final ItemDisposalFunc itemDisposalFunc; @@ -41,22 +40,16 @@ public class ExpirationCache { public ExpirationCache( final Class valueClass, final boolean isRenewableExpiration, - final long cleanupIntervalNanos, final long timeToLiveNanos, final @Nullable ShouldDisposeFunc shouldDisposeFunc, final @Nullable ItemDisposalFunc itemDisposalFunc) { this.valueClass = valueClass; this.isRenewableExpiration = isRenewableExpiration; - this.cleanupIntervalNanos = cleanupIntervalNanos; this.timeToLiveNanos = timeToLiveNanos; this.shouldDisposeFunc = shouldDisposeFunc; this.itemDisposalFunc = itemDisposalFunc; } - public long getCleanupIntervalNanos() { - return cleanupIntervalNanos; - } - /** * If a value does not exist for the given key or the existing value is expired and non-renewable, stores the value * returned by the given mapping function, unless the function returns null, in which case the key will be removed. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCacheBuilder.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCacheBuilder.java index 5afc781a6..1c78d52c8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCacheBuilder.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCacheBuilder.java @@ -25,7 +25,6 @@ public class ExpirationCacheBuilder { protected @NonNull Class valueClass; protected boolean isRenewableExpiration = false; - protected long cleanupIntervalNanos = TimeUnit.MINUTES.toNanos(10); protected long timeToLiveNanos = TimeUnit.MINUTES.toNanos(5); protected @Nullable ShouldDisposeFunc shouldDisposeFunc; protected @Nullable ItemDisposalFunc itemDisposalFunc; @@ -39,11 +38,6 @@ public ExpirationCacheBuilder withIsRenewableExpiration(boolean isRenewableEx return this; } - public ExpirationCacheBuilder withCleanupIntervalNanos(long cleanupIntervalNanos) { - this.cleanupIntervalNanos = cleanupIntervalNanos; - return this; - } - public ExpirationCacheBuilder withTimeToLiveNanos(long timeToLiveNanos) { this.timeToLiveNanos = timeToLiveNanos; return this; @@ -63,7 +57,6 @@ public ExpirationCache build() { return new ExpirationCache<>( this.valueClass, this.isRenewableExpiration, - this.cleanupIntervalNanos, this.timeToLiveNanos, this.shouldDisposeFunc, this.itemDisposalFunc); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java index 3ed600e08..be2b9ac54 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java @@ -33,7 +33,6 @@ public interface StorageService { * `CustomEndpointInfo.class`. * @param isRenewableExpiration controls whether the item's expiration should be renewed if the item is fetched, * regardless of whether it is already expired or not. - * @param cleanupIntervalNanos how often the item category should be cleaned of expired entries, in nanoseconds. * @param timeToLiveNanos how long an item should be stored before being considered expired, in nanoseconds. * @param shouldDisposeFunc a function defining whether an item should be disposed if expired. If null is passed, * the item will always be disposed if expired. @@ -45,7 +44,6 @@ void registerItemCategoryIfAbsent( String itemCategory, Class itemClass, boolean isRenewableExpiration, - long cleanupIntervalNanos, long timeToLiveNanos, @Nullable ShouldDisposeFunc shouldDisposeFunc, @Nullable ItemDisposalFunc itemDisposalFunc); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index dc82a2a25..9a14eee25 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -35,12 +35,11 @@ public class StorageServiceImpl implements StorageService { private static final Logger LOGGER = Logger.getLogger(StorageServiceImpl.class.getName()); - protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(1); + protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(5); protected static final Map> caches = new ConcurrentHashMap<>(); protected static final Map>> defaultCacheSuppliers; protected static final AtomicBoolean isInitialized = new AtomicBoolean(false); protected static final ReentrantLock initLock = new ReentrantLock(); - protected static final Map cleanupTimes = new ConcurrentHashMap<>(); protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { final Thread thread = new Thread(r); thread.setDaemon(true); @@ -86,15 +85,8 @@ protected void initCleanupThread(long cleanupIntervalNanos) { protected void removeExpiredItems() { LOGGER.finest(Messages.get("StorageServiceImpl.removeExpiredItems")); - for (Map.Entry> entry : caches.entrySet()) { - String category = entry.getKey(); - ExpirationCache cache = entry.getValue(); - if (System.nanoTime() < cleanupTimes.get(category)) { - continue; - } - + for (ExpirationCache cache : caches.values()) { cache.removeExpiredEntries(); - cleanupTimes.put(category, System.nanoTime() + cache.getCleanupIntervalNanos()); } } @@ -104,22 +96,16 @@ public void registerItemCategoryIfAbsent( Class itemClass, boolean isRenewableExpiration, long timeToLiveNanos, - // TODO: should we make all caches use a default cleanup interval to simplify the API/implementation? - long cleanupIntervalNanos, @Nullable ShouldDisposeFunc shouldDisposeFunc, @Nullable ItemDisposalFunc itemDisposalFunc) { caches.computeIfAbsent( itemCategory, - category -> { - cleanupTimes.put(category, System.nanoTime() + cleanupIntervalNanos); - return new ExpirationCache<>( - itemClass, - isRenewableExpiration, - timeToLiveNanos, - cleanupIntervalNanos, - shouldDisposeFunc, - itemDisposalFunc); - }); + category -> new ExpirationCache<>( + itemClass, + isRenewableExpiration, + timeToLiveNanos, + shouldDisposeFunc, + itemDisposalFunc)); } @Override diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java index af9cc64f3..8b5fcccba 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java @@ -103,7 +103,6 @@ void setUp() throws SQLException { ItemCategory.TOPOLOGY, Topology.class, false, - TimeUnit.MINUTES.toNanos(10), TimeUnit.MINUTES.toNanos(5), null, null); From f4390cadf383131e058b8b4d3c97babeaaf1dca2 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 28 Feb 2025 14:42:11 -0800 Subject: [PATCH 042/149] MonitorServiceImpl with MonitorItem --- .../util/monitoring/MonitorServiceImpl.java | 54 +++++++++++----- .../jdbc/util/storage/ExpirationCache.java | 19 ++---- .../util/storage/ExpirationCacheBuilder.java | 64 ------------------- .../jdbc/util/storage/StorageServiceImpl.java | 28 ++++---- ..._advanced_jdbc_wrapper_messages.properties | 2 +- 5 files changed, 60 insertions(+), 107 deletions(-) delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCacheBuilder.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 084fbd915..295f3f988 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -31,7 +31,7 @@ public class MonitorServiceImpl implements MonitorService { private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(1); protected static final Map, Set> errorResponsesByType = new ConcurrentHashMap<>(); - protected static final Map, ExpirationCache> monitorCaches = new ConcurrentHashMap<>(); + protected static final Map, ExpirationCache>> monitorCaches = new ConcurrentHashMap<>(); @Override public void registerMonitorTypeIfAbsent( @@ -44,7 +44,6 @@ public void registerMonitorTypeIfAbsent( mc -> { errorResponsesByType.putIfAbsent(monitorClass, errorResponses); return new ExpirationCache<>( - monitorClass, true, timeToLiveNanos, null, @@ -55,21 +54,20 @@ public void registerMonitorTypeIfAbsent( @Override public void runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier) { - ExpirationCache cache = monitorCaches.get(monitorClass); - if (cache.getValueClass() != monitorClass) { - throw new IllegalArgumentException( - Messages.get( - "MonitorServiceImpl.incorrectValueType", - new Object[] {cache.getValueClass(), monitorClass})); + ExpirationCache> cache = monitorCaches.get(monitorClass); + if (cache == null) { + throw new IllegalStateException( + Messages.get("MonitorServiceImpl.monitorClassNotRegistered", new Object[] {monitorClass})); } - ExpirationCache typedCache = (ExpirationCache) cache; + ExpirationCache> typedCache = + (ExpirationCache>) (ExpirationCache) cache; typedCache.computeIfAbsent( key, k -> { T monitor = monitorSupplier.get(); monitor.start(); - return monitor; + return new MonitorItem<>(monitorSupplier, monitor); }); } @@ -81,9 +79,12 @@ public void stopAndRemove(Class monitorClass, Object key) } Object result = cache.remove(key); - if (result instanceof Monitor) { - Monitor monitor = (Monitor) result; - monitor.stop(); + if (result instanceof MonitorItem) { + MonitorItem monitorItem = (MonitorItem) result; + Monitor monitor = monitorItem.getMonitor(); + if (monitor != null) { + monitor.stop(); + } } } @@ -95,9 +96,12 @@ public void stopAndRemoveMonitors(Class monitorClass) { } for (Object value : cache.getEntries().values()) { - if (value instanceof Monitor) { - Monitor monitor = (Monitor) value; - monitor.stop(); + if (value instanceof MonitorItem) { + MonitorItem monitorItem = (MonitorItem) value; + Monitor monitor = monitorItem.getMonitor(); + if (monitor != null) { + monitor.stop(); + } } } @@ -110,4 +114,22 @@ public void stopAndRemoveAll() { stopAndRemoveMonitors(monitorClass); } } + + protected static class MonitorItem { + private final Supplier monitorSupplier; + private final T monitor; + + protected MonitorItem(Supplier monitorSupplier, T monitor) { + this.monitorSupplier = monitorSupplier; + this.monitor = monitor; + } + + public Supplier getMonitorSupplier() { + return monitorSupplier; + } + + public T getMonitor() { + return monitor; + } + } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index 473d4c624..1e4ee46ee 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -21,6 +21,7 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; @@ -29,21 +30,22 @@ public class ExpirationCache { private static final Logger LOGGER = Logger.getLogger(ExpirationCache.class.getName()); - + protected static final long DEFAULT_TIME_TO_LIVE_NANOS = TimeUnit.MINUTES.toNanos(5); protected final Map cache = new ConcurrentHashMap<>(); - protected final Class valueClass; protected final boolean isRenewableExpiration; protected final long timeToLiveNanos; protected final ShouldDisposeFunc shouldDisposeFunc; protected final ItemDisposalFunc itemDisposalFunc; + public ExpirationCache() { + this(false, DEFAULT_TIME_TO_LIVE_NANOS, null, null); + } + public ExpirationCache( - final Class valueClass, final boolean isRenewableExpiration, final long timeToLiveNanos, final @Nullable ShouldDisposeFunc shouldDisposeFunc, final @Nullable ItemDisposalFunc itemDisposalFunc) { - this.valueClass = valueClass; this.isRenewableExpiration = isRenewableExpiration; this.timeToLiveNanos = timeToLiveNanos; this.shouldDisposeFunc = shouldDisposeFunc; @@ -241,15 +243,6 @@ public int size() { return this.cache.size(); } - /** - * Get the class of the values stored in the cache. - * - * @return the class of the values stored in the cache - */ - public Class getValueClass() { - return this.valueClass; - } - protected class CacheItem { private final V item; private long expirationTimeNanos; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCacheBuilder.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCacheBuilder.java deleted file mode 100644 index 1c78d52c8..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCacheBuilder.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.util.storage; - -import java.util.concurrent.TimeUnit; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import software.amazon.jdbc.util.ItemDisposalFunc; -import software.amazon.jdbc.util.ShouldDisposeFunc; - -public class ExpirationCacheBuilder { - protected @NonNull Class valueClass; - protected boolean isRenewableExpiration = false; - protected long timeToLiveNanos = TimeUnit.MINUTES.toNanos(5); - protected @Nullable ShouldDisposeFunc shouldDisposeFunc; - protected @Nullable ItemDisposalFunc itemDisposalFunc; - - public ExpirationCacheBuilder(@NonNull Class valueClass) { - this.valueClass = valueClass; - } - - public ExpirationCacheBuilder withIsRenewableExpiration(boolean isRenewableExpiration) { - this.isRenewableExpiration = isRenewableExpiration; - return this; - } - - public ExpirationCacheBuilder withTimeToLiveNanos(long timeToLiveNanos) { - this.timeToLiveNanos = timeToLiveNanos; - return this; - } - - public ExpirationCacheBuilder withShouldDisposeFunc(ShouldDisposeFunc shouldDisposeFunc) { - this.shouldDisposeFunc = shouldDisposeFunc; - return this; - } - - public ExpirationCacheBuilder withItemDisposalFunc(ItemDisposalFunc itemDisposalFunc) { - this.itemDisposalFunc = itemDisposalFunc; - return this; - } - - public ExpirationCache build() { - return new ExpirationCache<>( - this.valueClass, - this.isRenewableExpiration, - this.timeToLiveNanos, - this.shouldDisposeFunc, - this.itemDisposalFunc); - } -} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 9a14eee25..8dbaa5e9a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -28,7 +28,6 @@ import java.util.function.Supplier; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; -import software.amazon.jdbc.AllowedAndBlockedHosts; import software.amazon.jdbc.util.ItemDisposalFunc; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ShouldDisposeFunc; @@ -38,6 +37,8 @@ public class StorageServiceImpl implements StorageService { protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(5); protected static final Map> caches = new ConcurrentHashMap<>(); protected static final Map>> defaultCacheSuppliers; + // Map from item category to the expected value class for that category. + protected static final Map> valueClasses = new ConcurrentHashMap<>(); protected static final AtomicBoolean isInitialized = new AtomicBoolean(false); protected static final ReentrantLock initLock = new ReentrantLock(); protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { @@ -48,10 +49,8 @@ public class StorageServiceImpl implements StorageService { static { Map>> suppliers = new HashMap<>(); - suppliers.put(ItemCategory.TOPOLOGY, () -> new ExpirationCacheBuilder<>(Topology.class).build()); - suppliers.put( - ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, - () -> new ExpirationCacheBuilder<>(AllowedAndBlockedHosts.class).build()); + suppliers.put(ItemCategory.TOPOLOGY, ExpirationCache::new); + suppliers.put(ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, ExpirationCache::new); defaultCacheSuppliers = Collections.unmodifiableMap(suppliers); } @@ -100,12 +99,14 @@ public void registerItemCategoryIfAbsent( @Nullable ItemDisposalFunc itemDisposalFunc) { caches.computeIfAbsent( itemCategory, - category -> new ExpirationCache<>( - itemClass, - isRenewableExpiration, - timeToLiveNanos, - shouldDisposeFunc, - itemDisposalFunc)); + category -> { + valueClasses.put(itemCategory, itemClass); + return new ExpirationCache<>( + isRenewableExpiration, + timeToLiveNanos, + shouldDisposeFunc, + itemDisposalFunc); + }); } @Override @@ -121,11 +122,12 @@ public void set(String itemCategory, Object key, V value) { } } - if (!cache.getValueClass().isInstance(value)) { + Class expectedValueClass = valueClasses.get(itemCategory); + if (expectedValueClass == null || !expectedValueClass.isInstance(value)) { throw new IllegalArgumentException( Messages.get( "StorageServiceImpl.incorrectValueType", - new Object[] {itemCategory, cache.getValueClass(), value.getClass(), value})); + new Object[] {itemCategory, expectedValueClass, value.getClass(), value})); } ExpirationCache typedCache = (ExpirationCache) cache; diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 649424be8..58bdc4da3 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -292,7 +292,7 @@ MonitorImpl.stopMonitoringThread=Stop monitoring thread for {0}. MonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should not be empty. # monitoring.MonitorServiceImpl -MonitorServiceImpl.incorrectValueType= +MonitorServiceImpl.monitorClassNotRegistered=The given monitor class ''{0}'' is not registered. Please register the monitor class before running monitors of that class with the monitor service. NodeMonitoringThread.detectedWriter=Writer detected by node monitoring thread: ''{0}''. NodeMonitoringThread.invalidWriterQuery=The writer topology query is invalid: {0} From e06375a94630872e80abcade54852b8b30cc66d5 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 3 Mar 2025 14:36:21 -0800 Subject: [PATCH 043/149] MonitorServiceImpl wip --- .../amazon/jdbc/util/monitoring/Monitor.java | 11 +- .../jdbc/util/monitoring/MonitorService.java | 5 +- .../util/monitoring/MonitorServiceImpl.java | 118 ++++++++++++++---- .../jdbc/util/monitoring/MonitorStatus.java | 43 ------- .../jdbc/util/storage/ExpirationCache.java | 6 +- .../jdbc/util/storage/StorageServiceImpl.java | 3 + ..._advanced_jdbc_wrapper_messages.properties | 4 +- 7 files changed, 115 insertions(+), 75 deletions(-) delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index b20f0ceda..fad4c8609 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -16,10 +16,17 @@ package software.amazon.jdbc.util.monitoring; +import org.checkerframework.checker.nullness.qual.Nullable; + public interface Monitor { void start(); - void stop(); + void close(); + + long getLastUsedTimeNanos(); + + MonitorState getState(); - MonitorStatus getStatus(); + @Nullable + Exception getUnhandledException(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index fd8a4cecd..385be6090 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -52,10 +52,7 @@ void registerMonitorTypeIfAbsent( * "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". * @param monitorSupplier a supplier lambda that can be used to create the monitor if it is absent. */ - void runIfAbsent( - Class monitorClass, - Object key, - Supplier monitorSupplier); + void runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier); /** * Stops the given monitor and removes it from the monitor service. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 295f3f988..45602a7f5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -19,19 +19,97 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ShouldDisposeFunc; +import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.ExpirationCache; public class MonitorServiceImpl implements MonitorService { private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(1); - protected static final Map, Set> errorResponsesByType = new ConcurrentHashMap<>(); - protected static final Map, ExpirationCache>> monitorCaches = new ConcurrentHashMap<>(); + protected static final Map, Set> errorResponsesByType = + new ConcurrentHashMap<>(); + protected static final Map, ExpirationCache> monitorCaches = + new ConcurrentHashMap<>(); + protected static final AtomicBoolean isInitialized = new AtomicBoolean(false); + protected static final ReentrantLock initLock = new ReentrantLock(); + protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { + final Thread thread = new Thread(r); + thread.setDaemon(true); + return thread; + })); + + public MonitorServiceImpl() { + initCleanupThread(DEFAULT_CLEANUP_INTERVAL_NANOS); + } + + public MonitorServiceImpl(long cleanupIntervalNanos) { + initCleanupThread(cleanupIntervalNanos); + } + + protected void initCleanupThread(long cleanupIntervalNanos) { + if (isInitialized.get()) { + return; + } + + initLock.lock(); + try { + if (isInitialized.get()) { + return; + } + + cleanupExecutor.scheduleAtFixedRate( + this::checkMonitors, cleanupIntervalNanos, cleanupIntervalNanos, TimeUnit.NANOSECONDS); + cleanupExecutor.shutdown(); + isInitialized.set(true); + } finally { + initLock.unlock(); + } + } + + protected void checkMonitors() { + LOGGER.finest(Messages.get("MonitorServiceImpl.checkingMonitors")); + for (ExpirationCache cache : monitorCaches.values()) { + // Note: the map returned by getEntries is a copy of the ExpirationCache map + for (Map.Entry entry : cache.getEntries().entrySet()) { + MonitorItem monitorItem = entry.getValue(); + if (monitorItem == null) { + continue; + } + + Monitor monitor = monitorItem.getMonitor(); + if (monitor == null) { + continue; + } + + if (monitor.getState() != MonitorState.ERROR) { + cache.removeIfExpired(entry.getKey()); + continue; + } + + LOGGER.fine( + Messages.get("MonitorServiceImpl.errorInMonitor", new Object[]{monitor.getUnhandledException(), monitor})); + + Set errorResponses = errorResponsesByType.get(monitor.getClass()); + if (!Utils.isNullOrEmpty(errorResponses) && errorResponses.contains(MonitorErrorResponse.RESTART)) { + // Note: the put method disposes of the old item + LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); + cache.put(entry.getKey(), new MonitorItem(monitorItem.getMonitorSupplier())); + continue; + } + + cache.removeIfExpired(entry.getKey()); + } + } + } @Override public void registerMonitorTypeIfAbsent( @@ -54,21 +132,15 @@ public void registerMonitorTypeIfAbsent( @Override public void runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier) { - ExpirationCache> cache = monitorCaches.get(monitorClass); + ExpirationCache cache = monitorCaches.get(monitorClass); if (cache == null) { throw new IllegalStateException( - Messages.get("MonitorServiceImpl.monitorClassNotRegistered", new Object[] {monitorClass})); + Messages.get("MonitorServiceImpl.monitorTypeNotRegistered", new Object[] {monitorClass})); } - ExpirationCache> typedCache = - (ExpirationCache>) (ExpirationCache) cache; - typedCache.computeIfAbsent( + cache.computeIfAbsent( key, - k -> { - T monitor = monitorSupplier.get(); - monitor.start(); - return new MonitorItem<>(monitorSupplier, monitor); - }); + k -> new MonitorItem((Supplier) monitorSupplier)); } @Override @@ -80,10 +152,10 @@ public void stopAndRemove(Class monitorClass, Object key) Object result = cache.remove(key); if (result instanceof MonitorItem) { - MonitorItem monitorItem = (MonitorItem) result; + MonitorItem monitorItem = (MonitorItem) result; Monitor monitor = monitorItem.getMonitor(); if (monitor != null) { - monitor.stop(); + monitor.close(); } } } @@ -97,10 +169,10 @@ public void stopAndRemoveMonitors(Class monitorClass) { for (Object value : cache.getEntries().values()) { if (value instanceof MonitorItem) { - MonitorItem monitorItem = (MonitorItem) value; + MonitorItem monitorItem = (MonitorItem) value; Monitor monitor = monitorItem.getMonitor(); if (monitor != null) { - monitor.stop(); + monitor.close(); } } } @@ -115,20 +187,20 @@ public void stopAndRemoveAll() { } } - protected static class MonitorItem { - private final Supplier monitorSupplier; - private final T monitor; + protected static class MonitorItem { + private final Supplier monitorSupplier; + private final Monitor monitor; - protected MonitorItem(Supplier monitorSupplier, T monitor) { + protected MonitorItem(Supplier monitorSupplier) { this.monitorSupplier = monitorSupplier; - this.monitor = monitor; + this.monitor = monitorSupplier.get(); } - public Supplier getMonitorSupplier() { + public Supplier getMonitorSupplier() { return monitorSupplier; } - public T getMonitor() { + public Monitor getMonitor() { return monitor; } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java deleted file mode 100644 index 2fb4a3c14..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorStatus.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.util.monitoring; - -import org.checkerframework.checker.nullness.qual.Nullable; - -public class MonitorStatus { - private final MonitorState state; - private final long lastUsedTimeNs; - private final @Nullable Throwable exception; - - public MonitorStatus(MonitorState state, long lastUsedTimeNs, @Nullable Throwable exception) { - this.state = state; - this.lastUsedTimeNs = lastUsedTimeNs; - this.exception = exception; - } - - public MonitorState getState() { - return state; - } - - public long getLastUsedTimeNs() { - return lastUsedTimeNs; - } - - public @Nullable Throwable getException() { - return this.exception; - } -} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index 1e4ee46ee..ca669973f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -119,7 +119,8 @@ public ExpirationCache( } // cacheItem is the previous value associated with the key. - if (cacheItem.shouldCleanup() && this.itemDisposalFunc != null) { + // TODO: should we check expiration or call shouldDisposeFunc here even though the item is definitely being removed? + if (this.itemDisposalFunc != null) { this.itemDisposalFunc.dispose(cacheItem.item); } @@ -186,7 +187,7 @@ public void removeExpiredEntries() { }); } - protected void removeIfExpired(K key) { + public void removeIfExpired(K key) { // A list is used to store the cached item for later disposal since lambdas require references to outer variables // to be final. This allows us to dispose of the item after it has been removed and the cache has been unlocked, // which is important because the disposal function may be long-running. @@ -231,6 +232,7 @@ public Map getEntries() { for (final Map.Entry entry : this.cache.entrySet()) { entries.put(entry.getKey(), entry.getValue().item); } + return entries; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 8dbaa5e9a..ccbdb4bb9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -28,6 +28,7 @@ import java.util.function.Supplier; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; +import software.amazon.jdbc.AllowedAndBlockedHosts; import software.amazon.jdbc.util.ItemDisposalFunc; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ShouldDisposeFunc; @@ -50,7 +51,9 @@ public class StorageServiceImpl implements StorageService { static { Map>> suppliers = new HashMap<>(); suppliers.put(ItemCategory.TOPOLOGY, ExpirationCache::new); + valueClasses.put(ItemCategory.TOPOLOGY, Topology.class); suppliers.put(ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, ExpirationCache::new); + valueClasses.put(ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, AllowedAndBlockedHosts.class); defaultCacheSuppliers = Collections.unmodifiableMap(suppliers); } diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 58bdc4da3..33ad78f4c 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -292,7 +292,9 @@ MonitorImpl.stopMonitoringThread=Stop monitoring thread for {0}. MonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should not be empty. # monitoring.MonitorServiceImpl -MonitorServiceImpl.monitorClassNotRegistered=The given monitor class ''{0}'' is not registered. Please register the monitor class before running monitors of that class with the monitor service. +MonitorServiceImpl.errorInMonitor=The monitor supervisor detected a monitor in an error state. Error: ''{0}''. Monitor: ''{1}''. +MonitorServiceImpl.monitorTypeNotRegistered=The given monitor class ''{0}'' is not registered. Please register the monitor class before running monitors of that class with the monitor service. +MonitorServiceImpl.restartingMonitor=Restarting monitor: ''{0}''. NodeMonitoringThread.detectedWriter=Writer detected by node monitoring thread: ''{0}''. NodeMonitoringThread.invalidWriterQuery=The writer topology query is invalid: {0} From e8a88209282532ef99d84f212978358d5f07078a Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 3 Mar 2025 18:08:31 -0800 Subject: [PATCH 044/149] Implemented inactive timeout, AbstractMonitor --- .../jdbc/util/monitoring/AbstractMonitor.java | 53 +++++++++++++++++++ .../amazon/jdbc/util/monitoring/Monitor.java | 4 +- .../jdbc/util/monitoring/MonitorService.java | 29 +++++----- .../util/monitoring/MonitorServiceImpl.java | 35 ++++++++++-- .../jdbc/util/monitoring/MonitorSettings.java | 37 +++++++++++++ ..._advanced_jdbc_wrapper_messages.properties | 3 ++ 6 files changed, 141 insertions(+), 20 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java new file mode 100644 index 000000000..d8426a7fb --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -0,0 +1,53 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +import java.util.logging.Logger; +import software.amazon.jdbc.util.Messages; + +public abstract class AbstractMonitor implements Monitor, Runnable { + private static Logger LOGGER = Logger.getLogger(AbstractMonitor.class.getName()); + protected long lastUsedTimestampNanos; + protected MonitorState state; + protected Exception unhandledException; + + @Override + public void run() { + try { + execute(); + } catch (Exception e) { + LOGGER.fine(Messages.get("AbstractMonitor.unexpectedError", new Object[]{this, this.unhandledException})); + this.state = MonitorState.ERROR; + this.unhandledException = e; + } + } + + @Override + public long getLastUsedTimestampNanos() { + return this.lastUsedTimestampNanos; + } + + @Override + public MonitorState getState() { + return this.state; + } + + @Override + public Exception getUnhandledException() { + return this.unhandledException; + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index fad4c8609..4e691bd7b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -19,11 +19,11 @@ import org.checkerframework.checker.nullness.qual.Nullable; public interface Monitor { - void start(); + void execute(); void close(); - long getLastUsedTimeNanos(); + long getLastUsedTimestampNanos(); MonitorState getState(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 385be6090..376dd25e8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -21,7 +21,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.util.ShouldDisposeFunc; -// TODO: fix javadocs (eg monitorType -> monitorClass) public interface MonitorService { /** * Register a new monitor type with the monitor service. This method needs to be called before adding new types of @@ -30,24 +29,28 @@ public interface MonitorService { * but this method can be called by users if they want to add a new monitor type. * * @param monitorClass the class of the monitor, eg `CustomEndpointMonitorImpl.class`. + * @param timeToLiveNanos how long a monitor should be stored before being considered expired, in nanoseconds. If + * the monitor is expired and shouldDisposeFunc returns `true`, the monitor will be + * stopped. + * @param inactiveTimeoutNanos a duration in nanoseconds defining the maximum amount of time that a monitor should + * take between updating its last-updated timestamp. If a monitor has not updated its + * last-updated timestamp within this value it will be considered stuck. * @param errorResponses a Set defining actions to take if the monitor is in an error state. - * @param timeToLiveNanos how long a monitor should be stored before expiring, in nanoseconds. If the monitor is - * expired and shouldDisposeFunc returns `true`, the monitor will be stopped. - * @param shouldDisposeFunc a function defining whether an item should be stopped if expired. If `null` is passed, the - * monitor will always be stopped if the monitor is expired. + * @param shouldDisposeFunc a function defining whether an item should be stopped if expired. If `null` is + * passed, the monitor will always be stopped if the monitor is expired. */ void registerMonitorTypeIfAbsent( Class monitorClass, - Set errorResponses, - long cleanupIntervalNanos, long timeToLiveNanos, + long inactiveTimeoutNanos, + Set errorResponses, @Nullable ShouldDisposeFunc shouldDisposeFunc); /** * Creates and starts the given monitor if it does not already exist and stores it under the given monitor type and - * key. + * key. If the monitor already exists, its time-to-live duration will be renewed, even if it was already expired. * - * @param monitorClass a String representing the monitor type, eg "customEndpoint". + * @param monitorClass the class of the monitor, eg `CustomEndpointMonitorImpl.class`. * @param key the key for the monitor, eg * "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". * @param monitorSupplier a supplier lambda that can be used to create the monitor if it is absent. @@ -57,16 +60,16 @@ void registerMonitorTypeIfAbsent( /** * Stops the given monitor and removes it from the monitor service. * - * @param monitorClass a String representing the monitor type, eg "customEndpoint". - * @param key the key for the monitor, eg - * "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". + * @param monitorClass the class of the monitor, eg `CustomEndpointMonitorImpl.class`. + * @param key the key for the monitor, eg + * "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". */ void stopAndRemove(Class monitorClass, Object key); /** * Stops all monitors for the given type and removes them from the monitor service. * - * @param monitorClass a String representing the monitor type, eg "customEndpoint". + * @param monitorClass the class of the monitor, eg `CustomEndpointMonitorImpl.class`. */ void stopAndRemoveMonitors(Class monitorClass); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 45602a7f5..411e9852c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -35,7 +35,7 @@ public class MonitorServiceImpl implements MonitorService { private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(1); - protected static final Map, Set> errorResponsesByType = + protected static final Map, MonitorSettings> monitorSettingsByType = new ConcurrentHashMap<>(); protected static final Map, ExpirationCache> monitorCaches = new ConcurrentHashMap<>(); @@ -90,6 +90,31 @@ protected void checkMonitors() { continue; } + if (monitor.getState() == MonitorState.STOPPED) { + cache.remove(entry.getKey()); + } + + MonitorSettings monitorSettings = monitorSettingsByType.get(monitor.getClass()); + if (monitorSettings == null) { + cache.removeIfExpired(entry.getKey()); + continue; + } + + Set errorResponses = monitorSettings.getErrorResponses(); + if (System.nanoTime() - monitor.getLastUsedTimestampNanos() > monitorSettings.getInactiveTimeoutNanos()) { + LOGGER.fine( + Messages.get("MonitorServiceImpl.monitorStuck", + new Object[]{monitor, TimeUnit.NANOSECONDS.toSeconds(monitorSettings.getInactiveTimeoutNanos())})); + if (!Utils.isNullOrEmpty(errorResponses) && errorResponses.contains(MonitorErrorResponse.RESTART)) { + // Note: the put method disposes of the old item + LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); + cache.put(entry.getKey(), new MonitorItem(monitorItem.getMonitorSupplier())); + continue; + } else { + cache.remove(entry.getKey()); + } + } + if (monitor.getState() != MonitorState.ERROR) { cache.removeIfExpired(entry.getKey()); continue; @@ -98,7 +123,6 @@ protected void checkMonitors() { LOGGER.fine( Messages.get("MonitorServiceImpl.errorInMonitor", new Object[]{monitor.getUnhandledException(), monitor})); - Set errorResponses = errorResponsesByType.get(monitor.getClass()); if (!Utils.isNullOrEmpty(errorResponses) && errorResponses.contains(MonitorErrorResponse.RESTART)) { // Note: the put method disposes of the old item LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); @@ -114,13 +138,14 @@ protected void checkMonitors() { @Override public void registerMonitorTypeIfAbsent( Class monitorClass, + long timeToLiveNanos, + long inactiveTimeoutNanos, Set errorResponses, - long cleanupIntervalNanos, long timeToLiveNanos, @Nullable ShouldDisposeFunc shouldDisposeFunc) { monitorCaches.computeIfAbsent( monitorClass, mc -> { - errorResponsesByType.putIfAbsent(monitorClass, errorResponses); + monitorSettingsByType.putIfAbsent(monitorClass, new MonitorSettings(inactiveTimeoutNanos, errorResponses)); return new ExpirationCache<>( true, timeToLiveNanos, @@ -182,7 +207,7 @@ public void stopAndRemoveMonitors(Class monitorClass) { @Override public void stopAndRemoveAll() { - for (Class monitorClass : errorResponsesByType.keySet()) { + for (Class monitorClass : monitorCaches.keySet()) { stopAndRemoveMonitors(monitorClass); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java new file mode 100644 index 000000000..a90a0030c --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java @@ -0,0 +1,37 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +import java.util.Set; + +public class MonitorSettings { + private final long inactiveTimeoutNanos; + private final Set errorResponses; + + public MonitorSettings(long inactiveTimeoutNanos, Set errorResponses) { + this.inactiveTimeoutNanos = inactiveTimeoutNanos; + this.errorResponses = errorResponses; + } + + public long getInactiveTimeoutNanos() { + return inactiveTimeoutNanos; + } + + public Set getErrorResponses() { + return errorResponses; + } +} diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 33ad78f4c..b8c0413e9 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -14,6 +14,8 @@ # limitations under the License. # +AbstractMonitor.unexpectedError=A monitor encountered an unexpected exception. Monitor: ''{0}''. Exception: ''{1}''. + # ADFS Credentials Provider Getter AdfsCredentialsProviderFactory.failedLogin=Failed login. Could not obtain SAML Assertion from ADFS SignOn Page POST response: \n''{0}'' AdfsCredentialsProviderFactory.invalidHttpsUrl=Invalid HTTPS URL: ''{0}'' @@ -293,6 +295,7 @@ MonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should # monitoring.MonitorServiceImpl MonitorServiceImpl.errorInMonitor=The monitor supervisor detected a monitor in an error state. Error: ''{0}''. Monitor: ''{1}''. +MonitorServiceImpl.monitorStuck=Monitor ''{0}'' has not been updated within the inactive timeout of {1} seconds. MonitorServiceImpl.monitorTypeNotRegistered=The given monitor class ''{0}'' is not registered. Please register the monitor class before running monitors of that class with the monitor service. MonitorServiceImpl.restartingMonitor=Restarting monitor: ''{0}''. From bef36576a294e82c5e95725b92aa1373a96ad4bc Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 4 Mar 2025 13:42:11 -0800 Subject: [PATCH 045/149] Implement default registration --- .../jdbc/util/monitoring/AbstractMonitor.java | 17 ++-- .../amazon/jdbc/util/monitoring/Monitor.java | 5 - .../jdbc/util/monitoring/MonitorService.java | 10 ++ .../util/monitoring/MonitorServiceImpl.java | 94 ++++++++++--------- .../jdbc/util/monitoring/MonitorSettings.java | 7 +- ..._advanced_jdbc_wrapper_messages.properties | 1 - 6 files changed, 73 insertions(+), 61 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index d8426a7fb..bf3dd15ca 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -21,18 +21,24 @@ public abstract class AbstractMonitor implements Monitor, Runnable { private static Logger LOGGER = Logger.getLogger(AbstractMonitor.class.getName()); + private final MonitorService monitorService; + private final Object monitorKey; protected long lastUsedTimestampNanos; protected MonitorState state; - protected Exception unhandledException; + + protected AbstractMonitor(MonitorService monitorService, Object monitorKey) { + this.monitorService = monitorService; + this.monitorKey = monitorKey; + } @Override public void run() { try { execute(); } catch (Exception e) { - LOGGER.fine(Messages.get("AbstractMonitor.unexpectedError", new Object[]{this, this.unhandledException})); + LOGGER.fine(Messages.get("AbstractMonitor.unexpectedError", new Object[]{this, e})); this.state = MonitorState.ERROR; - this.unhandledException = e; + monitorService.processMonitorError(this, monitorKey, e); } } @@ -45,9 +51,4 @@ public long getLastUsedTimestampNanos() { public MonitorState getState() { return this.state; } - - @Override - public Exception getUnhandledException() { - return this.unhandledException; - } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index 4e691bd7b..c6e030627 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -16,8 +16,6 @@ package software.amazon.jdbc.util.monitoring; -import org.checkerframework.checker.nullness.qual.Nullable; - public interface Monitor { void execute(); @@ -26,7 +24,4 @@ public interface Monitor { long getLastUsedTimestampNanos(); MonitorState getState(); - - @Nullable - Exception getUnhandledException(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 376dd25e8..0e127c969 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -57,6 +57,16 @@ void registerMonitorTypeIfAbsent( */ void runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier); + /** + * Process a monitor error. The monitor service will respond to the error based on the monitor error responses defined + * when the monitor type was registered. + * + * @param monitor the monitor that encountered the unexpected exception. + * @param key the key for the monitor. + * @param exception the unexpected exception that occurred. + */ + void processMonitorError(Monitor monitor, Object key, Exception exception); + /** * Stops the given monitor and removes it from the monitor service. * diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 411e9852c..807018cbb 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -16,6 +16,8 @@ package software.amazon.jdbc.util.monitoring; +import java.util.Collections; +import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -29,7 +31,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ShouldDisposeFunc; -import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.ExpirationCache; public class MonitorServiceImpl implements MonitorService { @@ -39,6 +40,8 @@ public class MonitorServiceImpl implements MonitorService { new ConcurrentHashMap<>(); protected static final Map, ExpirationCache> monitorCaches = new ConcurrentHashMap<>(); + protected static final Map, Supplier>> + defaultCacheSuppliers; protected static final AtomicBoolean isInitialized = new AtomicBoolean(false); protected static final ReentrantLock initLock = new ReentrantLock(); protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { @@ -47,6 +50,23 @@ public class MonitorServiceImpl implements MonitorService { return thread; })); + static { + Map, Supplier>> suppliers = new HashMap<>(); + // suppliers.put( + // ClusterTopologyMonitorImpl.class, + // () -> new ExpirationCache( + // true, + // TimeUnit.MINUTES.toNanos(15), + // null, + // (monitorItem) -> monitorItem.getMonitor().close())); + // monitorSettingsByType.put( + // ClusterTopologyMonitorImpl.class, + // new MonitorSettings( + // TimeUnit.MINUTES.toNanos(1), + // new HashSet<>(Collections.singletonList(MonitorErrorResponse.NO_ACTION)))); + defaultCacheSuppliers = Collections.unmodifiableMap(suppliers); + } + public MonitorServiceImpl() { initCleanupThread(DEFAULT_CLEANUP_INTERVAL_NANOS); } @@ -101,36 +121,22 @@ protected void checkMonitors() { } Set errorResponses = monitorSettings.getErrorResponses(); - if (System.nanoTime() - monitor.getLastUsedTimestampNanos() > monitorSettings.getInactiveTimeoutNanos()) { - LOGGER.fine( - Messages.get("MonitorServiceImpl.monitorStuck", - new Object[]{monitor, TimeUnit.NANOSECONDS.toSeconds(monitorSettings.getInactiveTimeoutNanos())})); - if (!Utils.isNullOrEmpty(errorResponses) && errorResponses.contains(MonitorErrorResponse.RESTART)) { - // Note: the put method disposes of the old item - LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); - cache.put(entry.getKey(), new MonitorItem(monitorItem.getMonitorSupplier())); - continue; - } else { - cache.remove(entry.getKey()); - } - } - - if (monitor.getState() != MonitorState.ERROR) { + if (System.nanoTime() - monitor.getLastUsedTimestampNanos() < monitorSettings.getInactiveTimeoutNanos()) { + // Monitor has updated its last-used timestamp recently and is not considered stuck. cache.removeIfExpired(entry.getKey()); - continue; } + // Monitor has been inactive for longer than the inactive timeout and is considered stuck. LOGGER.fine( - Messages.get("MonitorServiceImpl.errorInMonitor", new Object[]{monitor.getUnhandledException(), monitor})); - - if (!Utils.isNullOrEmpty(errorResponses) && errorResponses.contains(MonitorErrorResponse.RESTART)) { + Messages.get("MonitorServiceImpl.monitorStuck", + new Object[]{monitor, TimeUnit.NANOSECONDS.toSeconds(monitorSettings.getInactiveTimeoutNanos())})); + if (errorResponses.contains(MonitorErrorResponse.RESTART)) { // Note: the put method disposes of the old item LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); cache.put(entry.getKey(), new MonitorItem(monitorItem.getMonitorSupplier())); - continue; + } else { + cache.remove(entry.getKey()); } - - cache.removeIfExpired(entry.getKey()); } } } @@ -146,12 +152,13 @@ public void registerMonitorTypeIfAbsent( monitorClass, mc -> { monitorSettingsByType.putIfAbsent(monitorClass, new MonitorSettings(inactiveTimeoutNanos, errorResponses)); + ShouldDisposeFunc wrappedShouldDisposeFunc = shouldDisposeFunc == null ? null + : (monitorItem) -> shouldDisposeFunc.shouldDispose((T) monitorItem.getMonitor()); return new ExpirationCache<>( true, timeToLiveNanos, - null, - null - ); + wrappedShouldDisposeFunc, + (monitorItem) -> monitorItem.getMonitor().close()); }); } @@ -168,6 +175,20 @@ public void runIfAbsent(Class monitorClass, Object key, S k -> new MonitorItem((Supplier) monitorSupplier)); } + @Override + public void processMonitorError(Monitor monitor, Object key, Exception exception) { + MonitorSettings settings = monitorSettingsByType.get(monitor.getClass()); + if (settings == null) { + stopAndRemove(monitor.getClass(), key); + return; + } + + Set errorResponses = settings.getErrorResponses(); + if (errorResponses.contains(MonitorErrorResponse.RESTART)) { + LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); + } + } + @Override public void stopAndRemove(Class monitorClass, Object key) { ExpirationCache cache = monitorCaches.get(monitorClass); @@ -175,14 +196,8 @@ public void stopAndRemove(Class monitorClass, Object key) return; } - Object result = cache.remove(key); - if (result instanceof MonitorItem) { - MonitorItem monitorItem = (MonitorItem) result; - Monitor monitor = monitorItem.getMonitor(); - if (monitor != null) { - monitor.close(); - } - } + // Note: remove() automatically closes the monitor. + cache.remove(key); } @Override @@ -192,16 +207,7 @@ public void stopAndRemoveMonitors(Class monitorClass) { return; } - for (Object value : cache.getEntries().values()) { - if (value instanceof MonitorItem) { - MonitorItem monitorItem = (MonitorItem) value; - Monitor monitor = monitorItem.getMonitor(); - if (monitor != null) { - monitor.close(); - } - } - } - + // Note: clear() automatically closes the monitors. cache.clear(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java index a90a0030c..b8bbf2483 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java @@ -17,12 +17,13 @@ package software.amazon.jdbc.util.monitoring; import java.util.Set; +import org.checkerframework.checker.nullness.qual.NonNull; public class MonitorSettings { private final long inactiveTimeoutNanos; - private final Set errorResponses; + private @NonNull final Set errorResponses; - public MonitorSettings(long inactiveTimeoutNanos, Set errorResponses) { + public MonitorSettings(long inactiveTimeoutNanos, @NonNull Set errorResponses) { this.inactiveTimeoutNanos = inactiveTimeoutNanos; this.errorResponses = errorResponses; } @@ -31,7 +32,7 @@ public long getInactiveTimeoutNanos() { return inactiveTimeoutNanos; } - public Set getErrorResponses() { + public @NonNull Set getErrorResponses() { return errorResponses; } } diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index b8c0413e9..30480ae4d 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -294,7 +294,6 @@ MonitorImpl.stopMonitoringThread=Stop monitoring thread for {0}. MonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should not be empty. # monitoring.MonitorServiceImpl -MonitorServiceImpl.errorInMonitor=The monitor supervisor detected a monitor in an error state. Error: ''{0}''. Monitor: ''{1}''. MonitorServiceImpl.monitorStuck=Monitor ''{0}'' has not been updated within the inactive timeout of {1} seconds. MonitorServiceImpl.monitorTypeNotRegistered=The given monitor class ''{0}'' is not registered. Please register the monitor class before running monitors of that class with the monitor service. MonitorServiceImpl.restartingMonitor=Restarting monitor: ''{0}''. From b05c6f45595450ff2effba759d0ec51d8e16f706 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 4 Mar 2025 16:00:48 -0800 Subject: [PATCH 046/149] replace commented out default suppliers with TODO --- .../jdbc/util/monitoring/MonitorServiceImpl.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 807018cbb..3dfad0d5e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -52,18 +52,7 @@ public class MonitorServiceImpl implements MonitorService { static { Map, Supplier>> suppliers = new HashMap<>(); - // suppliers.put( - // ClusterTopologyMonitorImpl.class, - // () -> new ExpirationCache( - // true, - // TimeUnit.MINUTES.toNanos(15), - // null, - // (monitorItem) -> monitorItem.getMonitor().close())); - // monitorSettingsByType.put( - // ClusterTopologyMonitorImpl.class, - // new MonitorSettings( - // TimeUnit.MINUTES.toNanos(1), - // new HashSet<>(Collections.singletonList(MonitorErrorResponse.NO_ACTION)))); + // TODO: add default suppliers once monitors have been adjusted to implement the Monitor interface defaultCacheSuppliers = Collections.unmodifiableMap(suppliers); } From f647219ea747fa9584d2fc53efc68d352e0d506f Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 4 Mar 2025 16:21:39 -0800 Subject: [PATCH 047/149] Auto-register default monitor types if missing --- .../amazon/jdbc/util/monitoring/MonitorServiceImpl.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 3dfad0d5e..6fc48e8eb 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -155,8 +155,13 @@ public void registerMonitorTypeIfAbsent( public void runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier) { ExpirationCache cache = monitorCaches.get(monitorClass); if (cache == null) { - throw new IllegalStateException( - Messages.get("MonitorServiceImpl.monitorTypeNotRegistered", new Object[] {monitorClass})); + Supplier> supplier = defaultCacheSuppliers.get(monitorClass); + if (supplier == null) { + throw new IllegalStateException( + Messages.get("MonitorServiceImpl.monitorTypeNotRegistered", new Object[] {monitorClass})); + } else { + cache = monitorCaches.computeIfAbsent(monitorClass, c -> supplier.get()); + } } cache.computeIfAbsent( From 01014b44d1d8a16230dd5e69e88f06cb373c8c66 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 5 Mar 2025 14:58:45 -0800 Subject: [PATCH 048/149] PR comments, fix missing monitor error processing --- .../jdbc/util/monitoring/AbstractMonitor.java | 8 ++-- .../amazon/jdbc/util/monitoring/Monitor.java | 4 +- .../jdbc/util/monitoring/MonitorService.java | 27 ++++++------- .../util/monitoring/MonitorServiceImpl.java | 40 +++++++++++++++---- ..._advanced_jdbc_wrapper_messages.properties | 2 + 5 files changed, 53 insertions(+), 28 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index bf3dd15ca..05cb2eac5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -22,23 +22,21 @@ public abstract class AbstractMonitor implements Monitor, Runnable { private static Logger LOGGER = Logger.getLogger(AbstractMonitor.class.getName()); private final MonitorService monitorService; - private final Object monitorKey; protected long lastUsedTimestampNanos; protected MonitorState state; - protected AbstractMonitor(MonitorService monitorService, Object monitorKey) { + protected AbstractMonitor(MonitorService monitorService) { this.monitorService = monitorService; - this.monitorKey = monitorKey; } @Override public void run() { try { - execute(); + start(); } catch (Exception e) { LOGGER.fine(Messages.get("AbstractMonitor.unexpectedError", new Object[]{this, e})); this.state = MonitorState.ERROR; - monitorService.processMonitorError(this, monitorKey, e); + monitorService.processMonitorError(this, e); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index c6e030627..eef6014ea 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -17,9 +17,9 @@ package software.amazon.jdbc.util.monitoring; public interface Monitor { - void execute(); + void start(); - void close(); + void stop(); long getLastUsedTimestampNanos(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 0e127c969..abf942214 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -28,21 +28,21 @@ public interface MonitorService { * Expected monitor types ("topology" and "customEndpoint") will be added automatically during driver initialization, * but this method can be called by users if they want to add a new monitor type. * - * @param monitorClass the class of the monitor, eg `CustomEndpointMonitorImpl.class`. - * @param timeToLiveNanos how long a monitor should be stored before being considered expired, in nanoseconds. If - * the monitor is expired and shouldDisposeFunc returns `true`, the monitor will be - * stopped. - * @param inactiveTimeoutNanos a duration in nanoseconds defining the maximum amount of time that a monitor should - * take between updating its last-updated timestamp. If a monitor has not updated its - * last-updated timestamp within this value it will be considered stuck. - * @param errorResponses a Set defining actions to take if the monitor is in an error state. - * @param shouldDisposeFunc a function defining whether an item should be stopped if expired. If `null` is - * passed, the monitor will always be stopped if the monitor is expired. + * @param monitorClass the class of the monitor, eg `CustomEndpointMonitorImpl.class`. + * @param expirationTimeoutNanos how long a monitor should be stored without use before being considered expired, in + * nanoseconds. If the monitor is expired and shouldDisposeFunc returns `true`, the + * monitor will be stopped. + * @param heartbeatTimeoutNanos a duration in nanoseconds defining the maximum amount of time that a monitor should + * take between updating its last-updated timestamp. If a monitor has not updated its + * last-updated timestamp within this value it will be considered stuck. + * @param errorResponses a Set defining actions to take if the monitor is in an error state. + * @param shouldDisposeFunc a function defining whether an item should be stopped if expired. If `null` is + * passed, the monitor will always be stopped if the monitor is expired. */ void registerMonitorTypeIfAbsent( Class monitorClass, - long timeToLiveNanos, - long inactiveTimeoutNanos, + long expirationTimeoutNanos, + long heartbeatTimeoutNanos, Set errorResponses, @Nullable ShouldDisposeFunc shouldDisposeFunc); @@ -62,10 +62,9 @@ void registerMonitorTypeIfAbsent( * when the monitor type was registered. * * @param monitor the monitor that encountered the unexpected exception. - * @param key the key for the monitor. * @param exception the unexpected exception that occurred. */ - void processMonitorError(Monitor monitor, Object key, Exception exception); + void processMonitorError(Monitor monitor, Exception exception); /** * Stops the given monitor and removes it from the monitor service. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 6fc48e8eb..0f636eb2c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -133,21 +133,21 @@ protected void checkMonitors() { @Override public void registerMonitorTypeIfAbsent( Class monitorClass, - long timeToLiveNanos, - long inactiveTimeoutNanos, + long expirationTimeoutNanos, + long heartbeatTimeoutNanos, Set errorResponses, @Nullable ShouldDisposeFunc shouldDisposeFunc) { monitorCaches.computeIfAbsent( monitorClass, mc -> { - monitorSettingsByType.putIfAbsent(monitorClass, new MonitorSettings(inactiveTimeoutNanos, errorResponses)); + monitorSettingsByType.putIfAbsent(monitorClass, new MonitorSettings(heartbeatTimeoutNanos, errorResponses)); ShouldDisposeFunc wrappedShouldDisposeFunc = shouldDisposeFunc == null ? null : (monitorItem) -> shouldDisposeFunc.shouldDispose((T) monitorItem.getMonitor()); return new ExpirationCache<>( true, - timeToLiveNanos, + expirationTimeoutNanos, wrappedShouldDisposeFunc, - (monitorItem) -> monitorItem.getMonitor().close()); + (monitorItem) -> monitorItem.getMonitor().stop()); }); } @@ -170,16 +170,42 @@ public void runIfAbsent(Class monitorClass, Object key, S } @Override - public void processMonitorError(Monitor monitor, Object key, Exception exception) { + public void processMonitorError(Monitor monitor, Exception exception) { + ExpirationCache cache = monitorCaches.get(monitor.getClass()); + if (cache == null) { + LOGGER.fine(Messages.get("MonitorServiceImpl.unregisteredMonitorError", new Object[] {monitor, exception})); + return; + } + + Object monitorKey = null; + Supplier monitorSupplier = null; + for (Map.Entry entry : cache.getEntries().entrySet()) { + MonitorItem monitorItem = entry.getValue(); + if (monitorItem != null && monitorItem.getMonitor() == monitor) { + monitorKey = entry.getKey(); + monitorSupplier = entry.getValue().getMonitorSupplier(); + break; + } + } + + if (monitorKey == null || monitorSupplier == null) { + LOGGER.fine(Messages.get("MonitorServiceImpl.monitorErrorForMissingMonitor", new Object[] {monitor, exception})); + return; + } + MonitorSettings settings = monitorSettingsByType.get(monitor.getClass()); if (settings == null) { - stopAndRemove(monitor.getClass(), key); + stopAndRemove(monitor.getClass(), monitorKey); return; } Set errorResponses = settings.getErrorResponses(); if (errorResponses.contains(MonitorErrorResponse.RESTART)) { LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); + // Note: the put method automatically disposes of the old item. + cache.put(monitorKey, new MonitorItem(monitorSupplier)); + } else { + stopAndRemove(monitor.getClass(), monitorKey); } } diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 30480ae4d..4d868b787 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -294,9 +294,11 @@ MonitorImpl.stopMonitoringThread=Stop monitoring thread for {0}. MonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should not be empty. # monitoring.MonitorServiceImpl +MonitorServiceImpl.monitorErrorForMissingMonitor=Monitor ''{0}'' reported an error to the monitor service, but the monitor service could not find the monitor under its registered monitors. Reported error: ''{1}''. MonitorServiceImpl.monitorStuck=Monitor ''{0}'' has not been updated within the inactive timeout of {1} seconds. MonitorServiceImpl.monitorTypeNotRegistered=The given monitor class ''{0}'' is not registered. Please register the monitor class before running monitors of that class with the monitor service. MonitorServiceImpl.restartingMonitor=Restarting monitor: ''{0}''. +MonitorServiceImpl.unregisteredMonitorError=Monitor ''{0}'' reported an error to the monitor service, but the monitor type has not been registered. Reported error: ''{1}''. NodeMonitoringThread.detectedWriter=Writer detected by node monitoring thread: ''{0}''. NodeMonitoringThread.invalidWriterQuery=The writer topology query is invalid: {0} From 2cd58696d8520ec56b6f93e233cc98ef92c791d4 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 6 Mar 2025 09:49:52 -0800 Subject: [PATCH 049/149] Added CacheContainer --- .../util/monitoring/MonitorServiceImpl.java | 101 ++++++++---------- 1 file changed, 47 insertions(+), 54 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 0f636eb2c..594719b8e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -16,8 +16,6 @@ package software.amazon.jdbc.util.monitoring; -import java.util.Collections; -import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -28,6 +26,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; import java.util.logging.Logger; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ShouldDisposeFunc; @@ -36,12 +35,7 @@ public class MonitorServiceImpl implements MonitorService { private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(1); - protected static final Map, MonitorSettings> monitorSettingsByType = - new ConcurrentHashMap<>(); - protected static final Map, ExpirationCache> monitorCaches = - new ConcurrentHashMap<>(); - protected static final Map, Supplier>> - defaultCacheSuppliers; + protected static final Map, CacheContainer> monitorCaches = new ConcurrentHashMap<>(); protected static final AtomicBoolean isInitialized = new AtomicBoolean(false); protected static final ReentrantLock initLock = new ReentrantLock(); protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { @@ -51,9 +45,7 @@ public class MonitorServiceImpl implements MonitorService { })); static { - Map, Supplier>> suppliers = new HashMap<>(); - // TODO: add default suppliers once monitors have been adjusted to implement the Monitor interface - defaultCacheSuppliers = Collections.unmodifiableMap(suppliers); + // TODO: add default caches once monitors have been adjusted to implement the Monitor interface } public MonitorServiceImpl() { @@ -86,7 +78,8 @@ protected void initCleanupThread(long cleanupIntervalNanos) { protected void checkMonitors() { LOGGER.finest(Messages.get("MonitorServiceImpl.checkingMonitors")); - for (ExpirationCache cache : monitorCaches.values()) { + for (CacheContainer container : monitorCaches.values()) { + ExpirationCache cache = container.getCache(); // Note: the map returned by getEntries is a copy of the ExpirationCache map for (Map.Entry entry : cache.getEntries().entrySet()) { MonitorItem monitorItem = entry.getValue(); @@ -95,20 +88,11 @@ protected void checkMonitors() { } Monitor monitor = monitorItem.getMonitor(); - if (monitor == null) { - continue; - } - if (monitor.getState() == MonitorState.STOPPED) { cache.remove(entry.getKey()); } - MonitorSettings monitorSettings = monitorSettingsByType.get(monitor.getClass()); - if (monitorSettings == null) { - cache.removeIfExpired(entry.getKey()); - continue; - } - + MonitorSettings monitorSettings = container.getSettings(); Set errorResponses = monitorSettings.getErrorResponses(); if (System.nanoTime() - monitor.getLastUsedTimestampNanos() < monitorSettings.getInactiveTimeoutNanos()) { // Monitor has updated its last-used timestamp recently and is not considered stuck. @@ -140,45 +124,41 @@ public void registerMonitorTypeIfAbsent( monitorCaches.computeIfAbsent( monitorClass, mc -> { - monitorSettingsByType.putIfAbsent(monitorClass, new MonitorSettings(heartbeatTimeoutNanos, errorResponses)); ShouldDisposeFunc wrappedShouldDisposeFunc = shouldDisposeFunc == null ? null : (monitorItem) -> shouldDisposeFunc.shouldDispose((T) monitorItem.getMonitor()); - return new ExpirationCache<>( + ExpirationCache cache = new ExpirationCache<>( true, expirationTimeoutNanos, wrappedShouldDisposeFunc, (monitorItem) -> monitorItem.getMonitor().stop()); + return new CacheContainer(new MonitorSettings(heartbeatTimeoutNanos, errorResponses), cache); }); } @Override public void runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier) { - ExpirationCache cache = monitorCaches.get(monitorClass); - if (cache == null) { - Supplier> supplier = defaultCacheSuppliers.get(monitorClass); - if (supplier == null) { - throw new IllegalStateException( - Messages.get("MonitorServiceImpl.monitorTypeNotRegistered", new Object[] {monitorClass})); - } else { - cache = monitorCaches.computeIfAbsent(monitorClass, c -> supplier.get()); - } + CacheContainer cacheContainer = monitorCaches.get(monitorClass); + if (cacheContainer == null) { + throw new IllegalStateException( + Messages.get("MonitorServiceImpl.monitorTypeNotRegistered", new Object[] {monitorClass})); } - cache.computeIfAbsent( + cacheContainer.getCache().computeIfAbsent( key, k -> new MonitorItem((Supplier) monitorSupplier)); } @Override public void processMonitorError(Monitor monitor, Exception exception) { - ExpirationCache cache = monitorCaches.get(monitor.getClass()); - if (cache == null) { + CacheContainer cacheContainer = monitorCaches.get(monitor.getClass()); + if (cacheContainer == null) { LOGGER.fine(Messages.get("MonitorServiceImpl.unregisteredMonitorError", new Object[] {monitor, exception})); return; } Object monitorKey = null; Supplier monitorSupplier = null; + ExpirationCache cache = cacheContainer.getCache(); for (Map.Entry entry : cache.getEntries().entrySet()) { MonitorItem monitorItem = entry.getValue(); if (monitorItem != null && monitorItem.getMonitor() == monitor) { @@ -188,17 +168,12 @@ public void processMonitorError(Monitor monitor, Exception exception) { } } - if (monitorKey == null || monitorSupplier == null) { + if (monitorKey == null) { LOGGER.fine(Messages.get("MonitorServiceImpl.monitorErrorForMissingMonitor", new Object[] {monitor, exception})); return; } - MonitorSettings settings = monitorSettingsByType.get(monitor.getClass()); - if (settings == null) { - stopAndRemove(monitor.getClass(), monitorKey); - return; - } - + MonitorSettings settings = cacheContainer.getSettings(); Set errorResponses = settings.getErrorResponses(); if (errorResponses.contains(MonitorErrorResponse.RESTART)) { LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); @@ -211,24 +186,24 @@ public void processMonitorError(Monitor monitor, Exception exception) { @Override public void stopAndRemove(Class monitorClass, Object key) { - ExpirationCache cache = monitorCaches.get(monitorClass); - if (cache == null) { + CacheContainer cacheContainer = monitorCaches.get(monitorClass); + if (cacheContainer == null) { return; } // Note: remove() automatically closes the monitor. - cache.remove(key); + cacheContainer.getCache().remove(key); } @Override public void stopAndRemoveMonitors(Class monitorClass) { - ExpirationCache cache = monitorCaches.get(monitorClass); - if (cache == null) { + CacheContainer cacheContainer = monitorCaches.get(monitorClass); + if (cacheContainer == null) { return; } // Note: clear() automatically closes the monitors. - cache.clear(); + cacheContainer.getCache().clear(); } @Override @@ -238,20 +213,38 @@ public void stopAndRemoveAll() { } } + protected static class CacheContainer { + private @NonNull final MonitorSettings settings; + private @NonNull final ExpirationCache cache; + + public CacheContainer(@NonNull MonitorSettings settings, @NonNull ExpirationCache cache) { + this.settings = settings; + this.cache = cache; + } + + public @NonNull MonitorSettings getSettings() { + return settings; + } + + public @NonNull ExpirationCache getCache() { + return cache; + } + } + protected static class MonitorItem { - private final Supplier monitorSupplier; - private final Monitor monitor; + private @NonNull final Supplier monitorSupplier; + private @NonNull final Monitor monitor; - protected MonitorItem(Supplier monitorSupplier) { + protected MonitorItem(@NonNull Supplier monitorSupplier) { this.monitorSupplier = monitorSupplier; this.monitor = monitorSupplier.get(); } - public Supplier getMonitorSupplier() { + public @NonNull Supplier getMonitorSupplier() { return monitorSupplier; } - public Monitor getMonitor() { + public @NonNull Monitor getMonitor() { return monitor; } } From 497b648bd9a2bed7407607a999cc4012dd0383c1 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 6 Mar 2025 14:49:08 -0800 Subject: [PATCH 050/149] Implement ExternallyManagedCache --- .../util/monitoring/MonitorServiceImpl.java | 24 ++- .../jdbc/util/storage/ExpirationCache.java | 76 ++++---- .../util/storage/ExternallyManagedCache.java | 169 ++++++++++++++++++ 3 files changed, 217 insertions(+), 52 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 594719b8e..20232d0a8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -30,7 +30,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ShouldDisposeFunc; -import software.amazon.jdbc.util.storage.ExpirationCache; +import software.amazon.jdbc.util.storage.ExternallyManagedCache; public class MonitorServiceImpl implements MonitorService { private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); @@ -79,8 +79,8 @@ protected void initCleanupThread(long cleanupIntervalNanos) { protected void checkMonitors() { LOGGER.finest(Messages.get("MonitorServiceImpl.checkingMonitors")); for (CacheContainer container : monitorCaches.values()) { - ExpirationCache cache = container.getCache(); - // Note: the map returned by getEntries is a copy of the ExpirationCache map + ExternallyManagedCache cache = container.getCache(); + // Note: the map returned by getEntries is a copy of the ExternallyManagedCache map for (Map.Entry entry : cache.getEntries().entrySet()) { MonitorItem monitorItem = entry.getValue(); if (monitorItem == null) { @@ -104,6 +104,7 @@ protected void checkMonitors() { Messages.get("MonitorServiceImpl.monitorStuck", new Object[]{monitor, TimeUnit.NANOSECONDS.toSeconds(monitorSettings.getInactiveTimeoutNanos())})); if (errorResponses.contains(MonitorErrorResponse.RESTART)) { + // TODO: fix the inaccurate notes // Note: the put method disposes of the old item LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); cache.put(entry.getKey(), new MonitorItem(monitorItem.getMonitorSupplier())); @@ -124,13 +125,8 @@ public void registerMonitorTypeIfAbsent( monitorCaches.computeIfAbsent( monitorClass, mc -> { - ShouldDisposeFunc wrappedShouldDisposeFunc = shouldDisposeFunc == null ? null - : (monitorItem) -> shouldDisposeFunc.shouldDispose((T) monitorItem.getMonitor()); - ExpirationCache cache = new ExpirationCache<>( - true, - expirationTimeoutNanos, - wrappedShouldDisposeFunc, - (monitorItem) -> monitorItem.getMonitor().stop()); + ExternallyManagedCache cache = + new ExternallyManagedCache<>(true, expirationTimeoutNanos); return new CacheContainer(new MonitorSettings(heartbeatTimeoutNanos, errorResponses), cache); }); } @@ -158,7 +154,7 @@ public void processMonitorError(Monitor monitor, Exception exception) { Object monitorKey = null; Supplier monitorSupplier = null; - ExpirationCache cache = cacheContainer.getCache(); + ExternallyManagedCache cache = cacheContainer.getCache(); for (Map.Entry entry : cache.getEntries().entrySet()) { MonitorItem monitorItem = entry.getValue(); if (monitorItem != null && monitorItem.getMonitor() == monitor) { @@ -215,9 +211,9 @@ public void stopAndRemoveAll() { protected static class CacheContainer { private @NonNull final MonitorSettings settings; - private @NonNull final ExpirationCache cache; + private @NonNull final ExternallyManagedCache cache; - public CacheContainer(@NonNull MonitorSettings settings, @NonNull ExpirationCache cache) { + public CacheContainer(@NonNull MonitorSettings settings, @NonNull ExternallyManagedCache cache) { this.settings = settings; this.cache = cache; } @@ -226,7 +222,7 @@ public CacheContainer(@NonNull MonitorSettings settings, @NonNull ExpirationCach return settings; } - public @NonNull ExpirationCache getCache() { + public @NonNull ExternallyManagedCache getCache() { return cache; } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index ca669973f..3da6ed398 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -52,6 +52,32 @@ public ExpirationCache( this.itemDisposalFunc = itemDisposalFunc; } + /** + * Store the given value at the given key. + * + * @param key the key at which the value should be stored + * @param value the value to be stored + * @return the previous value stored at the given key, or null if there was no previous value. If the previous value + * is expired it will be disposed and returned. + */ + public @Nullable V put( + final K key, + final V value) { + final CacheItem cacheItem = + cache.put(key, new CacheItem(value, System.nanoTime() + this.timeToLiveNanos)); + if (cacheItem == null) { + return null; + } + + // cacheItem is the previous value associated with the key. + // TODO: should we check expiration or call shouldDisposeFunc here even though the item is definitely being removed? + if (this.itemDisposalFunc != null) { + this.itemDisposalFunc.dispose(cacheItem.item); + } + + return cacheItem.item; + } + /** * If a value does not exist for the given key or the existing value is expired and non-renewable, stores the value * returned by the given mapping function, unless the function returns null, in which case the key will be removed. @@ -70,29 +96,29 @@ public ExpirationCache( final List toDisposeList = new ArrayList<>(1); final CacheItem cacheItem = cache.compute( key, - (k, v) -> { - if (v == null) { + (k, valueItem) -> { + if (valueItem == null) { // The key is absent; compute and store the new value. return new CacheItem( mappingFunction.apply(k), System.nanoTime() + this.timeToLiveNanos); } - if (v.shouldCleanup() && !this.isRenewableExpiration) { + if (valueItem.shouldCleanup() && !this.isRenewableExpiration) { // The existing value is expired and non-renewable. Mark it for disposal and store the new value. - toDisposeList.add(v.item); + toDisposeList.add(valueItem.item); return new CacheItem( mappingFunction.apply(k), System.nanoTime() + this.timeToLiveNanos); } // The existing value is non-expired or renewable. Keep the existing value. - return v; - }); + if (this.isRenewableExpiration) { + valueItem.extendExpiration(); + } - if (this.isRenewableExpiration) { - cacheItem.extendExpiration(); - } + return valueItem; + }); if (this.itemDisposalFunc != null && !toDisposeList.isEmpty()) { this.itemDisposalFunc.dispose(toDisposeList.get(0)); @@ -101,32 +127,6 @@ public ExpirationCache( return cacheItem.item; } - /** - * Store the given value at the given key. - * - * @param key the key at which the value should be stored - * @param value the value to be stored - * @return the previous value stored at the given key, or null if there was no previous value. If the previous value - * is expired it will be disposed and returned. - */ - public @Nullable V put( - final K key, - final V value) { - final CacheItem cacheItem = - cache.put(key, new CacheItem(value, System.nanoTime() + this.timeToLiveNanos)); - if (cacheItem == null) { - return null; - } - - // cacheItem is the previous value associated with the key. - // TODO: should we check expiration or call shouldDisposeFunc here even though the item is definitely being removed? - if (this.itemDisposalFunc != null) { - this.itemDisposalFunc.dispose(cacheItem.item); - } - - return cacheItem.item; - } - /** * Retrieves the value stored at the given key. * @@ -253,7 +253,7 @@ protected class CacheItem { * CacheItem constructor. * * @param item the item value - * @param expirationTimeNanos the amount of time before a CacheItem should be marked as expired. + * @param expirationTimeNanos the time at which a CacheItem should be considered expired. */ protected CacheItem(final V item, final long expirationTimeNanos) { this.item = item; @@ -268,7 +268,7 @@ protected CacheItem(final V item, final long expirationTimeNanos) { * @return true if the cache item should be cleaned up at cleanup time. Otherwise, returns * false. */ - boolean shouldCleanup() { + protected boolean shouldCleanup() { final boolean isExpired = this.expirationTimeNanos != 0 && System.nanoTime() > this.expirationTimeNanos; if (shouldDisposeFunc != null) { return isExpired && shouldDisposeFunc.shouldDispose(this.item); @@ -279,7 +279,7 @@ boolean shouldCleanup() { /** * Renews a cache item's expiration time. */ - public void extendExpiration() { + protected void extendExpiration() { this.expirationTimeNanos = System.nanoTime() + timeToLiveNanos; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java new file mode 100644 index 000000000..2cd429fdd --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java @@ -0,0 +1,169 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.storage; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.logging.Logger; +import org.checkerframework.checker.nullness.qual.Nullable; + +public class ExternallyManagedCache { + private static final Logger LOGGER = Logger.getLogger(ExpirationCache.class.getName()); + protected final Map cache = new ConcurrentHashMap<>(); + protected final boolean isRenewableExpiration; + protected final long timeToLiveNanos; + + public ExternallyManagedCache(boolean isRenewableExpiration, long timeToLiveNanos) { + this.isRenewableExpiration = isRenewableExpiration; + this.timeToLiveNanos = timeToLiveNanos; + } + + public @Nullable V put(K key, V value) { + CacheItem cacheItem = this.cache.put(key, new CacheItem(value, timeToLiveNanos)); + if (cacheItem == null) { + return null; + } + + return cacheItem.item; + } + + public @Nullable V computeIfAbsent(K key, Function mappingFunction) { + final CacheItem cacheItem = cache.compute( + key, + (k, valueItem) -> { + if (valueItem == null) { + // The key is absent; compute and store the new value. + return new CacheItem( + mappingFunction.apply(k), + System.nanoTime() + this.timeToLiveNanos); + } + + // The existing value is non-expired or renewable. Keep the existing value. + if (this.isRenewableExpiration) { + valueItem.extendExpiration(); + } + + return valueItem; + }); + + return cacheItem.item; + } + + public @Nullable V remove(K key) { + CacheItem cacheItem = cache.remove(key); + if (cacheItem == null) { + return null; + } + + return cacheItem.item; + } + + public @Nullable V removeIfExpired(K key) { + // The function only returns a value if it was removed. A list is used to store the removed item since lambdas + // require references to outer variables to be final. + final List removedItemList = new ArrayList<>(1); + cache.computeIfPresent( + key, + (k, valueItem) -> { + if (valueItem.isExpired()) { + removedItemList.add(valueItem.item); + return null; + } + + return valueItem; + }); + + if (removedItemList.isEmpty()) { + return null; + } else { + return removedItemList.get(0); + } + } + + public Map getEntries() { + final Map entries = new HashMap<>(); + for (final Map.Entry entry : this.cache.entrySet()) { + entries.put(entry.getKey(), entry.getValue().item); + } + + return entries; + } + + public void clear() { + this.cache.clear(); + } + + protected class CacheItem { + private final V item; + private long expirationTimeNanos; + + /** + * CacheItem constructor. + * + * @param item the item value + * @param expirationTimeNanos the amount of time before a CacheItem should be marked as expired. + */ + protected CacheItem(final V item, final long expirationTimeNanos) { + this.item = item; + this.expirationTimeNanos = expirationTimeNanos; + } + + protected boolean isExpired() { + return System.nanoTime() >= expirationTimeNanos; + } + + /** + * Renews a cache item's expiration time. + */ + protected void extendExpiration() { + this.expirationTimeNanos = System.nanoTime() + timeToLiveNanos; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((item == null) ? 0 : item.hashCode()); + return result; + } + + @Override + @SuppressWarnings("unchecked") + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + // First check null and type (use instanceof for correct type checking) + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + CacheItem other = (CacheItem) obj; + return this.item.equals(other.item) && this.expirationTimeNanos == other.expirationTimeNanos; + } + + @Override + public String toString() { + return "CacheItem [item=" + item + ", expirationTime=" + expirationTimeNanos + "]"; + } + } +} From c99243d6e572e812e6b171595cbb2e7b15fc79d6 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 6 Mar 2025 17:13:21 -0800 Subject: [PATCH 051/149] Add removeIf() method --- .../util/monitoring/MonitorServiceImpl.java | 74 ++++++++++--------- .../util/storage/ExternallyManagedCache.java | 23 ++++++ 2 files changed, 64 insertions(+), 33 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 20232d0a8..7c64fce31 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -82,34 +82,41 @@ protected void checkMonitors() { ExternallyManagedCache cache = container.getCache(); // Note: the map returned by getEntries is a copy of the ExternallyManagedCache map for (Map.Entry entry : cache.getEntries().entrySet()) { - MonitorItem monitorItem = entry.getValue(); - if (monitorItem == null) { + Object key = entry.getKey(); + MonitorItem removedItem = cache.removeIf(key, mi -> mi.getMonitor().getState() == MonitorState.STOPPED); + if (removedItem != null) { + removedItem.getMonitor().stop(); continue; } - Monitor monitor = monitorItem.getMonitor(); - if (monitor.getState() == MonitorState.STOPPED) { - cache.remove(entry.getKey()); - } - MonitorSettings monitorSettings = container.getSettings(); Set errorResponses = monitorSettings.getErrorResponses(); - if (System.nanoTime() - monitor.getLastUsedTimestampNanos() < monitorSettings.getInactiveTimeoutNanos()) { - // Monitor has updated its last-used timestamp recently and is not considered stuck. - cache.removeIfExpired(entry.getKey()); + removedItem = cache.removeIf(key, mi -> mi.getMonitor().getState() == MonitorState.ERROR); + if (removedItem != null) { + Monitor monitor = removedItem.getMonitor(); + monitor.stop(); + if (errorResponses.contains(MonitorErrorResponse.RESTART)) { + LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); + Supplier monitorSupplier = removedItem.getMonitorSupplier(); + cache.computeIfAbsent(key, k -> new MonitorItem(monitorSupplier)); + } } - // Monitor has been inactive for longer than the inactive timeout and is considered stuck. - LOGGER.fine( - Messages.get("MonitorServiceImpl.monitorStuck", - new Object[]{monitor, TimeUnit.NANOSECONDS.toSeconds(monitorSettings.getInactiveTimeoutNanos())})); - if (errorResponses.contains(MonitorErrorResponse.RESTART)) { - // TODO: fix the inaccurate notes - // Note: the put method disposes of the old item - LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); - cache.put(entry.getKey(), new MonitorItem(monitorItem.getMonitorSupplier())); - } else { - cache.remove(entry.getKey()); + long inactiveTimeoutNanos = monitorSettings.getInactiveTimeoutNanos(); + removedItem = cache.removeIf( + key, mi -> System.nanoTime() - mi.getMonitor().getLastUsedTimestampNanos() > inactiveTimeoutNanos); + if (removedItem != null) { + // Monitor has been inactive for longer than the inactive timeout and is considered stuck. + LOGGER.fine( + Messages.get("MonitorServiceImpl.monitorStuck", + new Object[]{removedItem.getMonitor(), TimeUnit.NANOSECONDS.toSeconds(inactiveTimeoutNanos)})); + Monitor monitor = removedItem.getMonitor(); + monitor.stop(); + if (errorResponses.contains(MonitorErrorResponse.RESTART)) { + LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); + Supplier monitorSupplier = removedItem.getMonitorSupplier(); + cache.computeIfAbsent(key, k -> new MonitorItem(monitorSupplier)); + } } } } @@ -127,7 +134,7 @@ public void registerMonitorTypeIfAbsent( mc -> { ExternallyManagedCache cache = new ExternallyManagedCache<>(true, expirationTimeoutNanos); - return new CacheContainer(new MonitorSettings(heartbeatTimeoutNanos, errorResponses), cache); + return new CacheContainer(cache, new MonitorSettings(heartbeatTimeoutNanos, errorResponses)); }); } @@ -141,7 +148,7 @@ public void runIfAbsent(Class monitorClass, Object key, S cacheContainer.getCache().computeIfAbsent( key, - k -> new MonitorItem((Supplier) monitorSupplier)); + k -> new MonitorItem(monitorSupplier)); } @Override @@ -153,7 +160,7 @@ public void processMonitorError(Monitor monitor, Exception exception) { } Object monitorKey = null; - Supplier monitorSupplier = null; + Supplier monitorSupplier = null; ExternallyManagedCache cache = cacheContainer.getCache(); for (Map.Entry entry : cache.getEntries().entrySet()) { MonitorItem monitorItem = entry.getValue(); @@ -210,33 +217,34 @@ public void stopAndRemoveAll() { } protected static class CacheContainer { - private @NonNull final MonitorSettings settings; private @NonNull final ExternallyManagedCache cache; + private @NonNull final MonitorSettings settings; - public CacheContainer(@NonNull MonitorSettings settings, @NonNull ExternallyManagedCache cache) { + public CacheContainer( + @NonNull ExternallyManagedCache cache, @NonNull MonitorSettings settings) { this.settings = settings; this.cache = cache; } - public @NonNull MonitorSettings getSettings() { - return settings; - } - public @NonNull ExternallyManagedCache getCache() { return cache; } + + public @NonNull MonitorSettings getSettings() { + return settings; + } } protected static class MonitorItem { - private @NonNull final Supplier monitorSupplier; + private @NonNull final Supplier monitorSupplier; private @NonNull final Monitor monitor; - protected MonitorItem(@NonNull Supplier monitorSupplier) { + protected MonitorItem(@NonNull Supplier monitorSupplier) { this.monitorSupplier = monitorSupplier; this.monitor = monitorSupplier.get(); } - public @NonNull Supplier getMonitorSupplier() { + public @NonNull Supplier getMonitorSupplier() { return monitorSupplier; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java index 2cd429fdd..52f7f4c7c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java @@ -22,6 +22,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; +import java.util.function.Predicate; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; @@ -76,6 +77,28 @@ public ExternallyManagedCache(boolean isRenewableExpiration, long timeToLiveNano return cacheItem.item; } + public @Nullable V removeIf(K key, Predicate predicate) { + // The function only returns a value if it was removed. A list is used to store the removed item since lambdas + // require references to outer variables to be final. + final List removedItemList = new ArrayList<>(1); + cache.computeIfPresent( + key, + (k, valueItem) -> { + if (predicate.test(valueItem.item)) { + removedItemList.add(valueItem.item); + return null; + } + + return valueItem; + }); + + if (removedItemList.isEmpty()) { + return null; + } else { + return removedItemList.get(0); + } + } + public @Nullable V removeIfExpired(K key) { // The function only returns a value if it was removed. A list is used to store the removed item since lambdas // require references to outer variables to be final. From 7d6b0f01c12264338b8831eda1006ec1cce70081 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 6 Mar 2025 17:35:43 -0800 Subject: [PATCH 052/149] Cleanup MonitorServiceImpl and fix minor issues --- .../jdbc/util/monitoring/AbstractMonitor.java | 2 +- .../jdbc/util/monitoring/MonitorService.java | 2 +- .../util/monitoring/MonitorServiceImpl.java | 100 ++++++++---------- .../util/storage/ExternallyManagedCache.java | 4 - 4 files changed, 47 insertions(+), 61 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index 05cb2eac5..c42aa5c8d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -36,7 +36,7 @@ public void run() { } catch (Exception e) { LOGGER.fine(Messages.get("AbstractMonitor.unexpectedError", new Object[]{this, e})); this.state = MonitorState.ERROR; - monitorService.processMonitorError(this, e); + monitorService.handleMonitorError(this, e); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index abf942214..3cdf51b4b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -64,7 +64,7 @@ void registerMonitorTypeIfAbsent( * @param monitor the monitor that encountered the unexpected exception. * @param exception the unexpected exception that occurred. */ - void processMonitorError(Monitor monitor, Exception exception); + void handleMonitorError(Monitor monitor, Exception exception); /** * Stops the given monitor and removes it from the monitor service. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 7c64fce31..e895f9cd8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -90,16 +90,9 @@ protected void checkMonitors() { } MonitorSettings monitorSettings = container.getSettings(); - Set errorResponses = monitorSettings.getErrorResponses(); removedItem = cache.removeIf(key, mi -> mi.getMonitor().getState() == MonitorState.ERROR); if (removedItem != null) { - Monitor monitor = removedItem.getMonitor(); - monitor.stop(); - if (errorResponses.contains(MonitorErrorResponse.RESTART)) { - LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); - Supplier monitorSupplier = removedItem.getMonitorSupplier(); - cache.computeIfAbsent(key, k -> new MonitorItem(monitorSupplier)); - } + handleMonitorError(container, key, removedItem); } long inactiveTimeoutNanos = monitorSettings.getInactiveTimeoutNanos(); @@ -110,18 +103,44 @@ protected void checkMonitors() { LOGGER.fine( Messages.get("MonitorServiceImpl.monitorStuck", new Object[]{removedItem.getMonitor(), TimeUnit.NANOSECONDS.toSeconds(inactiveTimeoutNanos)})); - Monitor monitor = removedItem.getMonitor(); - monitor.stop(); - if (errorResponses.contains(MonitorErrorResponse.RESTART)) { - LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); - Supplier monitorSupplier = removedItem.getMonitorSupplier(); - cache.computeIfAbsent(key, k -> new MonitorItem(monitorSupplier)); - } + handleMonitorError(container, key, removedItem); } } } } + @Override + public void handleMonitorError(Monitor monitor, Exception exception) { + CacheContainer cacheContainer = monitorCaches.get(monitor.getClass()); + if (cacheContainer == null) { + LOGGER.fine(Messages.get("MonitorServiceImpl.unregisteredMonitorError", new Object[] {monitor, exception})); + return; + } + + ExternallyManagedCache cache = cacheContainer.getCache(); + for (Map.Entry entry : cache.getEntries().entrySet()) { + MonitorItem monitorItem = cache.removeIf(entry.getKey(), mi -> mi.getMonitor() == monitor); + if (monitorItem != null) { + handleMonitorError(cacheContainer, entry.getKey(), monitorItem); + return; + } + } + + LOGGER.finest(Messages.get("MonitorServiceImpl.monitorErrorForMissingMonitor", new Object[] {monitor, exception})); + } + + private void handleMonitorError(CacheContainer container, Object key, MonitorItem monitorItem) { + Monitor monitor = monitorItem.getMonitor(); + monitor.stop(); + + Set errorResponses = container.getSettings().getErrorResponses(); + if (errorResponses.contains(MonitorErrorResponse.RESTART)) { + LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); + ExternallyManagedCache cache = container.getCache(); + cache.computeIfAbsent(key, k -> new MonitorItem(monitorItem.getMonitorSupplier())); + } + } + @Override public void registerMonitorTypeIfAbsent( Class monitorClass, @@ -151,42 +170,6 @@ public void runIfAbsent(Class monitorClass, Object key, S k -> new MonitorItem(monitorSupplier)); } - @Override - public void processMonitorError(Monitor monitor, Exception exception) { - CacheContainer cacheContainer = monitorCaches.get(monitor.getClass()); - if (cacheContainer == null) { - LOGGER.fine(Messages.get("MonitorServiceImpl.unregisteredMonitorError", new Object[] {monitor, exception})); - return; - } - - Object monitorKey = null; - Supplier monitorSupplier = null; - ExternallyManagedCache cache = cacheContainer.getCache(); - for (Map.Entry entry : cache.getEntries().entrySet()) { - MonitorItem monitorItem = entry.getValue(); - if (monitorItem != null && monitorItem.getMonitor() == monitor) { - monitorKey = entry.getKey(); - monitorSupplier = entry.getValue().getMonitorSupplier(); - break; - } - } - - if (monitorKey == null) { - LOGGER.fine(Messages.get("MonitorServiceImpl.monitorErrorForMissingMonitor", new Object[] {monitor, exception})); - return; - } - - MonitorSettings settings = cacheContainer.getSettings(); - Set errorResponses = settings.getErrorResponses(); - if (errorResponses.contains(MonitorErrorResponse.RESTART)) { - LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); - // Note: the put method automatically disposes of the old item. - cache.put(monitorKey, new MonitorItem(monitorSupplier)); - } else { - stopAndRemove(monitor.getClass(), monitorKey); - } - } - @Override public void stopAndRemove(Class monitorClass, Object key) { CacheContainer cacheContainer = monitorCaches.get(monitorClass); @@ -194,8 +177,10 @@ public void stopAndRemove(Class monitorClass, Object key) return; } - // Note: remove() automatically closes the monitor. - cacheContainer.getCache().remove(key); + MonitorItem monitorItem = cacheContainer.getCache().remove(key); + if (monitorItem != null) { + monitorItem.getMonitor().stop(); + } } @Override @@ -205,8 +190,13 @@ public void stopAndRemoveMonitors(Class monitorClass) { return; } - // Note: clear() automatically closes the monitors. - cacheContainer.getCache().clear(); + ExternallyManagedCache cache = cacheContainer.getCache(); + for (Map.Entry entry : cache.getEntries().entrySet()) { + MonitorItem monitorItem = cache.remove(entry.getKey()); + if (monitorItem != null) { + monitorItem.getMonitor().stop(); + } + } } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java index 52f7f4c7c..63cdaba2c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java @@ -130,10 +130,6 @@ public Map getEntries() { return entries; } - public void clear() { - this.cache.clear(); - } - protected class CacheItem { private final V item; private long expirationTimeNanos; From eb8f45daa457c05eb383b5067e96129dce5ba644 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 7 Mar 2025 10:25:35 -0800 Subject: [PATCH 053/149] Remove monitor if expired --- .../jdbc/util/monitoring/AbstractMonitor.java | 2 +- .../amazon/jdbc/util/monitoring/Monitor.java | 2 + .../jdbc/util/monitoring/MonitorService.java | 2 +- .../util/monitoring/MonitorServiceImpl.java | 49 ++++++++++--------- .../util/storage/ExternallyManagedCache.java | 4 +- 5 files changed, 33 insertions(+), 26 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index c42aa5c8d..e5a88dc8b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -36,7 +36,7 @@ public void run() { } catch (Exception e) { LOGGER.fine(Messages.get("AbstractMonitor.unexpectedError", new Object[]{this, e})); this.state = MonitorState.ERROR; - monitorService.handleMonitorError(this, e); + monitorService.reportMonitorError(this, e); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index eef6014ea..a6218c50f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -24,4 +24,6 @@ public interface Monitor { long getLastUsedTimestampNanos(); MonitorState getState(); + + boolean canDispose(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 3cdf51b4b..bb8598c58 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -64,7 +64,7 @@ void registerMonitorTypeIfAbsent( * @param monitor the monitor that encountered the unexpected exception. * @param exception the unexpected exception that occurred. */ - void handleMonitorError(Monitor monitor, Exception exception); + void reportMonitorError(Monitor monitor, Exception exception); /** * Stops the given monitor and removes it from the monitor service. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index e895f9cd8..531c9c7a5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -93,6 +93,7 @@ protected void checkMonitors() { removedItem = cache.removeIf(key, mi -> mi.getMonitor().getState() == MonitorState.ERROR); if (removedItem != null) { handleMonitorError(container, key, removedItem); + continue; } long inactiveTimeoutNanos = monitorSettings.getInactiveTimeoutNanos(); @@ -104,29 +105,15 @@ protected void checkMonitors() { Messages.get("MonitorServiceImpl.monitorStuck", new Object[]{removedItem.getMonitor(), TimeUnit.NANOSECONDS.toSeconds(inactiveTimeoutNanos)})); handleMonitorError(container, key, removedItem); + continue; } - } - } - } - @Override - public void handleMonitorError(Monitor monitor, Exception exception) { - CacheContainer cacheContainer = monitorCaches.get(monitor.getClass()); - if (cacheContainer == null) { - LOGGER.fine(Messages.get("MonitorServiceImpl.unregisteredMonitorError", new Object[] {monitor, exception})); - return; - } - - ExternallyManagedCache cache = cacheContainer.getCache(); - for (Map.Entry entry : cache.getEntries().entrySet()) { - MonitorItem monitorItem = cache.removeIf(entry.getKey(), mi -> mi.getMonitor() == monitor); - if (monitorItem != null) { - handleMonitorError(cacheContainer, entry.getKey(), monitorItem); - return; + removedItem = cache.removeExpiredIf(key, mi -> mi.getMonitor().canDispose()); + if (removedItem != null) { + removedItem.getMonitor().stop(); + } } } - - LOGGER.finest(Messages.get("MonitorServiceImpl.monitorErrorForMissingMonitor", new Object[] {monitor, exception})); } private void handleMonitorError(CacheContainer container, Object key, MonitorItem monitorItem) { @@ -165,9 +152,27 @@ public void runIfAbsent(Class monitorClass, Object key, S Messages.get("MonitorServiceImpl.monitorTypeNotRegistered", new Object[] {monitorClass})); } - cacheContainer.getCache().computeIfAbsent( - key, - k -> new MonitorItem(monitorSupplier)); + cacheContainer.getCache().computeIfAbsent(key, k -> new MonitorItem(monitorSupplier)); + } + + @Override + public void reportMonitorError(Monitor monitor, Exception exception) { + CacheContainer cacheContainer = monitorCaches.get(monitor.getClass()); + if (cacheContainer == null) { + LOGGER.fine(Messages.get("MonitorServiceImpl.unregisteredMonitorError", new Object[] {monitor, exception})); + return; + } + + ExternallyManagedCache cache = cacheContainer.getCache(); + for (Map.Entry entry : cache.getEntries().entrySet()) { + MonitorItem monitorItem = cache.removeIf(entry.getKey(), mi -> mi.getMonitor() == monitor); + if (monitorItem != null) { + handleMonitorError(cacheContainer, entry.getKey(), monitorItem); + return; + } + } + + LOGGER.finest(Messages.get("MonitorServiceImpl.monitorErrorForMissingMonitor", new Object[] {monitor, exception})); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java index 63cdaba2c..c37b74e0d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java @@ -99,14 +99,14 @@ public ExternallyManagedCache(boolean isRenewableExpiration, long timeToLiveNano } } - public @Nullable V removeIfExpired(K key) { + public @Nullable V removeExpiredIf(K key, Predicate predicate) { // The function only returns a value if it was removed. A list is used to store the removed item since lambdas // require references to outer variables to be final. final List removedItemList = new ArrayList<>(1); cache.computeIfPresent( key, (k, valueItem) -> { - if (valueItem.isExpired()) { + if (valueItem.isExpired() && predicate.test(valueItem.item)) { removedItemList.add(valueItem.item); return null; } From 7261a6bd1cad7b72e616a44427790031b7cfcb0b Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 10 Mar 2025 13:54:36 -0700 Subject: [PATCH 054/149] Add MonitorService to ServiceContainer, adapt CustomEndpointMonitor to MonitorService --- .../jdbc/benchmarks/PluginBenchmarks.java | 38 +++++--- .../testplugin/TestConnectionWrapper.java | 6 +- wrapper/build.gradle.kts | 4 +- .../java/software/amazon/jdbc/Driver.java | 14 +-- .../software/amazon/jdbc/PluginService.java | 5 +- .../amazon/jdbc/PluginServiceImpl.java | 11 +-- .../amazon/jdbc/ds/AwsWrapperDataSource.java | 11 ++- .../customendpoint/CustomEndpointMonitor.java | 11 +-- .../CustomEndpointMonitorImpl.java | 16 ++-- .../customendpoint/CustomEndpointPlugin.java | 36 ++------ .../amazon/jdbc/util/ServiceContainer.java | 14 +-- .../jdbc/util/ServiceContainerImpl.java | 55 +++++++----- .../jdbc/util/monitoring/AbstractMonitor.java | 10 ++- .../amazon/jdbc/util/monitoring/Monitor.java | 2 +- .../jdbc/util/monitoring/MonitorService.java | 3 +- .../util/monitoring/MonitorServiceImpl.java | 82 +++++++++++++---- .../jdbc/util/monitoring/MonitorSettings.java | 9 +- .../util/storage/ExternallyManagedCache.java | 11 +-- .../jdbc/wrapper/ConnectionWrapper.java | 14 +-- ..._advanced_jdbc_wrapper_messages.properties | 7 ++ wrapper/src/test/build.gradle.kts | 2 +- .../container/tests/CustomEndpointTest.java | 5 +- .../host/TestEnvironmentConfiguration.java | 18 ++-- .../amazon/jdbc/DialectDetectionTests.java | 14 ++- ...AwsSecretsManagerConnectionPluginTest.java | 14 ++- .../CustomEndpointMonitorImplTest.java | 6 +- .../CustomEndpointPluginTest.java | 13 --- .../dev/DeveloperConnectionPluginTest.java | 88 +++++++++---------- .../jdbc/plugin/efm/ConcurrencyTests.java | 4 +- 29 files changed, 291 insertions(+), 232 deletions(-) diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java index 570cbd2da..6e25fc9a5 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java @@ -62,8 +62,8 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.telemetry.GaugeCallable; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; @@ -87,10 +87,11 @@ public class PluginBenchmarks { "jdbc:aws-wrapper:postgresql://instance-0.XYZ.us-east-2.rds.amazonaws.com"; private static final String TEST_HOST = "instance-0"; private static final int TEST_PORT = 5432; - private static final StorageService storageService = new StorageServiceImpl(); private final HostSpec writerHostSpec = new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) .host(TEST_HOST).port(TEST_PORT).build(); + @Mock private StorageService mockStorageService; + @Mock private MonitorService mockMonitorService; @Mock private PluginService mockPluginService; @Mock private Dialect mockDialect; @Mock private ConnectionPluginManager mockConnectionPluginManager; @@ -168,7 +169,8 @@ public ConnectionWrapper initAndReleaseWithExecutionTimePlugin() throws SQLExcep mockPluginService, mockHostListProviderService, mockPluginManagerService, - storageService)) { + mockStorageService, + mockMonitorService)) { wrapper.releaseResources(); return wrapper; } @@ -184,7 +186,8 @@ public ConnectionWrapper initAndReleaseWithAuroraHostListPlugin() throws SQLExce mockPluginService, mockHostListProviderService, mockPluginManagerService, - storageService)) { + mockStorageService, + mockMonitorService)) { wrapper.releaseResources(); return wrapper; } @@ -200,7 +203,8 @@ public ConnectionWrapper initAndReleaseWithExecutionTimeAndAuroraHostListPlugins mockPluginService, mockHostListProviderService, mockPluginManagerService, - storageService)) { + mockStorageService, + mockMonitorService)) { wrapper.releaseResources(); return wrapper; } @@ -216,7 +220,8 @@ public ConnectionWrapper initAndReleaseWithReadWriteSplittingPlugin() throws SQL mockPluginService, mockHostListProviderService, mockPluginManagerService, - storageService)) { + mockStorageService, + mockMonitorService)) { wrapper.releaseResources(); return wrapper; } @@ -233,7 +238,8 @@ public ConnectionWrapper initAndReleaseWithAuroraHostListAndReadWriteSplittingPl mockPluginService, mockHostListProviderService, mockPluginManagerService, - storageService)) { + mockStorageService, + mockMonitorService)) { wrapper.releaseResources(); return wrapper; } @@ -252,7 +258,8 @@ public ConnectionWrapper initAndReleaseWithReadWriteSplittingPlugin_internalConn mockPluginService, mockHostListProviderService, mockPluginManagerService, - storageService)) { + mockStorageService, + mockMonitorService)) { wrapper.releaseResources(); ConnectionProviderManager.releaseResources(); Driver.resetCustomConnectionProvider(); @@ -274,7 +281,8 @@ public ConnectionWrapper initAndReleaseWithAuroraHostListAndReadWriteSplittingPl mockPluginService, mockHostListProviderService, mockPluginManagerService, - storageService)) { + mockStorageService, + mockMonitorService)) { wrapper.releaseResources(); ConnectionProviderManager.releaseResources(); Driver.resetCustomConnectionProvider(); @@ -292,7 +300,8 @@ public Statement executeStatementBaseline() throws SQLException { mockPluginService, mockHostListProviderService, mockPluginManagerService, - storageService); + mockStorageService, + mockMonitorService); Statement statement = wrapper.createStatement()) { return statement; } @@ -309,7 +318,8 @@ public ResultSet executeStatementWithExecutionTimePlugin() throws SQLException { mockPluginService, mockHostListProviderService, mockPluginManagerService, - storageService); + mockStorageService, + mockMonitorService); Statement statement = wrapper.createStatement(); ResultSet resultSet = statement.executeQuery("some sql")) { return resultSet; @@ -327,7 +337,8 @@ public ResultSet executeStatementWithTelemetryDisabled() throws SQLException { mockPluginService, mockHostListProviderService, mockPluginManagerService, - storageService); + mockStorageService, + mockMonitorService); Statement statement = wrapper.createStatement(); ResultSet resultSet = statement.executeQuery("some sql")) { return resultSet; @@ -345,7 +356,8 @@ public ResultSet executeStatementWithTelemetry() throws SQLException { mockPluginService, mockHostListProviderService, mockPluginManagerService, - storageService); + mockStorageService, + mockMonitorService); Statement statement = wrapper.createStatement(); ResultSet resultSet = statement.executeQuery("some sql")) { return resultSet; diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java index aa0a09594..6bcedf942 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java @@ -23,6 +23,7 @@ import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.wrapper.ConnectionWrapper; @@ -37,9 +38,10 @@ public TestConnectionWrapper(@NonNull Properties props, @NonNull PluginService pluginService, @NonNull HostListProviderService hostListProviderService, @NonNull PluginManagerService pluginManagerService, - @NonNull StorageService storageService) + @NonNull StorageService storageService, + @NonNull MonitorService monitorService) throws SQLException { super(props, url, connectionPluginManager, telemetryFactory, pluginService, hostListProviderService, - pluginManagerService, storageService); + pluginManagerService, storageService, monitorService); } } diff --git a/wrapper/build.gradle.kts b/wrapper/build.gradle.kts index 533b9ff47..231ca9d20 100644 --- a/wrapper/build.gradle.kts +++ b/wrapper/build.gradle.kts @@ -347,7 +347,7 @@ tasks.register("test-all-aurora") { systemProperty("test-no-performance", "true") systemProperty("test-no-mariadb-engine", "true") systemProperty("test-no-graalvm", "true") - systemProperty("test-no-openjdk8", "true") +// systemProperty("test-no-openjdk8", "true") systemProperty("test-no-multi-az", "true") } } @@ -360,7 +360,7 @@ tasks.register("test-all-multi-az") { systemProperty("test-no-performance", "true") systemProperty("test-no-mariadb-engine", "true") systemProperty("test-no-graalvm", "true") - systemProperty("test-no-openjdk8", "true") +// systemProperty("test-no-openjdk8", "true") systemProperty("test-no-aurora", "true") } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index b0f36d3ac..dc9783411 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -44,7 +44,6 @@ import software.amazon.jdbc.plugin.DataCacheConnectionPlugin; import software.amazon.jdbc.plugin.OpenedConnectionTracker; import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; -import software.amazon.jdbc.plugin.customendpoint.CustomEndpointPlugin; import software.amazon.jdbc.plugin.efm.MonitorThreadContainer; import software.amazon.jdbc.plugin.federatedauth.FederatedAuthCacheHolder; import software.amazon.jdbc.plugin.federatedauth.OktaAuthCacheHolder; @@ -63,7 +62,11 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; +import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.ServiceContainerImpl; import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.MonitorServiceImpl; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; @@ -80,6 +83,7 @@ public class Driver implements java.sql.Driver { private static @Nullable Driver registeredDriver; private static final StorageService storageService = new StorageServiceImpl(); + private static final MonitorService monitorService = new MonitorServiceImpl(); private static final AtomicReference resetSessionStateOnCloseCallable = new AtomicReference<>(null); @@ -220,15 +224,15 @@ public Connection connect(final String url, final Properties info) throws SQLExc effectiveConnectionProvider = configurationProfile.getConnectionProvider(); } + ServiceContainer serviceContainer = new ServiceContainerImpl(storageService, monitorService, telemetryFactory); return new ConnectionWrapper( + serviceContainer, props, driverUrl, defaultConnectionProvider, effectiveConnectionProvider, targetDriverDialect, - configurationProfile, - storageService, - telemetryFactory); + configurationProfile); } catch (Exception ex) { context.setException(ex); @@ -412,10 +416,10 @@ public static void clearCaches() { } public static void releaseResources() { + monitorService.stopAndRemoveAll(); software.amazon.jdbc.plugin.efm2.MonitorServiceImpl.closeAllMonitors(); MonitorThreadContainer.releaseInstance(); ConnectionProviderManager.releaseResources(); - CustomEndpointPlugin.closeMonitors(); HikariPoolsHolder.closeAllPools(); HostResponseTimeServiceImpl.closeAllMonitors(); MonitoringRdsHostListProvider.closeAllMonitors(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java index 1b8e7dcfc..64007139a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java @@ -29,7 +29,7 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.telemetry.TelemetryFactory; /** @@ -81,8 +81,7 @@ EnumSet setCurrentConnection( HostSpec getInitialConnectionHostSpec(); - // TODO: evaluate if there is a better way to pass the storage service to the monitors that need it. - StorageService getStorageService(); + ServiceContainer getServiceContainer(); /** * Set the collection of hosts that should be allowed and/or blocked for connections. diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index 827491540..04d1920be 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -55,7 +55,6 @@ import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.ItemCategory; -import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class PluginServiceImpl implements PluginService, CanReleaseResources, @@ -67,7 +66,6 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources, protected static final CacheMap hostAvailabilityExpiringCache = new CacheMap<>(); protected final ServiceContainer serviceContainer; protected final ConnectionPluginManager pluginManager; - protected final StorageService storageService; private final Properties props; private final String originalUrl; private final String driverProtocol; @@ -147,7 +145,6 @@ public PluginServiceImpl( this.exceptionManager = exceptionManager; this.dialectProvider = dialectProvider != null ? dialectProvider : new DialectManager(this); this.targetDriverDialect = targetDriverDialect; - this.storageService = serviceContainer.getStorageService(); this.connectionProviderManager = new ConnectionProviderManager( this.pluginManager.getDefaultConnProvider(), this.pluginManager.getEffectiveConnProvider()); @@ -213,14 +210,14 @@ public HostSpec getInitialConnectionHostSpec() { } @Override - public StorageService getStorageService() { - return this.storageService; + public ServiceContainer getServiceContainer() { + return this.serviceContainer; } @Override @Deprecated public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHosts) { - this.storageService.set( + this.serviceContainer.getStorageService().set( ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, this.initialConnectionHostSpec.getHost(), allowedAndBlockedHosts); } @@ -415,7 +412,7 @@ public List getAllHosts() { @Override public List getHosts() { - AllowedAndBlockedHosts hostPermissions = this.storageService.get( + AllowedAndBlockedHosts hostPermissions = this.serviceContainer.getStorageService().get( ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, this.initialConnectionHostSpec.getHost(), AllowedAndBlockedHosts.class); if (hostPermissions == null) { return this.allHosts; diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index 1946ee0e3..1c0e2bf21 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -49,9 +49,13 @@ import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; +import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.ServiceContainerImpl; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; +import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.MonitorServiceImpl; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; @@ -70,6 +74,7 @@ public class AwsWrapperDataSource implements DataSource, Referenceable, Serializ private static final String SERVER_PORT = "serverPort"; private static final StorageService storageService = new StorageServiceImpl(); + private static final MonitorService monitorService = new MonitorServiceImpl(); static { try { @@ -256,15 +261,15 @@ ConnectionWrapper createConnectionWrapper( final @NonNull TargetDriverDialect targetDriverDialect, final @Nullable ConfigurationProfile configurationProfile, final TelemetryFactory telemetryFactory) throws SQLException { + ServiceContainer serviceContainer = new ServiceContainerImpl(storageService, monitorService, telemetryFactory); return new ConnectionWrapper( + serviceContainer, props, url, defaultProvider, effectiveProvider, targetDriverDialect, - configurationProfile, - storageService, - telemetryFactory); + configurationProfile); } public void setTargetDataSourceClassName(@Nullable final String dataSourceClassName) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitor.java index fd8e82148..0e50d5844 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitor.java @@ -16,18 +16,13 @@ package software.amazon.jdbc.plugin.customendpoint; +import software.amazon.jdbc.util.monitoring.Monitor; + /** * Interface for custom endpoint monitors. Custom endpoint monitors analyze a given custom endpoint for custom endpoint * information and future changes to the endpoint. */ -public interface CustomEndpointMonitor extends AutoCloseable, Runnable { - - /** - * Evaluates whether the monitor should be disposed. - * - * @return true if the monitor should be disposed, otherwise return false. - */ - boolean shouldDispose(); +public interface CustomEndpointMonitor extends Monitor { /** * Indicates whether the monitor has info about the custom endpoint or not. This will be false if the monitor is new diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index d7f2b8986..5f27dc04c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -37,6 +37,8 @@ import software.amazon.jdbc.util.CacheMap; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.monitoring.AbstractMonitor; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; @@ -46,7 +48,7 @@ * The default custom endpoint monitor implementation. This class uses a background thread to monitor a given custom * endpoint for custom endpoint information and future changes to the custom endpoint. */ -public class CustomEndpointMonitorImpl implements CustomEndpointMonitor { +public class CustomEndpointMonitorImpl extends AbstractMonitor implements CustomEndpointMonitor { private static final Logger LOGGER = Logger.getLogger(CustomEndpointPlugin.class.getName()); private static final String TELEMETRY_ENDPOINT_INFO_CHANGED = "customEndpoint.infoChanged.counter"; @@ -76,6 +78,7 @@ public class CustomEndpointMonitorImpl implements CustomEndpointMonitor { /** * Constructs a CustomEndpointMonitorImpl instance for the host specified by {@code customEndpointHostSpec}. * + * @param monitorService The monitorService used to submit this monitor. * @param storageService The storage service used to store the set of allowed/blocked hosts according to the * custom endpoint info. * @param telemetryFactory The telemetry factory @@ -87,6 +90,7 @@ public class CustomEndpointMonitorImpl implements CustomEndpointMonitor { * information. */ public CustomEndpointMonitorImpl( + MonitorService monitorService, StorageService storageService, TelemetryFactory telemetryFactory, HostSpec customEndpointHostSpec, @@ -94,6 +98,7 @@ public CustomEndpointMonitorImpl( Region region, long refreshRateNano, BiFunction rdsClientFunc) { + super(monitorService); this.storageService = storageService; this.customEndpointHostSpec = customEndpointHostSpec; this.endpointIdentifier = endpointIdentifier; @@ -111,7 +116,7 @@ public CustomEndpointMonitorImpl( * Analyzes a given custom endpoint for changes to custom endpoint information. */ @Override - public void run() { + public void start() { LOGGER.fine( Messages.get( "CustomEndpointMonitorImpl.startingMonitor", @@ -208,16 +213,11 @@ public boolean hasCustomEndpointInfo() { return customEndpointInfoCache.get(this.customEndpointHostSpec.getHost()) != null; } - @Override - public boolean shouldDispose() { - return true; - } - /** * Stops the custom endpoint monitor. */ @Override - public void close() { + public void stop() { LOGGER.fine( Messages.get( "CustomEndpointMonitorImpl.stoppingMonitor", diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java index dfc7c534d..87df08af2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java @@ -37,7 +37,6 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUtils; import software.amazon.jdbc.util.RegionUtils; -import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SubscribedMethodHelper; import software.amazon.jdbc.util.WrapperUtils; @@ -51,21 +50,7 @@ public class CustomEndpointPlugin extends AbstractConnectionPlugin { private static final Logger LOGGER = Logger.getLogger(CustomEndpointPlugin.class.getName()); private static final String TELEMETRY_WAIT_FOR_INFO_COUNTER = "customEndpoint.waitForInfo.counter"; - - protected static final long CACHE_CLEANUP_RATE_NANO = TimeUnit.MINUTES.toNanos(1); protected static final RegionUtils regionUtils = new RegionUtils(); - protected static final SlidingExpirationCacheWithCleanupThread monitors = - new SlidingExpirationCacheWithCleanupThread<>( - CustomEndpointMonitor::shouldDispose, - (monitor) -> { - try { - monitor.close(); - } catch (Exception ex) { - // ignore - } - }, - CACHE_CLEANUP_RATE_NANO); - private static final Set subscribedMethods = Collections.unmodifiableSet(new HashSet() { { @@ -209,19 +194,19 @@ public Connection connect( * @param props The connection properties. */ protected CustomEndpointMonitor createMonitorIfAbsent(Properties props) { - return monitors.computeIfAbsent( + return this.pluginService.getServiceContainer().getMonitorService().runIfAbsent( + CustomEndpointMonitorImpl.class, this.customEndpointHostSpec.getHost(), - (customEndpoint) -> new CustomEndpointMonitorImpl( - this.pluginService.getStorageService(), + () -> new CustomEndpointMonitorImpl( + this.pluginService.getServiceContainer().getMonitorService(), + this.pluginService.getServiceContainer().getStorageService(), this.pluginService.getTelemetryFactory(), this.customEndpointHostSpec, this.customEndpointId, this.region, TimeUnit.MILLISECONDS.toNanos(CUSTOM_ENDPOINT_INFO_REFRESH_RATE_MS.getLong(props)), this.rdsClientFunc - ), - TimeUnit.MILLISECONDS.toNanos(this.idleMonitorExpirationMs) - ); + )); } @@ -307,13 +292,4 @@ public T execute( return jdbcMethodFunc.call(); } - - /** - * Closes all active custom endpoint monitors. - */ - public static void closeMonitors() { - LOGGER.info(Messages.get("CustomEndpointPlugin.closeMonitors")); - // The clear call automatically calls close() on all monitors. - monitors.clear(); - } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java index 7b1bf64d3..bab4eaf59 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java @@ -17,31 +17,35 @@ package software.amazon.jdbc.util; import software.amazon.jdbc.ConnectionPluginManager; -import software.amazon.jdbc.ConnectionProviderManager; import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public interface ServiceContainer { StorageService getStorageService(); - ConnectionPluginManager getConnectionPluginManager(); + MonitorService getMonitorService(); TelemetryFactory getTelemetryFactory(); + ConnectionPluginManager getConnectionPluginManager(); + HostListProviderService getHostListProviderService(); PluginService getPluginService(); PluginManagerService getPluginManagerService(); - StorageService setStorageService(StorageService storageService); + void setMonitorService(MonitorService monitorService); + + void setStorageService(StorageService storageService); - ConnectionPluginManager setConnectionPluginManager(ConnectionProviderManager connectionPluginManager); + void setTelemetryFactory(TelemetryFactory telemetryFactory); - TelemetryFactory setTelemetryFactory(TelemetryFactory telemetryFactory); + void setConnectionPluginManager(ConnectionPluginManager connectionPluginManager); void setHostListProviderService(HostListProviderService hostListProviderService); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java index 464e7545b..d05fdfffa 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java @@ -17,30 +17,32 @@ package software.amazon.jdbc.util; import software.amazon.jdbc.ConnectionPluginManager; -import software.amazon.jdbc.ConnectionProviderManager; import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class ServiceContainerImpl implements ServiceContainer { - private final StorageService storageService; - private final ConnectionPluginManager connectionPluginManager; - private final TelemetryFactory telemetryFactory; - + private StorageService storageService; + private MonitorService monitorService; + private TelemetryFactory telemetryFactory; + private ConnectionPluginManager connectionPluginManager; private HostListProviderService hostListProviderService; private PluginService pluginService; private PluginManagerService pluginManagerService; public ServiceContainerImpl( StorageService storageService, + MonitorService monitorService, ConnectionPluginManager connectionPluginManager, TelemetryFactory telemetryFactory, HostListProviderService hostListProviderService, PluginService pluginService, PluginManagerService pluginManagerService) { - this(storageService, connectionPluginManager, telemetryFactory); + this(storageService, monitorService, telemetryFactory); + this.connectionPluginManager = connectionPluginManager; this.hostListProviderService = hostListProviderService; this.pluginService = pluginService; this.pluginManagerService = pluginManagerService; @@ -48,61 +50,72 @@ public ServiceContainerImpl( public ServiceContainerImpl( StorageService storageService, - ConnectionPluginManager connectionPluginManager, + MonitorService monitorService, TelemetryFactory telemetryFactory) { this.storageService = storageService; - this.connectionPluginManager = connectionPluginManager; + this.monitorService = monitorService; this.telemetryFactory = telemetryFactory; } public StorageService getStorageService() { - return storageService; + return this.storageService; } - public ConnectionPluginManager getConnectionPluginManager() { - return connectionPluginManager; + public MonitorService getMonitorService() { + return this.monitorService; } public TelemetryFactory getTelemetryFactory() { - return telemetryFactory; + return this.telemetryFactory; + } + + public ConnectionPluginManager getConnectionPluginManager() { + return this.connectionPluginManager; } public HostListProviderService getHostListProviderService() { - return hostListProviderService; + return this.hostListProviderService; } public PluginService getPluginService() { - return pluginService; + return this.pluginService; } public PluginManagerService getPluginManagerService() { - return pluginManagerService; + return this.pluginManagerService; } @Override - public StorageService setStorageService(StorageService storageService) { - return null; + public void setMonitorService(MonitorService monitorService) { + this.monitorService = monitorService; } @Override - public ConnectionPluginManager setConnectionPluginManager(ConnectionProviderManager connectionPluginManager) { - return null; + public void setStorageService(StorageService storageService) { + this.storageService = storageService; } @Override - public TelemetryFactory setTelemetryFactory(TelemetryFactory telemetryFactory) { - return null; + public void setTelemetryFactory(TelemetryFactory telemetryFactory) { + this.telemetryFactory = telemetryFactory; } + @Override + public void setConnectionPluginManager(ConnectionPluginManager connectionPluginManager) { + this.connectionPluginManager = connectionPluginManager; + } + @Override public void setHostListProviderService(HostListProviderService hostListProviderService) { this.hostListProviderService = hostListProviderService; } + @Override public void setPluginService(PluginService pluginService) { this.pluginService = pluginService; } + @Override public void setPluginManagerService(PluginManagerService pluginManagerService) { this.pluginManagerService = pluginManagerService; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index e5a88dc8b..830e22377 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -27,11 +27,14 @@ public abstract class AbstractMonitor implements Monitor, Runnable { protected AbstractMonitor(MonitorService monitorService) { this.monitorService = monitorService; + this.lastUsedTimestampNanos = System.nanoTime(); } @Override public void run() { try { + this.state = MonitorState.RUNNING; + this.lastUsedTimestampNanos = System.nanoTime(); start(); } catch (Exception e) { LOGGER.fine(Messages.get("AbstractMonitor.unexpectedError", new Object[]{this, e})); @@ -41,7 +44,7 @@ public void run() { } @Override - public long getLastUsedTimestampNanos() { + public long getLastActivityTimestampNanos() { return this.lastUsedTimestampNanos; } @@ -49,4 +52,9 @@ public long getLastUsedTimestampNanos() { public MonitorState getState() { return this.state; } + + @Override + public boolean canDispose() { + return true; + } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index a6218c50f..6f9414287 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -21,7 +21,7 @@ public interface Monitor { void stop(); - long getLastUsedTimestampNanos(); + long getLastActivityTimestampNanos(); MonitorState getState(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index bb8598c58..5bf75bcca 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -54,8 +54,9 @@ void registerMonitorTypeIfAbsent( * @param key the key for the monitor, eg * "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". * @param monitorSupplier a supplier lambda that can be used to create the monitor if it is absent. + * @return the new or existing monitor. */ - void runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier); + T runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier); /** * Process a monitor error. The monitor service will respond to the error based on the monitor error responses defined diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 531c9c7a5..37a19adb4 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -16,6 +16,7 @@ package software.amazon.jdbc.util.monitoring; +import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -28,6 +29,7 @@ import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ShouldDisposeFunc; import software.amazon.jdbc.util.storage.ExternallyManagedCache; @@ -45,7 +47,11 @@ public class MonitorServiceImpl implements MonitorService { })); static { - // TODO: add default caches once monitors have been adjusted to implement the Monitor interface + Set resetErrorResponse = new HashSet<>(1); + resetErrorResponse.add(MonitorErrorResponse.RESTART); + MonitorSettings customEndpointSettings = new MonitorSettings( + TimeUnit.MINUTES.toNanos(5), TimeUnit.MINUTES.toNanos(1), resetErrorResponse); + monitorCaches.put(CustomEndpointMonitorImpl.class, new CacheContainer(customEndpointSettings, null)); } public MonitorServiceImpl() { @@ -80,6 +86,10 @@ protected void checkMonitors() { LOGGER.finest(Messages.get("MonitorServiceImpl.checkingMonitors")); for (CacheContainer container : monitorCaches.values()) { ExternallyManagedCache cache = container.getCache(); + if (cache == null) { + continue; + } + // Note: the map returned by getEntries is a copy of the ExternallyManagedCache map for (Map.Entry entry : cache.getEntries().entrySet()) { Object key = entry.getKey(); @@ -92,19 +102,19 @@ protected void checkMonitors() { MonitorSettings monitorSettings = container.getSettings(); removedItem = cache.removeIf(key, mi -> mi.getMonitor().getState() == MonitorState.ERROR); if (removedItem != null) { - handleMonitorError(container, key, removedItem); + handleMonitorError(monitorSettings.getErrorResponses(), cache, key, removedItem); continue; } long inactiveTimeoutNanos = monitorSettings.getInactiveTimeoutNanos(); removedItem = cache.removeIf( - key, mi -> System.nanoTime() - mi.getMonitor().getLastUsedTimestampNanos() > inactiveTimeoutNanos); + key, mi -> System.nanoTime() - mi.getMonitor().getLastActivityTimestampNanos() > inactiveTimeoutNanos); if (removedItem != null) { // Monitor has been inactive for longer than the inactive timeout and is considered stuck. LOGGER.fine( Messages.get("MonitorServiceImpl.monitorStuck", new Object[]{removedItem.getMonitor(), TimeUnit.NANOSECONDS.toSeconds(inactiveTimeoutNanos)})); - handleMonitorError(container, key, removedItem); + handleMonitorError(monitorSettings.getErrorResponses(), cache, key, removedItem); continue; } @@ -116,14 +126,16 @@ protected void checkMonitors() { } } - private void handleMonitorError(CacheContainer container, Object key, MonitorItem monitorItem) { + private void handleMonitorError( + Set errorResponses, + ExternallyManagedCache cache, + Object key, + MonitorItem monitorItem) { Monitor monitor = monitorItem.getMonitor(); monitor.stop(); - Set errorResponses = container.getSettings().getErrorResponses(); if (errorResponses.contains(MonitorErrorResponse.RESTART)) { LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); - ExternallyManagedCache cache = container.getCache(); cache.computeIfAbsent(key, k -> new MonitorItem(monitorItem.getMonitorSupplier())); } } @@ -140,19 +152,33 @@ public void registerMonitorTypeIfAbsent( mc -> { ExternallyManagedCache cache = new ExternallyManagedCache<>(true, expirationTimeoutNanos); - return new CacheContainer(cache, new MonitorSettings(heartbeatTimeoutNanos, errorResponses)); + return new CacheContainer( + new MonitorSettings(expirationTimeoutNanos, heartbeatTimeoutNanos, errorResponses), cache); }); } @Override - public void runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier) { + public T runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier) { CacheContainer cacheContainer = monitorCaches.get(monitorClass); if (cacheContainer == null) { throw new IllegalStateException( Messages.get("MonitorServiceImpl.monitorTypeNotRegistered", new Object[] {monitorClass})); } - cacheContainer.getCache().computeIfAbsent(key, k -> new MonitorItem(monitorSupplier)); + ExternallyManagedCache cache = cacheContainer.getCache(); + if (cache == null) { + MonitorSettings monitorSettings = cacheContainer.getSettings(); + cache = new ExternallyManagedCache<>(true, monitorSettings.getExpirationTimeoutNanos()); + cacheContainer.setCache(cache); + } + + Monitor monitor = cache.computeIfAbsent(key, k -> new MonitorItem(monitorSupplier)).getMonitor(); + if (monitorClass.isInstance(monitor)) { + return monitorClass.cast(monitor); + } + + throw new IllegalStateException( + Messages.get("MonitorServiceImpl.unexpectedMonitorClass", new Object[] {monitorClass, monitor})); } @Override @@ -164,10 +190,15 @@ public void reportMonitorError(Monitor monitor, Exception exception) { } ExternallyManagedCache cache = cacheContainer.getCache(); + if (cache == null) { + LOGGER.fine(Messages.get("MonitorServiceImpl.monitorErrorForNullCache", new Object[] {monitor, exception})); + return; + } + for (Map.Entry entry : cache.getEntries().entrySet()) { MonitorItem monitorItem = cache.removeIf(entry.getKey(), mi -> mi.getMonitor() == monitor); if (monitorItem != null) { - handleMonitorError(cacheContainer, entry.getKey(), monitorItem); + handleMonitorError(cacheContainer.getSettings().getErrorResponses(), cache, entry.getKey(), monitorItem); return; } } @@ -179,10 +210,17 @@ public void reportMonitorError(Monitor monitor, Exception exception) { public void stopAndRemove(Class monitorClass, Object key) { CacheContainer cacheContainer = monitorCaches.get(monitorClass); if (cacheContainer == null) { + LOGGER.fine(Messages.get("MonitorServiceImpl.stopAndRemoveMissingMonitorType", new Object[] {monitorClass, key})); + return; + } + + ExternallyManagedCache cache = cacheContainer.getCache(); + if (cache == null) { + LOGGER.fine(Messages.get("MonitorServiceImpl.stopAndRemoveNullCache", new Object[] {monitorClass, key})); return; } - MonitorItem monitorItem = cacheContainer.getCache().remove(key); + MonitorItem monitorItem = cache.remove(key); if (monitorItem != null) { monitorItem.getMonitor().stop(); } @@ -192,10 +230,16 @@ public void stopAndRemove(Class monitorClass, Object key) public void stopAndRemoveMonitors(Class monitorClass) { CacheContainer cacheContainer = monitorCaches.get(monitorClass); if (cacheContainer == null) { + LOGGER.fine(Messages.get("MonitorServiceImpl.stopAndRemoveMonitorsMissingType", new Object[] {monitorClass})); return; } ExternallyManagedCache cache = cacheContainer.getCache(); + if (cache == null) { + LOGGER.fine(Messages.get("MonitorServiceImpl.stopAndRemoveMonitorsNullCache", new Object[] {monitorClass})); + return; + } + for (Map.Entry entry : cache.getEntries().entrySet()) { MonitorItem monitorItem = cache.remove(entry.getKey()); if (monitorItem != null) { @@ -212,21 +256,25 @@ public void stopAndRemoveAll() { } protected static class CacheContainer { - private @NonNull final ExternallyManagedCache cache; private @NonNull final MonitorSettings settings; + private @Nullable ExternallyManagedCache cache; public CacheContainer( - @NonNull ExternallyManagedCache cache, @NonNull MonitorSettings settings) { + @NonNull MonitorSettings settings, @Nullable ExternallyManagedCache cache) { this.settings = settings; this.cache = cache; } - public @NonNull ExternallyManagedCache getCache() { + public @NonNull MonitorSettings getSettings() { + return settings; + } + + public @Nullable ExternallyManagedCache getCache() { return cache; } - public @NonNull MonitorSettings getSettings() { - return settings; + public void setCache(@NonNull ExternallyManagedCache cache) { + this.cache = cache; } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java index b8bbf2483..8fd030ebe 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java @@ -20,14 +20,21 @@ import org.checkerframework.checker.nullness.qual.NonNull; public class MonitorSettings { + private final long expirationTimeoutNanos; private final long inactiveTimeoutNanos; private @NonNull final Set errorResponses; - public MonitorSettings(long inactiveTimeoutNanos, @NonNull Set errorResponses) { + public MonitorSettings( + long expirationTimeoutNanos, long inactiveTimeoutNanos, @NonNull Set errorResponses) { + this.expirationTimeoutNanos = expirationTimeoutNanos; this.inactiveTimeoutNanos = inactiveTimeoutNanos; this.errorResponses = errorResponses; } + public long getExpirationTimeoutNanos() { + return expirationTimeoutNanos; + } + public long getInactiveTimeoutNanos() { return inactiveTimeoutNanos; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java index c37b74e0d..92864cc6c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java @@ -24,6 +24,7 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.logging.Logger; +import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; public class ExternallyManagedCache { @@ -37,7 +38,7 @@ public ExternallyManagedCache(boolean isRenewableExpiration, long timeToLiveNano this.timeToLiveNanos = timeToLiveNanos; } - public @Nullable V put(K key, V value) { + public @Nullable V put(@NonNull K key, @NonNull V value) { CacheItem cacheItem = this.cache.put(key, new CacheItem(value, timeToLiveNanos)); if (cacheItem == null) { return null; @@ -46,7 +47,7 @@ public ExternallyManagedCache(boolean isRenewableExpiration, long timeToLiveNano return cacheItem.item; } - public @Nullable V computeIfAbsent(K key, Function mappingFunction) { + public @NonNull V computeIfAbsent(K key, Function mappingFunction) { final CacheItem cacheItem = cache.compute( key, (k, valueItem) -> { @@ -131,7 +132,7 @@ public Map getEntries() { } protected class CacheItem { - private final V item; + private final @NonNull V item; private long expirationTimeNanos; /** @@ -140,7 +141,7 @@ protected class CacheItem { * @param item the item value * @param expirationTimeNanos the amount of time before a CacheItem should be marked as expired. */ - protected CacheItem(final V item, final long expirationTimeNanos) { + protected CacheItem(@NonNull final V item, final long expirationTimeNanos) { this.item = item; this.expirationTimeNanos = expirationTimeNanos; } @@ -160,7 +161,7 @@ protected void extendExpiration() { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((item == null) ? 0 : item.hashCode()); + result = prime * result + item.hashCode(); return result; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index d3b62e135..d32085041 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -57,6 +57,7 @@ import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -79,14 +80,13 @@ public class ConnectionWrapper implements Connection, CanReleaseResources { protected final ConnectionUrlParser connectionUrlParser = new ConnectionUrlParser(); public ConnectionWrapper( + @NonNull final ServiceContainer serviceContainer, @NonNull final Properties props, @NonNull final String url, @NonNull final ConnectionProvider defaultConnectionProvider, @Nullable final ConnectionProvider effectiveConnectionProvider, @NonNull final TargetDriverDialect targetDriverDialect, - @Nullable final ConfigurationProfile configurationProfile, - @NonNull final StorageService storageService, - @NonNull final TelemetryFactory telemetryFactory) + @Nullable final ConfigurationProfile configurationProfile) throws SQLException { if (StringUtils.isNullOrEmpty(url)) { @@ -102,8 +102,8 @@ public ConnectionWrapper( defaultConnectionProvider, effectiveConnectionProvider, this, - telemetryFactory); - ServiceContainer serviceContainer = new ServiceContainerImpl(storageService, pluginManager, telemetryFactory); + serviceContainer.getTelemetryFactory()); + serviceContainer.setConnectionPluginManager(pluginManager); final PluginServiceImpl pluginService = new PluginServiceImpl( serviceContainer, props, @@ -131,7 +131,8 @@ protected ConnectionWrapper( @NonNull final PluginService pluginService, @NonNull final HostListProviderService hostListProviderService, @NonNull final PluginManagerService pluginManagerService, - @NonNull final StorageService storageService) + @NonNull final StorageService storageService, + @NonNull final MonitorService monitorService) throws SQLException { if (StringUtils.isNullOrEmpty(url)) { @@ -140,6 +141,7 @@ protected ConnectionWrapper( ServiceContainer serviceContainer = new ServiceContainerImpl( storageService, + monitorService, connectionPluginManager, telemetryFactory, hostListProviderService, diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 4d868b787..af2c4a4f0 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -295,9 +295,16 @@ MonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should # monitoring.MonitorServiceImpl MonitorServiceImpl.monitorErrorForMissingMonitor=Monitor ''{0}'' reported an error to the monitor service, but the monitor service could not find the monitor under its registered monitors. Reported error: ''{1}''. +MonitorServiceImpl.monitorErrorForNullCache=Monitor ''{0}'' reported an error to the monitor service, but the cache for the monitor type was null. Reported error: ''{1}''. MonitorServiceImpl.monitorStuck=Monitor ''{0}'' has not been updated within the inactive timeout of {1} seconds. MonitorServiceImpl.monitorTypeNotRegistered=The given monitor class ''{0}'' is not registered. Please register the monitor class before running monitors of that class with the monitor service. MonitorServiceImpl.restartingMonitor=Restarting monitor: ''{0}''. +MonitorServiceImpl.stopAndRemoveMissingMonitorType=The monitor service received a request to stop a monitor with type ''{0}'' and key ''{1}'', but the monitor service does not have any monitors registered under the given type. Please ensure monitors are registered under the correct type. +MonitorServiceImpl.stopAndRemoveMonitorsMissingType=The monitor service received a request to stop all monitors with type ''{0}'', but the monitor service does not have any monitors registered under the given type. Please ensure monitors are registered under the correct type. +MonitorServiceImpl.stopAndRemoveMonitorsNullCache=The monitor service received a request to stop all monitors with type ''{0}'', but the cache for the given type is null. Please ensure monitors are registered under the correct type. +MonitorServiceImpl.stopAndRemoveNullCache=The monitor service received a request to stop a monitor with type ''{0}'' and key ''{1}'', but the cache for the given type is null. Please ensure monitors are registered under the correct type. + +MonitorServiceImpl.unexpectedMonitorClass=Monitor type mismatch - the monitor ''{0}'' was unexpectedly found under the ''{1}'' monitor class category. Please verify that monitors are submitted under their concrete class. MonitorServiceImpl.unregisteredMonitorError=Monitor ''{0}'' reported an error to the monitor service, but the monitor type has not been registered. Reported error: ''{1}''. NodeMonitoringThread.detectedWriter=Writer detected by node monitoring thread: ''{0}''. diff --git a/wrapper/src/test/build.gradle.kts b/wrapper/src/test/build.gradle.kts index 89e63ddbf..36ca4fda0 100644 --- a/wrapper/src/test/build.gradle.kts +++ b/wrapper/src/test/build.gradle.kts @@ -100,5 +100,5 @@ tasks.register("in-container") { // modify below filter to select specific integration tests // see https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/testing/TestFilter.html - filter.includeTestsMatching("integration.container.tests.*") + filter.includeTestsMatching("*.test_writerFailover_writerReelected") } diff --git a/wrapper/src/test/java/integration/container/tests/CustomEndpointTest.java b/wrapper/src/test/java/integration/container/tests/CustomEndpointTest.java index 9c0fdaab4..677a58366 100644 --- a/wrapper/src/test/java/integration/container/tests/CustomEndpointTest.java +++ b/wrapper/src/test/java/integration/container/tests/CustomEndpointTest.java @@ -44,7 +44,6 @@ import java.util.List; import java.util.Properties; import java.util.Set; -import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -78,11 +77,11 @@ @Order(16) public class CustomEndpointTest { private static final Logger LOGGER = Logger.getLogger(CustomEndpointTest.class.getName()); - protected static final String endpointId = "test-endpoint-1-" + UUID.randomUUID(); + protected static final String endpointId = "aurora-pg"; protected static DBClusterEndpoint endpointInfo; protected static final AuroraTestUtility auroraUtil = AuroraTestUtility.getUtility(); - protected static final boolean reuseExistingEndpoint = false; + protected static final boolean reuseExistingEndpoint = true; protected String currentWriter; diff --git a/wrapper/src/test/java/integration/host/TestEnvironmentConfiguration.java b/wrapper/src/test/java/integration/host/TestEnvironmentConfiguration.java index b20b17523..b72106c5d 100644 --- a/wrapper/src/test/java/integration/host/TestEnvironmentConfiguration.java +++ b/wrapper/src/test/java/integration/host/TestEnvironmentConfiguration.java @@ -27,17 +27,17 @@ public class TestEnvironmentConfiguration { public boolean noPerformance = Boolean.parseBoolean(System.getProperty("test-no-performance", "false")); public boolean noMysqlEngine = - Boolean.parseBoolean(System.getProperty("test-no-mysql-engine", "false")); + Boolean.parseBoolean(System.getProperty("test-no-mysql-engine", "true")); public boolean noMysqlDriver = - Boolean.parseBoolean(System.getProperty("test-no-mysql-driver", "false")); + Boolean.parseBoolean(System.getProperty("test-no-mysql-driver", "true")); public boolean noPgEngine = Boolean.parseBoolean(System.getProperty("test-no-pg-engine", "false")); public boolean noPgDriver = Boolean.parseBoolean(System.getProperty("test-no-pg-driver", "false")); public boolean noMariadbEngine = - Boolean.parseBoolean(System.getProperty("test-no-mariadb-engine", "false")); + Boolean.parseBoolean(System.getProperty("test-no-mariadb-engine", "true")); public boolean noMariadbDriver = - Boolean.parseBoolean(System.getProperty("test-no-mariadb-driver", "false")); + Boolean.parseBoolean(System.getProperty("test-no-mariadb-driver", "true")); public boolean noFailover = Boolean.parseBoolean(System.getProperty("test-no-failover", "false")); public boolean noIam = @@ -47,24 +47,24 @@ public class TestEnvironmentConfiguration { public boolean noHikari = Boolean.parseBoolean(System.getProperty("test-no-hikari", "false")); public boolean noGraalVm = - Boolean.parseBoolean(System.getProperty("test-no-graalvm", "false")); + Boolean.parseBoolean(System.getProperty("test-no-graalvm", "true")); public boolean noOpenJdk = Boolean.parseBoolean(System.getProperty("test-no-openjdk", "false")); public boolean noOpenJdk8 = Boolean.parseBoolean(System.getProperty("test-no-openjdk8", "false")); public boolean noOpenJdk11 = - Boolean.parseBoolean(System.getProperty("test-no-openjdk11", "false")); + Boolean.parseBoolean(System.getProperty("test-no-openjdk11", "true")); public boolean testHibernateOnly = Boolean.parseBoolean(System.getProperty("test-hibernate-only", "false")); public boolean testAutoscalingOnly = Boolean.parseBoolean(System.getProperty("test-autoscaling-only", "false")); public boolean noInstances1 = - Boolean.parseBoolean(System.getProperty("test-no-instances-1", "false")); + Boolean.parseBoolean(System.getProperty("test-no-instances-1", "true")); public boolean noInstances2 = - Boolean.parseBoolean(System.getProperty("test-no-instances-2", "false")); + Boolean.parseBoolean(System.getProperty("test-no-instances-2", "true")); public boolean noInstances3 = - Boolean.parseBoolean(System.getProperty("test-no-instances-3", "false")); + Boolean.parseBoolean(System.getProperty("test-no-instances-3", "true")); public boolean noInstances5 = Boolean.parseBoolean(System.getProperty("test-no-instances-5", "false")); diff --git a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java index d50bd58ce..7a6301036 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java @@ -52,10 +52,6 @@ import software.amazon.jdbc.exceptions.ExceptionManager; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.ServiceContainerImpl; -import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.util.storage.StorageServiceImpl; -import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class DialectDetectionTests { private static final String LOCALHOST = "localhost"; @@ -65,10 +61,9 @@ public class DialectDetectionTests { private static final String PG_PROTOCOL = "jdbc:postgresql://"; private static final String MARIA_PROTOCOL = "jdbc:mariadb://"; private final DialectManager dialectManager = new DialectManager(null); - private final StorageService storageService = new StorageServiceImpl(); private final Properties props = new Properties(); - private ServiceContainer serviceContainer; private AutoCloseable closeable; + @Mock private ServiceContainer mockServiceContainer; @Mock private HostListProvider mockHostListProvider; @Mock private Connection mockConnection; @Mock private Statement mockStatement; @@ -77,16 +72,15 @@ public class DialectDetectionTests { @Mock private HostSpec mockHost; @Mock private ConnectionPluginManager mockPluginManager; @Mock private TargetDriverDialect mockTargetDriverDialect; - @Mock private TelemetryFactory mockTelemetryFactory; @Mock private ResultSetMetaData mockResultSetMetaData; @BeforeEach void setUp() throws SQLException { closeable = MockitoAnnotations.openMocks(this); + when(this.mockServiceContainer.getConnectionPluginManager()).thenReturn(mockPluginManager); when(this.mockConnection.createStatement()).thenReturn(this.mockStatement); when(this.mockHost.getUrl()).thenReturn("url"); when(this.mockFailResultSet.next()).thenReturn(false); - serviceContainer = new ServiceContainerImpl(storageService, mockPluginManager, mockTelemetryFactory); mockPluginManager.plugins = new ArrayList<>(); } @@ -99,7 +93,7 @@ void cleanUp() throws Exception { PluginServiceImpl getPluginService(String protocol) throws SQLException { return spy( new PluginServiceImpl( - serviceContainer, + mockServiceContainer, new ExceptionManager(), props, protocol + DialectDetectionTests.LOCALHOST, @@ -175,6 +169,7 @@ void testUpdateDialectMysqlToAurora() throws SQLException { when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'aurora_version'")).thenReturn(mockSuccessResultSet); when(mockSuccessResultSet.next()).thenReturn(true, false); final PluginServiceImpl target = getPluginService(MYSQL_PROTOCOL); + when(mockServiceContainer.getPluginService()).thenReturn(target); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(AuroraMysqlDialect.class, target.dialect.getClass()); @@ -277,6 +272,7 @@ void testUpdateDialectMariaToMysqlAurora() throws SQLException { when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'aurora_version'")).thenReturn(mockSuccessResultSet); when(mockSuccessResultSet.next()).thenReturn(true, false); final PluginServiceImpl target = getPluginService(MARIA_PROTOCOL); + when(mockServiceContainer.getPluginService()).thenReturn(target); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(AuroraMysqlDialect.class, target.dialect.getClass()); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java index 70e84b26b..3ff241dda 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java @@ -73,9 +73,6 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Pair; import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.ServiceContainerImpl; -import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.telemetry.GaugeCallable; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; @@ -108,12 +105,11 @@ public class AwsSecretsManagerConnectionPluginTest { private static final GetSecretValueResponse INVALID_GET_SECRET_VALUE_RESPONSE = GetSecretValueResponse.builder().secretString(INVALID_SECRET_STRING).build(); private static final Properties TEST_PROPS = new Properties(); - private static final StorageService storageService = new StorageServiceImpl(); - private static ServiceContainer serviceContainer; private AwsSecretsManagerConnectionPlugin plugin; private AutoCloseable closeable; + @Mock ServiceContainer mockServiceContainer; @Mock SecretsManagerClient mockSecretsManagerClient; @Mock GetSecretValueRequest mockGetValueRequest; @Mock JdbcCallable connectFunc; @@ -140,6 +136,7 @@ public void init() throws SQLException { when(mockDialectManager.getDialect(anyString(), anyString(), any(Properties.class))) .thenReturn(mockTopologyAwareDialect); + when(mockServiceContainer.getConnectionPluginManager()).thenReturn(mockConnectionPluginManager); when(mockService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockConnectionPluginManager.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockTelemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(mockTelemetryContext); @@ -148,7 +145,6 @@ public void init() throws SQLException { // noinspection unchecked when(mockTelemetryFactory.createGauge(anyString(), any(GaugeCallable.class))).thenReturn(mockTelemetryGauge); - serviceContainer = new ServiceContainerImpl(storageService, mockConnectionPluginManager, mockTelemetryFactory); this.plugin = new AwsSecretsManagerConnectionPlugin( mockService, TEST_PROPS, @@ -284,7 +280,7 @@ public void testConnectWithNewSecretsAfterTryingWithCachedSecrets( private @NotNull PluginServiceImpl getPluginService(String protocol) throws SQLException { return new PluginServiceImpl( - serviceContainer, + mockServiceContainer, new ExceptionManager(), TEST_PROPS, "url", @@ -457,7 +453,7 @@ public void testConnectViaARN(final String arn, final Region expectedRegionParse SECRET_ID_PROPERTY.set(props, arn); this.plugin = spy(new AwsSecretsManagerConnectionPlugin( - new PluginServiceImpl(serviceContainer, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect), + new PluginServiceImpl(mockServiceContainer, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect), props, (host, r) -> mockSecretsManagerClient, (id) -> mockGetValueRequest)); @@ -477,7 +473,7 @@ public void testConnectionWithRegionParameterAndARN(final String arn, final Regi REGION_PROPERTY.set(props, expectedRegion.toString()); this.plugin = spy(new AwsSecretsManagerConnectionPlugin( - new PluginServiceImpl(serviceContainer, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect), + new PluginServiceImpl(mockServiceContainer, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect), props, (host, r) -> mockSecretsManagerClient, (id) -> mockGetValueRequest)); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java index 1e5f1c39a..f8218ffe9 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java @@ -49,12 +49,14 @@ import software.amazon.jdbc.HostSpecBuilder; import software.amazon.jdbc.hostavailability.HostAvailabilityStrategy; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class CustomEndpointMonitorImplTest { + @Mock private MonitorService mockMonitorService; @Mock private StorageService mockStorageService; @Mock private BiFunction mockRdsClientFunc; @Mock private RdsClient mockRdsClient; @@ -109,12 +111,12 @@ public void init() throws SQLException { @AfterEach void cleanUp() throws Exception { closeable.close(); - CustomEndpointPlugin.monitors.clear(); } @Test public void testRun() throws InterruptedException { CustomEndpointMonitorImpl monitor = new CustomEndpointMonitorImpl( + mockMonitorService, mockStorageService, mockTelemetryFactory, host, @@ -127,7 +129,7 @@ public void testRun() throws InterruptedException { // will return the expected number of endpoints (one). TimeUnit.MILLISECONDS.sleep(100); assertEquals(expectedInfo, CustomEndpointMonitorImpl.customEndpointInfoCache.get(host.getHost())); - monitor.close(); + monitor.stop(); ArgumentCaptor captor = ArgumentCaptor.forClass(AllowedAndBlockedHosts.class); verify(mockStorageService).set(eq(ItemCategory.ALLOWED_AND_BLOCKED_HOSTS), eq(host.getHost()), captor.capture()); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java index e6723d35b..9595687cf 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java @@ -19,7 +19,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -32,7 +31,6 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; -import java.util.concurrent.TimeUnit; import java.util.function.BiFunction; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -83,7 +81,6 @@ public void init() throws SQLException { void cleanUp() throws Exception { closeable.close(); props.clear(); - CustomEndpointPlugin.monitors.clear(); } private CustomEndpointPlugin getSpyPlugin() { @@ -147,14 +144,4 @@ public void testExecute_monitorCreated() throws SQLException { verify(spyPlugin, times(1)).createMonitorIfAbsent(eq(props)); verify(mockJdbcMethodFunc, times(1)).call(); } - - @Test - public void testCloseMonitors() throws Exception { - CustomEndpointPlugin.monitors.computeIfAbsent("test-monitor", (key) -> mockMonitor, TimeUnit.SECONDS.toNanos(30)); - - CustomEndpointPlugin.closeMonitors(); - - // close() may be called by the cleanup thread in addition to the call below. - verify(mockMonitor, atLeastOnce()).close(); - } } diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java index d141fb613..5f2e45e5e 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java @@ -17,11 +17,11 @@ package software.amazon.jdbc.plugin.dev; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; @@ -38,27 +38,30 @@ import org.mockito.MockitoAnnotations; import software.amazon.jdbc.ConnectionPluginManager; import software.amazon.jdbc.ConnectionProvider; -import software.amazon.jdbc.PluginServiceImpl; import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.dialect.DialectCodes; import software.amazon.jdbc.dialect.DialectManager; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.ServiceContainerImpl; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.wrapper.ConnectionWrapper; +@SuppressWarnings({"resource"}) public class DeveloperConnectionPluginTest { - + private ServiceContainer serviceContainer; + @Mock StorageService mockStorageService; + @Mock MonitorService mockMonitorService; @Mock ConnectionProvider mockConnectionProvider; @Mock Connection mockConnection; - @Mock PluginServiceImpl mockService; @Mock ConnectionPluginManager mockConnectionPluginManager; @Mock ExceptionSimulatorConnectCallback mockConnectCallback; @Mock private TelemetryFactory mockTelemetryFactory; @Mock TelemetryContext mockTelemetryContext; @Mock TargetDriverDialect mockTargetDriverDialect; - @Mock StorageService mockStorageService; private AutoCloseable closeable; @@ -70,31 +73,31 @@ void cleanUp() throws Exception { @BeforeEach void init() throws SQLException { closeable = MockitoAnnotations.openMocks(this); + serviceContainer = new ServiceContainerImpl(mockStorageService, mockMonitorService, mockTelemetryFactory); when(mockConnectionProvider.connect(any(), any(), any(), any(), any())).thenReturn(mockConnection); when(mockConnectCallback.getExceptionToRaise(any(), any(), any(), anyBoolean())).thenReturn(null); - when(mockService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockConnectionPluginManager.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockTelemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(mockTelemetryContext); when(mockTelemetryFactory.openTelemetryContext(eq(null), any())).thenReturn(mockTelemetryContext); } @Test + @SuppressWarnings("try") public void test_RaiseException() throws SQLException { final Properties props = new Properties(); props.put(PropertyDefinition.PLUGINS.name, "dev"); props.put(DialectManager.DIALECT.name, DialectCodes.PG); try (ConnectionWrapper wrapper = new ConnectionWrapper( + serviceContainer, props, "any-protocol://any-host/", mockConnectionProvider, null, mockTargetDriverDialect, - null, - mockStorageService, - mockTelemetryFactory)) { + null)) { ExceptionSimulator simulator = wrapper.unwrap(ExceptionSimulator.class); assertNotNull(simulator); @@ -117,14 +120,13 @@ public void test_RaiseExceptionForMethodName() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "dev"); props.put(DialectManager.DIALECT.name, DialectCodes.PG); try (ConnectionWrapper wrapper = new ConnectionWrapper( + serviceContainer, props, "any-protocol://any-host/", mockConnectionProvider, null, mockTargetDriverDialect, - null, - mockStorageService, - mockTelemetryFactory)) { + null)) { ExceptionSimulator simulator = wrapper.unwrap(ExceptionSimulator.class); assertNotNull(simulator); @@ -147,14 +149,13 @@ public void test_RaiseExceptionForAnyMethodName() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "dev"); props.put(DialectManager.DIALECT.name, DialectCodes.PG); try (ConnectionWrapper wrapper = new ConnectionWrapper( + serviceContainer, props, "any-protocol://any-host/", mockConnectionProvider, null, mockTargetDriverDialect, - null, - mockStorageService, - mockTelemetryFactory)) { + null)) { ExceptionSimulator simulator = wrapper.unwrap(ExceptionSimulator.class); assertNotNull(simulator); @@ -177,14 +178,13 @@ public void test_RaiseExceptionForWrongMethodName() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "dev"); props.put(DialectManager.DIALECT.name, DialectCodes.PG); try (ConnectionWrapper wrapper = new ConnectionWrapper( + serviceContainer, props, "any-protocol://any-host/", mockConnectionProvider, null, mockTargetDriverDialect, - null, - mockStorageService, - mockTelemetryFactory)) { + null)) { ExceptionSimulator simulator = wrapper.unwrap(ExceptionSimulator.class); assertNotNull(simulator); @@ -209,14 +209,13 @@ public void test_RaiseExpectedExceptionClass() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "dev"); props.put(DialectManager.DIALECT.name, DialectCodes.PG); try (ConnectionWrapper wrapper = new ConnectionWrapper( + serviceContainer, props, "any-protocol://any-host/", mockConnectionProvider, null, mockTargetDriverDialect, - null, - mockStorageService, - mockTelemetryFactory)) { + null)) { ExceptionSimulator simulator = wrapper.unwrap(ExceptionSimulator.class); assertNotNull(simulator); @@ -239,14 +238,13 @@ public void test_RaiseUnexpectedExceptionClass() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "dev"); props.put(DialectManager.DIALECT.name, DialectCodes.PG); try (ConnectionWrapper wrapper = new ConnectionWrapper( + serviceContainer, props, "any-protocol://any-host/", mockConnectionProvider, null, mockTargetDriverDialect, - null, - mockStorageService, - mockTelemetryFactory)) { + null)) { ExceptionSimulator simulator = wrapper.unwrap(ExceptionSimulator.class); assertNotNull(simulator); @@ -258,7 +256,7 @@ public void test_RaiseUnexpectedExceptionClass() throws SQLException { Throwable thrownException = assertThrows(SQLException.class, wrapper::createStatement); assertNotNull(thrownException); assertNotSame(exception, thrownException); - assertTrue(thrownException instanceof SQLException); + assertInstanceOf(SQLException.class, thrownException); assertNotNull(thrownException.getCause()); assertSame(thrownException.getCause(), exception); @@ -278,25 +276,25 @@ public void test_RaiseExceptionOnConnect() { Throwable thrownException = assertThrows( SQLException.class, - () -> new ConnectionWrapper(props, + () -> new ConnectionWrapper( + serviceContainer, + props, "any-protocol://any-host/", mockConnectionProvider, null, mockTargetDriverDialect, - null, - mockStorageService, - mockTelemetryFactory)); + null)); assertSame(exception, thrownException); assertDoesNotThrow( - () -> new ConnectionWrapper(props, + () -> new ConnectionWrapper( + serviceContainer, + props, "any-protocol://any-host/", mockConnectionProvider, null, mockTargetDriverDialect, - null, - mockStorageService, - mockTelemetryFactory)); + null)); } @Test @@ -309,14 +307,14 @@ public void test_NoExceptionOnConnectWithCallback() { ExceptionSimulatorManager.setCallback(mockConnectCallback); assertDoesNotThrow( - () -> new ConnectionWrapper(props, + () -> new ConnectionWrapper( + serviceContainer, + props, "any-protocol://any-host/", mockConnectionProvider, null, mockTargetDriverDialect, - null, - mockStorageService, - mockTelemetryFactory)); + null)); } @Test @@ -334,24 +332,24 @@ public void test_RaiseExceptionOnConnectWithCallback() { Throwable thrownException = assertThrows( SQLException.class, - () -> new ConnectionWrapper(props, + () -> new ConnectionWrapper( + serviceContainer, + props, "any-protocol://any-host/", mockConnectionProvider, null, mockTargetDriverDialect, - null, - mockStorageService, - mockTelemetryFactory)); + null)); assertSame(exception, thrownException); assertDoesNotThrow( - () -> new ConnectionWrapper(props, + () -> new ConnectionWrapper( + serviceContainer, + props, "any-protocol://any-host/", mockConnectionProvider, null, mockTargetDriverDialect, - null, - mockStorageService, - mockTelemetryFactory)); + null)); } } diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java index d8b7f80ff..1303c9b0a 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java @@ -74,7 +74,7 @@ import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.targetdriverdialect.PgTargetDriverDialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @Disabled @@ -498,7 +498,7 @@ public HostSpec getInitialConnectionHostSpec() { } @Override - public StorageService getStorageService() { + public ServiceContainer getServiceContainer() { return null; } From bd04054626d3d7cdc15b3a958bdebb5d5a2a6050 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 10 Mar 2025 17:33:35 -0700 Subject: [PATCH 055/149] Use defaultSuppliers --- .../util/monitoring/MonitorServiceImpl.java | 85 +++++++------------ 1 file changed, 30 insertions(+), 55 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 37a19adb4..c5a612f6c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -16,6 +16,8 @@ package software.amazon.jdbc.util.monitoring; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -40,6 +42,7 @@ public class MonitorServiceImpl implements MonitorService { protected static final Map, CacheContainer> monitorCaches = new ConcurrentHashMap<>(); protected static final AtomicBoolean isInitialized = new AtomicBoolean(false); protected static final ReentrantLock initLock = new ReentrantLock(); + protected static final Map, Supplier> defaultSuppliers; protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { final Thread thread = new Thread(r); thread.setDaemon(true); @@ -47,11 +50,14 @@ public class MonitorServiceImpl implements MonitorService { })); static { - Set resetErrorResponse = new HashSet<>(1); - resetErrorResponse.add(MonitorErrorResponse.RESTART); - MonitorSettings customEndpointSettings = new MonitorSettings( + Map, Supplier> suppliers = new HashMap<>(); + Set resetErrorResponse = + new HashSet<>(Collections.singletonList(MonitorErrorResponse.RESTART)); + MonitorSettings defaultSettings = new MonitorSettings( TimeUnit.MINUTES.toNanos(5), TimeUnit.MINUTES.toNanos(1), resetErrorResponse); - monitorCaches.put(CustomEndpointMonitorImpl.class, new CacheContainer(customEndpointSettings, null)); + + suppliers.put(CustomEndpointMonitorImpl.class, () -> new CacheContainer(defaultSettings)); + defaultSuppliers = Collections.unmodifiableMap(suppliers); } public MonitorServiceImpl() { @@ -86,10 +92,6 @@ protected void checkMonitors() { LOGGER.finest(Messages.get("MonitorServiceImpl.checkingMonitors")); for (CacheContainer container : monitorCaches.values()) { ExternallyManagedCache cache = container.getCache(); - if (cache == null) { - continue; - } - // Note: the map returned by getEntries is a copy of the ExternallyManagedCache map for (Map.Entry entry : cache.getEntries().entrySet()) { Object key = entry.getKey(); @@ -102,7 +104,7 @@ protected void checkMonitors() { MonitorSettings monitorSettings = container.getSettings(); removedItem = cache.removeIf(key, mi -> mi.getMonitor().getState() == MonitorState.ERROR); if (removedItem != null) { - handleMonitorError(monitorSettings.getErrorResponses(), cache, key, removedItem); + handleMonitorError(container, key, removedItem); continue; } @@ -114,7 +116,7 @@ protected void checkMonitors() { LOGGER.fine( Messages.get("MonitorServiceImpl.monitorStuck", new Object[]{removedItem.getMonitor(), TimeUnit.NANOSECONDS.toSeconds(inactiveTimeoutNanos)})); - handleMonitorError(monitorSettings.getErrorResponses(), cache, key, removedItem); + handleMonitorError(container, key, removedItem); continue; } @@ -127,16 +129,16 @@ protected void checkMonitors() { } private void handleMonitorError( - Set errorResponses, - ExternallyManagedCache cache, + CacheContainer cacheContainer, Object key, MonitorItem monitorItem) { Monitor monitor = monitorItem.getMonitor(); monitor.stop(); + Set errorResponses = cacheContainer.getSettings().getErrorResponses(); if (errorResponses.contains(MonitorErrorResponse.RESTART)) { LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); - cache.computeIfAbsent(key, k -> new MonitorItem(monitorItem.getMonitorSupplier())); + cacheContainer.getCache().computeIfAbsent(key, k -> new MonitorItem(monitorItem.getMonitorSupplier())); } } @@ -149,30 +151,24 @@ public void registerMonitorTypeIfAbsent( @Nullable ShouldDisposeFunc shouldDisposeFunc) { monitorCaches.computeIfAbsent( monitorClass, - mc -> { - ExternallyManagedCache cache = - new ExternallyManagedCache<>(true, expirationTimeoutNanos); - return new CacheContainer( - new MonitorSettings(expirationTimeoutNanos, heartbeatTimeoutNanos, errorResponses), cache); - }); + mc -> new CacheContainer(new MonitorSettings(expirationTimeoutNanos, heartbeatTimeoutNanos, errorResponses))); } @Override public T runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier) { CacheContainer cacheContainer = monitorCaches.get(monitorClass); if (cacheContainer == null) { - throw new IllegalStateException( - Messages.get("MonitorServiceImpl.monitorTypeNotRegistered", new Object[] {monitorClass})); - } + Supplier supplier = defaultSuppliers.get(monitorClass); + if (supplier == null) { + throw new IllegalStateException( + Messages.get("MonitorServiceImpl.monitorTypeNotRegistered", new Object[] {monitorClass})); + } - ExternallyManagedCache cache = cacheContainer.getCache(); - if (cache == null) { - MonitorSettings monitorSettings = cacheContainer.getSettings(); - cache = new ExternallyManagedCache<>(true, monitorSettings.getExpirationTimeoutNanos()); - cacheContainer.setCache(cache); + cacheContainer = monitorCaches.computeIfAbsent(monitorClass, k -> supplier.get()); } - Monitor monitor = cache.computeIfAbsent(key, k -> new MonitorItem(monitorSupplier)).getMonitor(); + Monitor monitor = + cacheContainer.getCache().computeIfAbsent(key, k -> new MonitorItem(monitorSupplier)).getMonitor(); if (monitorClass.isInstance(monitor)) { return monitorClass.cast(monitor); } @@ -190,15 +186,10 @@ public void reportMonitorError(Monitor monitor, Exception exception) { } ExternallyManagedCache cache = cacheContainer.getCache(); - if (cache == null) { - LOGGER.fine(Messages.get("MonitorServiceImpl.monitorErrorForNullCache", new Object[] {monitor, exception})); - return; - } - for (Map.Entry entry : cache.getEntries().entrySet()) { MonitorItem monitorItem = cache.removeIf(entry.getKey(), mi -> mi.getMonitor() == monitor); if (monitorItem != null) { - handleMonitorError(cacheContainer.getSettings().getErrorResponses(), cache, entry.getKey(), monitorItem); + handleMonitorError(cacheContainer, entry.getKey(), monitorItem); return; } } @@ -214,13 +205,7 @@ public void stopAndRemove(Class monitorClass, Object key) return; } - ExternallyManagedCache cache = cacheContainer.getCache(); - if (cache == null) { - LOGGER.fine(Messages.get("MonitorServiceImpl.stopAndRemoveNullCache", new Object[] {monitorClass, key})); - return; - } - - MonitorItem monitorItem = cache.remove(key); + MonitorItem monitorItem = cacheContainer.getCache().remove(key); if (monitorItem != null) { monitorItem.getMonitor().stop(); } @@ -235,11 +220,6 @@ public void stopAndRemoveMonitors(Class monitorClass) { } ExternallyManagedCache cache = cacheContainer.getCache(); - if (cache == null) { - LOGGER.fine(Messages.get("MonitorServiceImpl.stopAndRemoveMonitorsNullCache", new Object[] {monitorClass})); - return; - } - for (Map.Entry entry : cache.getEntries().entrySet()) { MonitorItem monitorItem = cache.remove(entry.getKey()); if (monitorItem != null) { @@ -257,25 +237,20 @@ public void stopAndRemoveAll() { protected static class CacheContainer { private @NonNull final MonitorSettings settings; - private @Nullable ExternallyManagedCache cache; + private @NonNull final ExternallyManagedCache cache; - public CacheContainer( - @NonNull MonitorSettings settings, @Nullable ExternallyManagedCache cache) { + public CacheContainer(@NonNull final MonitorSettings settings) { this.settings = settings; - this.cache = cache; + this.cache = new ExternallyManagedCache<>(true, settings.getExpirationTimeoutNanos()); } public @NonNull MonitorSettings getSettings() { return settings; } - public @Nullable ExternallyManagedCache getCache() { + public @NonNull ExternallyManagedCache getCache() { return cache; } - - public void setCache(@NonNull ExternallyManagedCache cache) { - this.cache = cache; - } } protected static class MonitorItem { From 381d9c636ab830b010822777a2cdf3197f8eef5d Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 10 Mar 2025 19:14:40 -0700 Subject: [PATCH 056/149] MonitorService starts the monitor --- .../CustomEndpointMonitorImpl.java | 17 +---------- .../jdbc/util/monitoring/AbstractMonitor.java | 28 +++++++++++++++++-- .../amazon/jdbc/util/monitoring/Monitor.java | 27 ++++++++++++++++++ .../util/monitoring/MonitorServiceImpl.java | 16 ++++++++--- 4 files changed, 65 insertions(+), 23 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index 5f27dc04c..862a09fea 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -19,8 +19,6 @@ import static software.amazon.jdbc.plugin.customendpoint.MemberListType.STATIC_LIST; import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiFunction; @@ -36,7 +34,6 @@ import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.util.CacheMap; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.monitoring.AbstractMonitor; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.ItemCategory; @@ -62,16 +59,7 @@ public class CustomEndpointMonitorImpl extends AbstractMonitor implements Custom protected final String endpointIdentifier; protected final Region region; protected final long refreshRateNano; - protected final StorageService storageService; - protected final ExecutorService monitorExecutor = Executors.newSingleThreadExecutor(runnableTarget -> { - final Thread monitoringThread = new Thread(runnableTarget); - monitoringThread.setDaemon(true); - if (!StringUtils.isNullOrEmpty(monitoringThread.getName())) { - monitoringThread.setName(monitoringThread.getName() + "-cem"); - } - return monitoringThread; - }); private final TelemetryCounter infoChangedCounter; @@ -107,16 +95,13 @@ public CustomEndpointMonitorImpl( this.rdsClient = rdsClientFunc.apply(customEndpointHostSpec, this.region); this.infoChangedCounter = telemetryFactory.createCounter(TELEMETRY_ENDPOINT_INFO_CHANGED); - - this.monitorExecutor.submit(this); - this.monitorExecutor.shutdown(); } /** * Analyzes a given custom endpoint for changes to custom endpoint information. */ @Override - public void start() { + public void monitor() { LOGGER.fine( Messages.get( "CustomEndpointMonitorImpl.startingMonitor", diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index 830e22377..bcbd86246 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -16,12 +16,24 @@ package software.amazon.jdbc.util.monitoring; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import java.util.logging.Logger; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.StringUtils; public abstract class AbstractMonitor implements Monitor, Runnable { - private static Logger LOGGER = Logger.getLogger(AbstractMonitor.class.getName()); - private final MonitorService monitorService; + private static final Logger LOGGER = Logger.getLogger(AbstractMonitor.class.getName()); + protected final MonitorService monitorService; + protected final ExecutorService monitorExecutor = Executors.newSingleThreadExecutor(runnableTarget -> { + final Thread monitoringThread = new Thread(runnableTarget); + monitoringThread.setDaemon(true); + if (!StringUtils.isNullOrEmpty(monitoringThread.getName())) { + monitoringThread.setName(monitoringThread.getName() + "-" + getMonitorSuffix()); + } + return monitoringThread; + }); + protected long lastUsedTimestampNanos; protected MonitorState state; @@ -30,12 +42,18 @@ protected AbstractMonitor(MonitorService monitorService) { this.lastUsedTimestampNanos = System.nanoTime(); } + @Override + public void start() { + this.monitorExecutor.submit(this); + this.monitorExecutor.shutdown(); + } + @Override public void run() { try { this.state = MonitorState.RUNNING; this.lastUsedTimestampNanos = System.nanoTime(); - start(); + monitor(); } catch (Exception e) { LOGGER.fine(Messages.get("AbstractMonitor.unexpectedError", new Object[]{this, e})); this.state = MonitorState.ERROR; @@ -57,4 +75,8 @@ public MonitorState getState() { public boolean canDispose() { return true; } + + private String getMonitorSuffix() { + return this.getClass().getSimpleName().replaceAll("[a-z]", "").toLowerCase(); + } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index 6f9414287..af3e0a38e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -17,13 +17,40 @@ package software.amazon.jdbc.util.monitoring; public interface Monitor { + /** + * Submit this monitor in a separate thread to begin its monitoring tasks. + */ void start(); + /** + * Execute the monitoring loop for this monitor. This method should be called in the run() method of the thread + * submitted during the call to {@link #start()}. + */ + void monitor(); + + /** + * Stop the monitoring tasks for this monitor and close all resources. + */ void stop(); + /** + * Get the timestamp for the last action performed by this monitor, in nanoseconds. + * + * @return the timestamp for the last action performed by this monitor, in nanoseconds. + */ long getLastActivityTimestampNanos(); + /** + * Get the current state of this monitor. + * + * @return the current state of this monitor. + */ MonitorState getState(); + /** + * Defines whether this monitor can be disposed. + * + * @return true if this monitor can be disposed, otherwise returns false. + */ boolean canDispose(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index c5a612f6c..3cc7444dd 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -131,14 +131,18 @@ protected void checkMonitors() { private void handleMonitorError( CacheContainer cacheContainer, Object key, - MonitorItem monitorItem) { - Monitor monitor = monitorItem.getMonitor(); + MonitorItem oldMonitorItem) { + Monitor monitor = oldMonitorItem.getMonitor(); monitor.stop(); Set errorResponses = cacheContainer.getSettings().getErrorResponses(); if (errorResponses.contains(MonitorErrorResponse.RESTART)) { LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); - cacheContainer.getCache().computeIfAbsent(key, k -> new MonitorItem(monitorItem.getMonitorSupplier())); + cacheContainer.getCache().computeIfAbsent(key, k -> { + MonitorItem newMonitorItem = new MonitorItem(oldMonitorItem.getMonitorSupplier()); + newMonitorItem.getMonitor().start(); + return newMonitorItem; + }); } } @@ -168,7 +172,11 @@ public T runIfAbsent(Class monitorClass, Object key, Supp } Monitor monitor = - cacheContainer.getCache().computeIfAbsent(key, k -> new MonitorItem(monitorSupplier)).getMonitor(); + cacheContainer.getCache().computeIfAbsent(key, k -> { + MonitorItem monitorItem = new MonitorItem(monitorSupplier); + monitorItem.getMonitor().start(); + return monitorItem; + }).getMonitor(); if (monitorClass.isInstance(monitor)) { return monitorClass.cast(monitor); } From a56acceb1f27dc14dc9a8d51afcef13b9392d0aa Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 11 Mar 2025 15:34:02 -0700 Subject: [PATCH 057/149] Implement data access messages --- .../java/software/amazon/jdbc/Driver.java | 7 +- .../amazon/jdbc/ds/AwsWrapperDataSource.java | 7 +- .../jdbc/util/events/DataAccessEvent.java | 74 +++++++++++++++++ .../util/events/PeriodicEventPublisher.java | 82 +++++++++++++++++++ .../jdbc/util/monitoring/MonitorService.java | 4 +- .../util/monitoring/MonitorServiceImpl.java | 55 +++++++++++-- .../util/storage/ExternallyManagedCache.java | 7 ++ .../jdbc/util/storage/StorageServiceImpl.java | 17 +++- 8 files changed, 237 insertions(+), 16 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index dc9783411..606353b8c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -65,6 +65,8 @@ import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.ServiceContainerImpl; import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.events.EventPublisher; +import software.amazon.jdbc.util.events.PeriodicEventPublisher; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.monitoring.MonitorServiceImpl; import software.amazon.jdbc.util.storage.StorageService; @@ -82,8 +84,9 @@ public class Driver implements java.sql.Driver { private static final Logger LOGGER = Logger.getLogger("software.amazon.jdbc.Driver"); private static @Nullable Driver registeredDriver; - private static final StorageService storageService = new StorageServiceImpl(); - private static final MonitorService monitorService = new MonitorServiceImpl(); + private static final EventPublisher publisher = new PeriodicEventPublisher(); + private static final StorageService storageService = new StorageServiceImpl(publisher); + private static final MonitorService monitorService = new MonitorServiceImpl(publisher); private static final AtomicReference resetSessionStateOnCloseCallable = new AtomicReference<>(null); diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index 1c0e2bf21..b14681281 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -54,6 +54,8 @@ import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; +import software.amazon.jdbc.util.events.EventPublisher; +import software.amazon.jdbc.util.events.PeriodicEventPublisher; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.monitoring.MonitorServiceImpl; import software.amazon.jdbc.util.storage.StorageService; @@ -73,8 +75,9 @@ public class AwsWrapperDataSource implements DataSource, Referenceable, Serializ private static final String SERVER_NAME = "serverName"; private static final String SERVER_PORT = "serverPort"; - private static final StorageService storageService = new StorageServiceImpl(); - private static final MonitorService monitorService = new MonitorServiceImpl(); + private static final EventPublisher publisher = new PeriodicEventPublisher(); + private static final StorageService storageService = new StorageServiceImpl(publisher); + private static final MonitorService monitorService = new MonitorServiceImpl(publisher); static { try { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java new file mode 100644 index 000000000..5f6e89ff5 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java @@ -0,0 +1,74 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.events; + +import java.util.Objects; +import org.checkerframework.checker.nullness.qual.NonNull; + +public class DataAccessEvent implements Event { + protected @NonNull String dataCategory; + protected @NonNull Class dataClass; + protected @NonNull Object key; + + public DataAccessEvent(@NonNull String dataCategory, @NonNull Class dataClass, @NonNull Object key) { + this.dataCategory = dataCategory; + this.dataClass = dataClass; + this.key = key; + } + + public @NonNull String getDataCategory() { + return dataCategory; + } + + public @NonNull Class getDataClass() { + return dataClass; + } + + public @NonNull Object getKey() { + return key; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null) { + return false; + } + + if (getClass() != obj.getClass()) { + return false; + } + + DataAccessEvent event = (DataAccessEvent) obj; + return Objects.equals(this.dataCategory, event.dataCategory) + && Objects.equals(this.dataClass, event.dataClass) + && Objects.equals(this.key, event.key); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + this.dataCategory.hashCode(); + result = prime * result + this.dataClass.hashCode(); + result = prime * result + this.key.hashCode(); + return result; + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java new file mode 100644 index 000000000..dff7f674d --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java @@ -0,0 +1,82 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.events; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import software.amazon.jdbc.util.StringUtils; + +public class PeriodicEventPublisher implements EventPublisher { + protected static final long DEFAULT_MESSAGE_INTERVAL_NANOS = TimeUnit.SECONDS.toNanos(30); + protected final Map, Set> subscribers = new ConcurrentHashMap<>(); + // ConcurrentHashMap.newKeySet() is the recommended way to get a concurrent set. A set is used to prevent duplicate + // event messages from being sent in the same message batch. + protected final Set eventMessages = ConcurrentHashMap.newKeySet(); + protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { + final Thread thread = new Thread(r); + thread.setDaemon(true); + if (!StringUtils.isNullOrEmpty(thread.getName())) { + thread.setName(thread.getName() + "-epi"); + } + return thread; + })); + + public PeriodicEventPublisher() { + this(DEFAULT_MESSAGE_INTERVAL_NANOS); + } + + + public PeriodicEventPublisher(long messageIntervalNanos) { + cleanupExecutor.scheduleAtFixedRate( + this::sendMessages, messageIntervalNanos, messageIntervalNanos, TimeUnit.NANOSECONDS); + } + + private void sendMessages() { + for (Event event : eventMessages) { + for (EventSubscriber subscriber : subscribers.get(event.getClass())) { + subscriber.processEvent(event); + } + } + } + + @Override + public void subscribe(EventSubscriber subscriber, Set> eventClasses) { + for (Class eventClass : eventClasses) { + // ConcurrentHashMap.newKeySet() is the recommended way to get a concurrent set. + subscribers.compute(eventClass, (k, v) -> v == null ? ConcurrentHashMap.newKeySet() : v).add(subscriber); + } + } + + @Override + public void unsubscribe(EventSubscriber subscriber, Set> eventClasses) { + for (Class eventClass : eventClasses) { + subscribers.computeIfPresent(eventClass, (k, v) -> { + v.remove(subscriber); + return v.isEmpty() ? null : v; + }); + } + } + + @Override + public void publish(Event event) { + eventMessages.add(event); + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 5bf75bcca..3e6ab9a12 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -38,13 +38,15 @@ public interface MonitorService { * @param errorResponses a Set defining actions to take if the monitor is in an error state. * @param shouldDisposeFunc a function defining whether an item should be stopped if expired. If `null` is * passed, the monitor will always be stopped if the monitor is expired. + * @param producedDataClass the class of data produced by the monitor. */ void registerMonitorTypeIfAbsent( Class monitorClass, long expirationTimeoutNanos, long heartbeatTimeoutNanos, Set errorResponses, - @Nullable ShouldDisposeFunc shouldDisposeFunc); + @Nullable ShouldDisposeFunc shouldDisposeFunc, + @Nullable Class producedDataClass); /** * Creates and starts the given monitor if it does not already exist and stores it under the given monitor type and diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 3cc7444dd..8a98133a7 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -31,12 +31,18 @@ import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import software.amazon.jdbc.AllowedAndBlockedHosts; import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ShouldDisposeFunc; +import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.events.DataAccessEvent; +import software.amazon.jdbc.util.events.Event; +import software.amazon.jdbc.util.events.EventPublisher; +import software.amazon.jdbc.util.events.EventSubscriber; import software.amazon.jdbc.util.storage.ExternallyManagedCache; -public class MonitorServiceImpl implements MonitorService { +public class MonitorServiceImpl implements MonitorService, EventSubscriber { private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(1); protected static final Map, CacheContainer> monitorCaches = new ConcurrentHashMap<>(); @@ -46,9 +52,14 @@ public class MonitorServiceImpl implements MonitorService { protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { final Thread thread = new Thread(r); thread.setDaemon(true); + if (!StringUtils.isNullOrEmpty(thread.getName())) { + thread.setName(thread.getName() + "-msi"); + } return thread; })); + protected final EventPublisher publisher; + static { Map, Supplier> suppliers = new HashMap<>(); Set resetErrorResponse = @@ -56,15 +67,18 @@ public class MonitorServiceImpl implements MonitorService { MonitorSettings defaultSettings = new MonitorSettings( TimeUnit.MINUTES.toNanos(5), TimeUnit.MINUTES.toNanos(1), resetErrorResponse); - suppliers.put(CustomEndpointMonitorImpl.class, () -> new CacheContainer(defaultSettings)); + suppliers.put( + CustomEndpointMonitorImpl.class, () -> new CacheContainer(defaultSettings, AllowedAndBlockedHosts.class)); defaultSuppliers = Collections.unmodifiableMap(suppliers); } - public MonitorServiceImpl() { - initCleanupThread(DEFAULT_CLEANUP_INTERVAL_NANOS); + public MonitorServiceImpl(EventPublisher publisher) { + this(DEFAULT_CLEANUP_INTERVAL_NANOS, publisher); } - public MonitorServiceImpl(long cleanupIntervalNanos) { + public MonitorServiceImpl(long cleanupIntervalNanos, EventPublisher publisher) { + this.publisher = publisher; + this.publisher.subscribe(this, new HashSet<>(Collections.singletonList(DataAccessEvent.class))); initCleanupThread(cleanupIntervalNanos); } @@ -152,10 +166,12 @@ public void registerMonitorTypeIfAbsent( long expirationTimeoutNanos, long heartbeatTimeoutNanos, Set errorResponses, - @Nullable ShouldDisposeFunc shouldDisposeFunc) { + @Nullable ShouldDisposeFunc shouldDisposeFunc, + @Nullable Class producedDataClass) { monitorCaches.computeIfAbsent( monitorClass, - mc -> new CacheContainer(new MonitorSettings(expirationTimeoutNanos, heartbeatTimeoutNanos, errorResponses))); + mc -> new CacheContainer( + new MonitorSettings(expirationTimeoutNanos, heartbeatTimeoutNanos, errorResponses), producedDataClass)); } @Override @@ -243,13 +259,32 @@ public void stopAndRemoveAll() { } } + @Override + public void processEvent(Event event) { + if (!(event instanceof DataAccessEvent)) { + return; + } + + DataAccessEvent accessEvent = (DataAccessEvent) event; + for (CacheContainer container : monitorCaches.values()) { + if (container.getProducedDataClass() == null + || !accessEvent.getDataClass().equals(container.getProducedDataClass())) { + continue; + } + + container.getCache().extendExpiration(accessEvent.getKey()); + } + } + protected static class CacheContainer { private @NonNull final MonitorSettings settings; private @NonNull final ExternallyManagedCache cache; + private @Nullable final Class producedDataClass; - public CacheContainer(@NonNull final MonitorSettings settings) { + public CacheContainer(@NonNull final MonitorSettings settings, @Nullable Class producedDataClass) { this.settings = settings; this.cache = new ExternallyManagedCache<>(true, settings.getExpirationTimeoutNanos()); + this.producedDataClass = producedDataClass; } public @NonNull MonitorSettings getSettings() { @@ -259,6 +294,10 @@ public CacheContainer(@NonNull final MonitorSettings settings) { public @NonNull ExternallyManagedCache getCache() { return cache; } + + public @Nullable Class getProducedDataClass() { + return producedDataClass; + } } protected static class MonitorItem { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java index 92864cc6c..4b8d74c27 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java @@ -69,6 +69,13 @@ public ExternallyManagedCache(boolean isRenewableExpiration, long timeToLiveNano return cacheItem.item; } + public void extendExpiration(K key) { + final CacheItem cacheItem = cache.get(key); + if (cacheItem != null) { + cacheItem.extendExpiration(); + } + } + public @Nullable V remove(K key) { CacheItem cacheItem = cache.remove(key); if (cacheItem == null) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index ccbdb4bb9..78334ddc3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -32,6 +32,9 @@ import software.amazon.jdbc.util.ItemDisposalFunc; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ShouldDisposeFunc; +import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.events.DataAccessEvent; +import software.amazon.jdbc.util.events.EventPublisher; public class StorageServiceImpl implements StorageService { private static final Logger LOGGER = Logger.getLogger(StorageServiceImpl.class.getName()); @@ -45,9 +48,14 @@ public class StorageServiceImpl implements StorageService { protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { final Thread thread = new Thread(r); thread.setDaemon(true); + if (!StringUtils.isNullOrEmpty(thread.getName())) { + thread.setName(thread.getName() + "-ssi"); + } return thread; })); + protected final EventPublisher publisher; + static { Map>> suppliers = new HashMap<>(); suppliers.put(ItemCategory.TOPOLOGY, ExpirationCache::new); @@ -57,11 +65,12 @@ public class StorageServiceImpl implements StorageService { defaultCacheSuppliers = Collections.unmodifiableMap(suppliers); } - public StorageServiceImpl() { - initCleanupThread(DEFAULT_CLEANUP_INTERVAL_NANOS); + public StorageServiceImpl(EventPublisher publisher) { + this(DEFAULT_CLEANUP_INTERVAL_NANOS, publisher); } - public StorageServiceImpl(long cleanupIntervalNanos) { + public StorageServiceImpl(long cleanupIntervalNanos, EventPublisher publisher) { + this.publisher = publisher; initCleanupThread(cleanupIntervalNanos); } @@ -150,6 +159,8 @@ public void set(String itemCategory, Object key, V value) { } if (itemClass.isInstance(value)) { + DataAccessEvent event = new DataAccessEvent(itemCategory, itemClass, key); + this.publisher.publish(event); return itemClass.cast(value); } From 0d7e1178b161dd31e38c1ebce3e5f5afb1e2952a Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 11 Mar 2025 17:21:35 -0700 Subject: [PATCH 058/149] Remove ItemCategory argument in StorageService --- .../software/amazon/jdbc/PluginService.java | 2 +- .../amazon/jdbc/PluginServiceImpl.java | 6 +- .../hostlistprovider/RdsHostListProvider.java | 19 ++--- .../ClusterTopologyMonitorImpl.java | 5 +- .../MonitoringRdsHostListProvider.java | 5 +- .../CustomEndpointMonitorImpl.java | 4 +- .../jdbc/util/events/DataAccessEvent.java | 12 +-- .../jdbc/util/storage/ItemCategory.java | 27 ------ .../jdbc/util/storage/StorageService.java | 67 +++++++-------- .../jdbc/util/storage/StorageServiceImpl.java | 84 ++++++++----------- ..._advanced_jdbc_wrapper_messages.properties | 6 +- .../amazon/jdbc/PluginServiceImplTests.java | 5 +- .../RdsHostListProviderTest.java | 30 +++---- .../RdsMultiAzDbClusterListProviderTest.java | 42 ++++------ .../CustomEndpointMonitorImplTest.java | 4 +- 15 files changed, 128 insertions(+), 190 deletions(-) delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemCategory.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java index 64007139a..6b1238d36 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java @@ -87,7 +87,7 @@ EnumSet setCurrentConnection( * Set the collection of hosts that should be allowed and/or blocked for connections. * * @param allowedAndBlockedHosts An object defining the allowed and blocked sets of hosts. - * @deprecated use StorageService#set(ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, key, allowedAndBlockedHosts) instead. + * @deprecated use StorageService#set(key, allowedAndBlockedHosts) instead. */ @Deprecated void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHosts); diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index 04d1920be..bca36d728 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -54,7 +54,6 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; -import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class PluginServiceImpl implements PluginService, CanReleaseResources, @@ -217,8 +216,7 @@ public ServiceContainer getServiceContainer() { @Override @Deprecated public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHosts) { - this.serviceContainer.getStorageService().set( - ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, this.initialConnectionHostSpec.getHost(), allowedAndBlockedHosts); + this.serviceContainer.getStorageService().set(this.initialConnectionHostSpec.getHost(), allowedAndBlockedHosts); } @Override @@ -413,7 +411,7 @@ public List getAllHosts() { @Override public List getHosts() { AllowedAndBlockedHosts hostPermissions = this.serviceContainer.getStorageService().get( - ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, this.initialConnectionHostSpec.getHost(), AllowedAndBlockedHosts.class); + AllowedAndBlockedHosts.class, this.initialConnectionHostSpec.getHost()); if (hostPermissions == null) { return this.allHosts; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index ba2c745c2..e1a5d515d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -58,7 +58,6 @@ import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SynchronousExecutor; import software.amazon.jdbc.util.Utils; -import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.Topology; @@ -232,7 +231,7 @@ protected void init() throws SQLException { * Returns an empty list if isn't available or is invalid (doesn't contain a writer). * @throws SQLException if errors occurred while retrieving the topology. */ - public FetchTopologyResult getTopology(final Connection conn, final boolean forceUpdate) throws SQLException { + protected FetchTopologyResult getTopology(final Connection conn, final boolean forceUpdate) throws SQLException { init(); final String suggestedPrimaryClusterId = suggestedPrimaryClusterIdCache.get(this.clusterId); @@ -268,7 +267,7 @@ public FetchTopologyResult getTopology(final Connection conn, final boolean forc final List hosts = queryForTopology(conn); if (!Utils.isNullOrEmpty(hosts)) { - storageService.set(ItemCategory.TOPOLOGY, this.clusterId, new Topology(hosts)); + storageService.set(this.clusterId, new Topology(hosts)); if (needToSuggest) { this.suggestPrimaryCluster(hosts); } @@ -289,7 +288,7 @@ protected void clusterIdChanged(final String oldClusterId) { } protected ClusterSuggestedResult getSuggestedClusterId(final String url) { - Map entries = storageService.getEntries(ItemCategory.TOPOLOGY); + Map entries = storageService.getEntries(Topology.class); if (entries == null) { return null; } @@ -326,7 +325,7 @@ protected void suggestPrimaryCluster(final @NonNull List primaryCluste primaryClusterHostUrls.add(hostSpec.getUrl()); } - Map entries = storageService.getEntries(ItemCategory.TOPOLOGY); + Map entries = storageService.getEntries(Topology.class); if (entries == null) { return; } @@ -509,7 +508,7 @@ protected String getHostEndpoint(final String nodeName) { * cached topology is outdated, it returns null. */ public @Nullable List getStoredTopology() { - Topology topology = storageService.get(ItemCategory.TOPOLOGY, this.clusterId, Topology.class); + Topology topology = storageService.get(Topology.class, this.clusterId); return topology == null ? null : topology.getHosts(); } @@ -525,7 +524,7 @@ public static void clearAll() { * Clear topology cache for the current cluster. */ public void clear() { - storageService.remove(ItemCategory.TOPOLOGY, this.clusterId); + storageService.remove(Topology.class, this.clusterId); } @Override @@ -571,7 +570,7 @@ public RdsUrlType getRdsUrlType() throws SQLException { } private void validateHostPatternSetting(final String hostPattern) { - if (!this.rdsHelper.isDnsPatternValid(hostPattern)) { + if (!rdsHelper.isDnsPatternValid(hostPattern)) { // "Invalid value for the 'clusterInstanceHostPattern' configuration setting - the host // pattern must contain a '?' // character as a placeholder for the DB instance identifiers of the instances in the cluster" @@ -580,7 +579,7 @@ private void validateHostPatternSetting(final String hostPattern) { throw new RuntimeException(message); } - final RdsUrlType rdsUrlType = this.rdsHelper.identifyRdsType(hostPattern); + final RdsUrlType rdsUrlType = rdsHelper.identifyRdsType(hostPattern); if (rdsUrlType == RdsUrlType.RDS_PROXY) { // "An RDS Proxy url can't be used as the 'clusterInstanceHostPattern' configuration setting." final String message = @@ -599,7 +598,7 @@ private void validateHostPatternSetting(final String hostPattern) { } } - static class FetchTopologyResult { + protected static class FetchTopologyResult { public List hosts; public boolean isCachedData; diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 584f62d74..6c4bef0ef 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -54,7 +54,6 @@ import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SynchronousExecutor; import software.amazon.jdbc.util.Utils; -import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.Topology; @@ -261,7 +260,7 @@ protected List waitTillTopologyGetsUpdated(final long timeoutMs) throw } private List getStoredHosts() { - Topology topology = storageService.get(ItemCategory.TOPOLOGY, this.clusterId, Topology.class); + Topology topology = storageService.get(Topology.class, this.clusterId); return topology == null ? null : topology.getHosts(); } @@ -597,7 +596,7 @@ protected void delay(boolean useHighRefreshRate) throws InterruptedException { protected void updateTopologyCache(final @NonNull List hosts) { synchronized (this.requestToUpdateTopology) { - storageService.set(ItemCategory.TOPOLOGY, this.clusterId, new Topology(hosts)); + storageService.set(this.clusterId, new Topology(hosts)); synchronized (this.topologyUpdated) { this.requestToUpdateTopology.set(false); diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index 927f05ed6..271f57ce1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -32,7 +32,6 @@ import software.amazon.jdbc.hostlistprovider.RdsHostListProvider; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; -import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.Topology; public class MonitoringRdsHostListProvider extends RdsHostListProvider @@ -140,10 +139,10 @@ protected void clusterIdChanged(final String oldClusterId) { monitors.remove(oldClusterId); } - final Topology existingTopology = storageService.get(ItemCategory.TOPOLOGY, oldClusterId, Topology.class); + final Topology existingTopology = storageService.get(Topology.class, oldClusterId); final List existingHosts = existingTopology == null ? null : existingTopology.getHosts(); if (existingHosts != null) { - storageService.set(ItemCategory.TOPOLOGY, this.clusterId, new Topology(existingHosts)); + storageService.set(this.clusterId, new Topology(existingHosts)); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index 862a09fea..5de5f4cf5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -36,7 +36,6 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.monitoring.AbstractMonitor; import software.amazon.jdbc.util.monitoring.MonitorService; -import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -159,8 +158,7 @@ public void monitor() { allowedAndBlockedHosts = new AllowedAndBlockedHosts(null, endpointInfo.getExcludedMembers()); } - this.storageService.set( - ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, this.customEndpointHostSpec.getHost(), allowedAndBlockedHosts); + this.storageService.set(this.customEndpointHostSpec.getHost(), allowedAndBlockedHosts); customEndpointInfoCache.put( this.customEndpointHostSpec.getHost(), endpointInfo, CUSTOM_ENDPOINT_INFO_EXPIRATION_NANO); this.infoChangedCounter.inc(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java index 5f6e89ff5..b3ffe1866 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java @@ -20,20 +20,14 @@ import org.checkerframework.checker.nullness.qual.NonNull; public class DataAccessEvent implements Event { - protected @NonNull String dataCategory; protected @NonNull Class dataClass; protected @NonNull Object key; - public DataAccessEvent(@NonNull String dataCategory, @NonNull Class dataClass, @NonNull Object key) { - this.dataCategory = dataCategory; + public DataAccessEvent(@NonNull Class dataClass, @NonNull Object key) { this.dataClass = dataClass; this.key = key; } - public @NonNull String getDataCategory() { - return dataCategory; - } - public @NonNull Class getDataClass() { return dataClass; } @@ -57,8 +51,7 @@ public boolean equals(Object obj) { } DataAccessEvent event = (DataAccessEvent) obj; - return Objects.equals(this.dataCategory, event.dataCategory) - && Objects.equals(this.dataClass, event.dataClass) + return Objects.equals(this.dataClass, event.dataClass) && Objects.equals(this.key, event.key); } @@ -66,7 +59,6 @@ public boolean equals(Object obj) { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + this.dataCategory.hashCode(); result = prime * result + this.dataClass.hashCode(); result = prime * result + this.key.hashCode(); return result; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemCategory.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemCategory.java deleted file mode 100644 index 117b3680c..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemCategory.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.util.storage; - -public class ItemCategory { - public static final String TOPOLOGY = "topology"; - public static final String ALLOWED_AND_BLOCKED_HOSTS = "allowedAndBlockedHosts"; - - private ItemCategory() { - throw new UnsupportedOperationException( - "ItemCategories should not be instantiated because its purpose is to provide a set of static constants."); - } -} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java index be2b9ac54..fec14e23e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java @@ -23,14 +23,12 @@ public interface StorageService { /** - * Register a new item category with the storage service. This method needs to be called before adding new categories - * of items to the service, so that the service knows when and how to dispose of the item. Expected item categories - * ("topology" and "customEndpoint") will be added automatically during driver initialization, but this method can be - * called by users if they want to add a new category. + * Register a new item class with the storage service. This method needs to be called before adding new classes of + * items to the service, so that the service knows when and how to dispose of the item. Expected item classes + * (`Topology.class` and `AllowedAndBlockedHosts.class`) will be added automatically during driver initialization, but + * this method can be called to add new classes of items. * - * @param itemCategory a String representing the item category, eg "customEndpoint". - * @param itemClass the class of item that will be stored under this category, eg - * `CustomEndpointInfo.class`. + * @param itemClass the class of the item that will be stored, eg `CustomEndpointInfo.class`. * @param isRenewableExpiration controls whether the item's expiration should be renewed if the item is fetched, * regardless of whether it is already expired or not. * @param timeToLiveNanos how long an item should be stored before being considered expired, in nanoseconds. @@ -38,10 +36,9 @@ public interface StorageService { * the item will always be disposed if expired. * @param itemDisposalFunc a function defining how to dispose of an item when it is removed. If null is * passed, the item will be removed without performing any additional operations. - * @param the type of item that will be stored under the item category. + * @param the type of item that will be stored under the item class. */ - void registerItemCategoryIfAbsent( - String itemCategory, + void registerItemClassIfAbsent( Class itemClass, boolean isRenewableExpiration, long timeToLiveNanos, @@ -49,60 +46,58 @@ void registerItemCategoryIfAbsent( @Nullable ItemDisposalFunc itemDisposalFunc); /** - * Stores an item under the given category. + * Stores an item in the storage service under the given item class. * - * @param itemCategory a String representing the item category, eg "customEndpoint". - * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". + * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com". * @param item the item to store. - * @param the type of the item being stored. + * @param the type of the item being retrieved. */ - void set(String itemCategory, Object key, V item); + void set(Object key, V item); /** - * Gets an item stored under the given category. + * Gets an item stored in the storage service. * - * @param itemCategory a String representing the item category, eg "customEndpoint". - * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". * @param itemClass the expected class of the item being retrieved, eg `CustomEndpointInfo.class`. + * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com". * @param the type of the item being retrieved. - * @return the item stored at the given key under the given category. + * @return the item stored at the given key for the given item class. */ - @Nullable V get(String itemCategory, Object key, Class itemClass); + @Nullable V get(Class itemClass, Object key); /** - * Indicates whether an item exists under the given item category and key. + * Indicates whether an item exists under the given item class and key. * - * @param itemCategory a String representing the item category, eg "customEndpoint". - * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". - * @return true if the item exists under the given item category and key, otherwise returns false. + * @param itemClass the class of the item. + * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com". + * @return true if the item exists under the given item class and key, otherwise returns false. */ - boolean exists(String itemCategory, Object key); + boolean exists(Class itemClass, Object key); /** - * Removes an item stored under the given category. + * Removes an item stored under the given item class. * - * @param itemCategory a String representing the item category, eg "customEndpoint". - * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". + * @param itemClass the class of the item. + * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com". */ - void remove(String itemCategory, Object key); + void remove(Class itemClass, Object key); /** - * Clears all items from the given category. For example, storageService.clear("customEndpoint") will remove all - * custom endpoint information from the storage service. + * Clears all items of the given item class. For example, storageService.clear(AllowedAndBlockedHosts.class) will + * remove all AllowedAndBlockedHost items from the storage service. * - * @param itemCategory a String representing the item category, eg "customEndpoint". + * @param itemClass the class of the items to clear. */ - void clear(String itemCategory); + void clear(Class itemClass); /** - * Clears all information from all categories of the storage service. + * Clears all items from the storage service. */ void clearAll(); // TODO: this is only called by the suggestedClusterId logic in RdsHostListProvider, which will be removed. This // method should potentially be removed at that point as well. - @Nullable Map getEntries(String itemCategory); + @Nullable Map getEntries(Class itemClass); // TODO: this method is only called by tests. We should evaluate whether we want to keep it or not. - int size(String itemCategory); + int size(Class itemClass); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 78334ddc3..e42455f6b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -39,10 +39,8 @@ public class StorageServiceImpl implements StorageService { private static final Logger LOGGER = Logger.getLogger(StorageServiceImpl.class.getName()); protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(5); - protected static final Map> caches = new ConcurrentHashMap<>(); - protected static final Map>> defaultCacheSuppliers; - // Map from item category to the expected value class for that category. - protected static final Map> valueClasses = new ConcurrentHashMap<>(); + protected static final Map, ExpirationCache> caches = new ConcurrentHashMap<>(); + protected static final Map, Supplier>> defaultCacheSuppliers; protected static final AtomicBoolean isInitialized = new AtomicBoolean(false); protected static final ReentrantLock initLock = new ReentrantLock(); protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { @@ -57,11 +55,9 @@ public class StorageServiceImpl implements StorageService { protected final EventPublisher publisher; static { - Map>> suppliers = new HashMap<>(); - suppliers.put(ItemCategory.TOPOLOGY, ExpirationCache::new); - valueClasses.put(ItemCategory.TOPOLOGY, Topology.class); - suppliers.put(ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, ExpirationCache::new); - valueClasses.put(ItemCategory.ALLOWED_AND_BLOCKED_HOSTS, AllowedAndBlockedHosts.class); + Map, Supplier>> suppliers = new HashMap<>(); + suppliers.put(Topology.class, ExpirationCache::new); + suppliers.put(AllowedAndBlockedHosts.class, ExpirationCache::new); defaultCacheSuppliers = Collections.unmodifiableMap(suppliers); } @@ -102,53 +98,47 @@ protected void removeExpiredItems() { } @Override - public void registerItemCategoryIfAbsent( - String itemCategory, + public void registerItemClassIfAbsent( Class itemClass, boolean isRenewableExpiration, long timeToLiveNanos, @Nullable ShouldDisposeFunc shouldDisposeFunc, @Nullable ItemDisposalFunc itemDisposalFunc) { caches.computeIfAbsent( - itemCategory, - category -> { - valueClasses.put(itemCategory, itemClass); - return new ExpirationCache<>( - isRenewableExpiration, - timeToLiveNanos, - shouldDisposeFunc, - itemDisposalFunc); - }); + itemClass, + category -> new ExpirationCache<>( + isRenewableExpiration, + timeToLiveNanos, + shouldDisposeFunc, + itemDisposalFunc)); } @Override - public void set(String itemCategory, Object key, V value) { - ExpirationCache cache = caches.get(itemCategory); + public void set(Object key, V value) { + ExpirationCache cache = caches.get(value.getClass()); if (cache == null) { - Supplier> supplier = defaultCacheSuppliers.get(itemCategory); + Supplier> supplier = defaultCacheSuppliers.get(value.getClass()); if (supplier == null) { throw new IllegalStateException( - Messages.get("StorageServiceImpl.itemCategoryNotRegistered", new Object[] {itemCategory})); + Messages.get("StorageServiceImpl.itemClassNotRegistered", new Object[] {value.getClass()})); } else { - cache = caches.computeIfAbsent(itemCategory, c -> supplier.get()); + cache = caches.computeIfAbsent(value.getClass(), c -> supplier.get()); } } - Class expectedValueClass = valueClasses.get(itemCategory); - if (expectedValueClass == null || !expectedValueClass.isInstance(value)) { + try { + // TODO: is there a way to get rid of this unchecked cast warning? Is the try-catch necessary? + ExpirationCache typedCache = (ExpirationCache) cache; + typedCache.put(key, value); + } catch (ClassCastException e) { throw new IllegalArgumentException( - Messages.get( - "StorageServiceImpl.incorrectValueType", - new Object[] {itemCategory, expectedValueClass, value.getClass(), value})); + Messages.get("StorageServiceImpl.unexpectedValueMismatch", new Object[] {value, value.getClass(), cache})); } - - ExpirationCache typedCache = (ExpirationCache) cache; - typedCache.put(key, value); } @Override - public @Nullable V get(String itemCategory, Object key, Class itemClass) { - final ExpirationCache cache = caches.get(itemCategory); + public @Nullable V get(Class itemClass, Object key) { + final ExpirationCache cache = caches.get(itemClass); if (cache == null) { return null; } @@ -159,7 +149,7 @@ public void set(String itemCategory, Object key, V value) { } if (itemClass.isInstance(value)) { - DataAccessEvent event = new DataAccessEvent(itemCategory, itemClass, key); + DataAccessEvent event = new DataAccessEvent(itemClass, key); this.publisher.publish(event); return itemClass.cast(value); } @@ -167,13 +157,13 @@ public void set(String itemCategory, Object key, V value) { LOGGER.fine( Messages.get( "StorageServiceImpl.itemClassMismatch", - new Object[]{key, itemCategory, itemClass, value.getClass()})); + new Object[]{key, itemClass, value, value.getClass()})); return null; } @Override - public boolean exists(String itemCategory, Object key) { - final ExpirationCache cache = caches.get(itemCategory); + public boolean exists(Class itemClass, Object key) { + final ExpirationCache cache = caches.get(itemClass); if (cache == null) { return false; } @@ -182,8 +172,8 @@ public boolean exists(String itemCategory, Object key) { } @Override - public void remove(String itemCategory, Object key) { - final ExpirationCache cache = caches.get(itemCategory); + public void remove(Class itemClass, Object key) { + final ExpirationCache cache = caches.get(itemClass); if (cache == null) { return; } @@ -192,8 +182,8 @@ public void remove(String itemCategory, Object key) { } @Override - public void clear(String itemCategory) { - final ExpirationCache cache = caches.get(itemCategory); + public void clear(Class itemClass) { + final ExpirationCache cache = caches.get(itemClass); if (cache != null) { cache.clear(); } @@ -207,8 +197,8 @@ public void clearAll() { } @Override - public @Nullable Map getEntries(String itemCategory) { - final ExpirationCache cache = caches.get(itemCategory); + public @Nullable Map getEntries(Class itemClass) { + final ExpirationCache cache = caches.get(itemClass); if (cache == null) { return null; } @@ -218,8 +208,8 @@ public void clearAll() { } @Override - public int size(String itemCategory) { - final ExpirationCache cache = caches.get(itemCategory); + public int size(Class itemClass) { + final ExpirationCache cache = caches.get(itemClass); if (cache == null) { return 0; } diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index af2c4a4f0..0b48c96b4 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -365,9 +365,9 @@ SAMLCredentialsProviderFactory.getSamlAssertionFailed=Failed to get SAML Asserti SamlAuthPlugin.javaStsSdkNotInClasspath=Required dependency 'AWS Java SDK for AWS Secret Token Service' is not on the classpath. SamlAuthPlugin.unhandledException=Unhandled exception: ''{0}'' -StorageServiceImpl.incorrectValueType=Attempted to store an incorrect item type under item category ''{0}''. Items under this category should have type ''{1}'' but the passed in item had a type of ''{2}''. Item value: ''{3}''. -StorageServiceImpl.itemCategoryNotRegistered=The given item category ''{0}'' is not registered. Please register the item category before storing items under the category. -StorageServiceImpl.itemClassMismatch=The item stored at ''{0}'' under the category ''{1}'' did not have the expected type. The expected type was ''{2}'', but the stored item had a type of ''{3}''. Returning null. +StorageServiceImpl.unexpectedValueMismatch=Attempted to store value ''{0}'' under item class ''{1}'' but there was an unexpected mismatch between the passed in value type and the expected value type. Item bin for item class ''{1}'': ''{2}''. +StorageServiceImpl.itemClassNotRegistered=The given item category ''{0}'' is not registered. Please register the item category before storing items under the category. +StorageServiceImpl.itemClassMismatch=The item stored at ''{0}'' did not have the expected type. The expected type was ''{1}'', but the stored item ''{2}'' had a type of ''{3}''. Returning null. StorageServiceImpl.removeExpiredItems=Removing expired items from the storage service... # Wrapper Utils diff --git a/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java b/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java index 5299c3afa..eb85248cb 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java @@ -67,6 +67,7 @@ import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; @@ -75,10 +76,11 @@ public class PluginServiceImplTests { private static final Properties PROPERTIES = new Properties(); private static final String URL = "url"; private static final String DRIVER_PROTOCOL = "driverProtocol"; - private static final StorageService storageService = new StorageServiceImpl(); + private StorageService storageService; private AutoCloseable closeable; @Mock ServiceContainer serviceContainer; + @Mock EventPublisher mockEventPublisher; @Mock ConnectionPluginManager pluginManager; @Mock Connection newConnection; @Mock Connection oldConnection; @@ -102,6 +104,7 @@ void setUp() throws SQLException { when(statement.executeQuery(any())).thenReturn(resultSet); when(serviceContainer.getConnectionPluginManager()).thenReturn(pluginManager); when(serviceContainer.getStorageService()).thenReturn(storageService); + storageService = new StorageServiceImpl(mockEventPublisher); PluginServiceImpl.hostAvailabilityExpiringCache.clear(); } diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java index 89fecc192..eee427193 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java @@ -65,13 +65,13 @@ import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider.FetchTopologyResult; import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.storage.ItemCategory; +import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.storage.Topology; class RdsHostListProviderTest { - private final StorageService storageService = new StorageServiceImpl(); + private StorageService storageService; private RdsHostListProvider rdsHostListProvider; @Mock private Connection mockConnection; @@ -80,6 +80,7 @@ class RdsHostListProviderTest { @Mock private ServiceContainer mockServiceContainer; @Mock private PluginService mockPluginService; @Mock private HostListProviderService mockHostListProviderService; + @Mock private EventPublisher mockEventPublisher; @Mock Dialect mockTopologyAwareDialect; @Captor private ArgumentCaptor queryCaptor; @@ -93,6 +94,7 @@ class RdsHostListProviderTest { @BeforeEach void setUp() throws SQLException { closeable = MockitoAnnotations.openMocks(this); + storageService = new StorageServiceImpl(mockEventPublisher); when(mockServiceContainer.getHostListProviderService()).thenReturn(mockHostListProviderService); when(mockServiceContainer.getStorageService()).thenReturn(storageService); when(mockPluginService.getCurrentConnection()).thenReturn(mockConnection); @@ -128,7 +130,7 @@ void testGetTopology_returnCachedTopology() throws SQLException { rdsHostListProvider = Mockito.spy(getRdsHostListProvider("protocol://url/")); final List expected = hosts; - storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, new Topology(expected)); + storageService.set(rdsHostListProvider.clusterId, new Topology(expected)); final FetchTopologyResult result = rdsHostListProvider.getTopology(mockConnection, false); assertEquals(expected, result.hosts); @@ -141,7 +143,7 @@ void testGetTopology_withForceUpdate_returnsUpdatedTopology() throws SQLExceptio rdsHostListProvider = Mockito.spy(getRdsHostListProvider("jdbc:someprotocol://url")); rdsHostListProvider.isInitialized = true; - storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, new Topology(hosts)); + storageService.set(rdsHostListProvider.clusterId, new Topology(hosts)); final List newHosts = Collections.singletonList( new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("newHost").build()); @@ -160,7 +162,7 @@ void testGetTopology_noForceUpdate_queryReturnsEmptyHostList() throws SQLExcepti rdsHostListProvider.isInitialized = true; final List expected = hosts; - storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, new Topology(expected)); + storageService.set(rdsHostListProvider.clusterId, new Topology(expected)); doReturn(new ArrayList<>()).when(rdsHostListProvider).queryForTopology(mockConnection); @@ -226,7 +228,7 @@ void testGetCachedTopology_returnStoredTopology() throws SQLException { rdsHostListProvider = getRdsHostListProvider("jdbc:someprotocol://url"); final List expected = hosts; - storageService.set(ItemCategory.TOPOLOGY, rdsHostListProvider.clusterId, new Topology(expected)); + storageService.set(rdsHostListProvider.clusterId, new Topology(expected)); final List result = rdsHostListProvider.getStoredTopology(); assertEquals(expected, result); @@ -249,7 +251,7 @@ void testTopologyCache_NoSuggestedClusterId() throws SQLException { doReturn(topologyClusterA) .when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(0, storageService.size(Topology.class)); final List topologyProvider1 = provider1.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -270,7 +272,7 @@ void testTopologyCache_NoSuggestedClusterId() throws SQLException { final List topologyProvider2 = provider2.refresh(mock(Connection.class)); assertEquals(topologyClusterB, topologyProvider2); - assertEquals(2, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(2, storageService.size(Topology.class)); } @Test @@ -299,7 +301,7 @@ void testTopologyCache_SuggestedClusterIdForRds() throws SQLException { doReturn(topologyClusterA).when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(0, storageService.size(Topology.class)); final List topologyProvider1 = provider1.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -315,7 +317,7 @@ void testTopologyCache_SuggestedClusterIdForRds() throws SQLException { final List topologyProvider2 = provider2.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider2); - assertEquals(1, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(1, storageService.size(Topology.class)); } @Test @@ -344,7 +346,7 @@ void testTopologyCache_SuggestedClusterIdForInstance() throws SQLException { doReturn(topologyClusterA).when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(0, storageService.size(Topology.class)); final List topologyProvider1 = provider1.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -360,7 +362,7 @@ void testTopologyCache_SuggestedClusterIdForInstance() throws SQLException { final List topologyProvider2 = provider2.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider2); - assertEquals(1, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(1, storageService.size(Topology.class)); } @Test @@ -389,7 +391,7 @@ void testTopologyCache_AcceptSuggestion() throws SQLException { doAnswer(a -> topologyClusterA).when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(0, storageService.size(Topology.class)); List topologyProvider1 = provider1.refresh(mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -408,7 +410,7 @@ void testTopologyCache_AcceptSuggestion() throws SQLException { assertNotEquals(provider1.clusterId, provider2.clusterId); assertFalse(provider1.isPrimaryClusterId); assertTrue(provider2.isPrimaryClusterId); - assertEquals(2, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(2, storageService.size(Topology.class)); assertEquals("cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com", RdsHostListProvider.suggestedPrimaryClusterIdCache.get(provider1.clusterId)); diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java index 8b5fcccba..6a74f61e2 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java @@ -42,7 +42,6 @@ import java.util.Collections; import java.util.List; import java.util.Properties; -import java.util.concurrent.TimeUnit; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -60,13 +59,13 @@ import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider.FetchTopologyResult; import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.storage.ItemCategory; +import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.storage.Topology; class RdsMultiAzDbClusterListProviderTest { - private final StorageService storageService = new StorageServiceImpl(); + private StorageService storageService; private RdsMultiAzDbClusterListProvider rdsMazDbClusterHostListProvider; @Mock private Connection mockConnection; @@ -75,6 +74,7 @@ class RdsMultiAzDbClusterListProviderTest { @Mock private ServiceContainer mockServiceContainer; @Mock private PluginService mockPluginService; @Mock private HostListProviderService mockHostListProviderService; + @Mock private EventPublisher mockEventPublisher; @Mock Dialect mockTopologyAwareDialect; @Captor private ArgumentCaptor queryCaptor; @@ -88,6 +88,7 @@ class RdsMultiAzDbClusterListProviderTest { @BeforeEach void setUp() throws SQLException { closeable = MockitoAnnotations.openMocks(this); + storageService = new StorageServiceImpl(mockEventPublisher); when(mockServiceContainer.getHostListProviderService()).thenReturn(mockHostListProviderService); when(mockServiceContainer.getStorageService()).thenReturn(storageService); when(mockPluginService.getCurrentConnection()).thenReturn(mockConnection); @@ -98,14 +99,6 @@ void setUp() throws SQLException { when(mockHostListProviderService.getDialect()).thenReturn(mockTopologyAwareDialect); when(mockHostListProviderService.getHostSpecBuilder()) .thenReturn(new HostSpecBuilder(new SimpleHostAvailabilityStrategy())); - - storageService.registerItemCategoryIfAbsent( - ItemCategory.TOPOLOGY, - Topology.class, - false, - TimeUnit.MINUTES.toNanos(5), - null, - null); } @AfterEach @@ -134,7 +127,7 @@ private RdsMultiAzDbClusterListProvider getRdsMazDbClusterHostListProvider(Strin void testGetTopology_returnCachedTopology() throws SQLException { rdsMazDbClusterHostListProvider = Mockito.spy(getRdsMazDbClusterHostListProvider("protocol://url/")); final List expected = hosts; - storageService.set(ItemCategory.TOPOLOGY, rdsMazDbClusterHostListProvider.clusterId, new Topology(expected)); + storageService.set(rdsMazDbClusterHostListProvider.clusterId, new Topology(expected)); final FetchTopologyResult result = rdsMazDbClusterHostListProvider.getTopology(mockConnection, false); assertEquals(expected, result.hosts); @@ -147,8 +140,7 @@ void testGetTopology_withForceUpdate_returnsUpdatedTopology() throws SQLExceptio rdsMazDbClusterHostListProvider = Mockito.spy(getRdsMazDbClusterHostListProvider("jdbc:someprotocol://url")); rdsMazDbClusterHostListProvider.isInitialized = true; - storageService.set( - ItemCategory.TOPOLOGY, rdsMazDbClusterHostListProvider.clusterId, new Topology(hosts)); + storageService.set(rdsMazDbClusterHostListProvider.clusterId, new Topology(hosts)); final List newHosts = Collections.singletonList( new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("newHost").build()); @@ -167,8 +159,7 @@ void testGetTopology_noForceUpdate_queryReturnsEmptyHostList() throws SQLExcepti rdsMazDbClusterHostListProvider.isInitialized = true; final List expected = hosts; - storageService.set( - ItemCategory.TOPOLOGY, rdsMazDbClusterHostListProvider.clusterId, new Topology(expected)); + storageService.set(rdsMazDbClusterHostListProvider.clusterId, new Topology(expected)); doReturn(new ArrayList<>()).when(rdsMazDbClusterHostListProvider).queryForTopology(mockConnection); @@ -208,8 +199,7 @@ void testGetCachedTopology_returnCachedTopology() throws SQLException { rdsMazDbClusterHostListProvider = getRdsMazDbClusterHostListProvider("jdbc:someprotocol://url"); final List expected = hosts; - storageService.set( - ItemCategory.TOPOLOGY, rdsMazDbClusterHostListProvider.clusterId, new Topology(expected)); + storageService.set(rdsMazDbClusterHostListProvider.clusterId, new Topology(expected)); final List result = rdsMazDbClusterHostListProvider.getStoredTopology(); assertEquals(expected, result); @@ -233,7 +223,7 @@ void testTopologyCache_NoSuggestedClusterId() throws SQLException { doReturn(topologyClusterA) .when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(0, storageService.size(Topology.class)); final List topologyProvider1 = provider1.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -255,7 +245,7 @@ void testTopologyCache_NoSuggestedClusterId() throws SQLException { final List topologyProvider2 = provider2.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterB, topologyProvider2); - assertEquals(2, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(2, storageService.size(Topology.class)); } @Test @@ -285,7 +275,7 @@ void testTopologyCache_SuggestedClusterIdForRds() throws SQLException { doReturn(topologyClusterA).when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(0, storageService.size(Topology.class)); final List topologyProvider1 = provider1.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -302,7 +292,7 @@ void testTopologyCache_SuggestedClusterIdForRds() throws SQLException { final List topologyProvider2 = provider2.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider2); - assertEquals(1, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(1, storageService.size(Topology.class)); } @Test @@ -332,7 +322,7 @@ void testTopologyCache_SuggestedClusterIdForInstance() throws SQLException { doReturn(topologyClusterA).when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(0, storageService.size(Topology.class)); final List topologyProvider1 = provider1.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -349,7 +339,7 @@ void testTopologyCache_SuggestedClusterIdForInstance() throws SQLException { final List topologyProvider2 = provider2.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider2); - assertEquals(1, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(1, storageService.size(Topology.class)); } @Test @@ -379,7 +369,7 @@ void testTopologyCache_AcceptSuggestion() throws SQLException { doAnswer(a -> topologyClusterA).when(provider1).queryForTopology(any(Connection.class)); - assertEquals(0, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(0, storageService.size(Topology.class)); List topologyProvider1 = provider1.refresh(Mockito.mock(Connection.class)); assertEquals(topologyClusterA, topologyProvider1); @@ -399,7 +389,7 @@ void testTopologyCache_AcceptSuggestion() throws SQLException { assertNotEquals(provider1.clusterId, provider2.clusterId); assertFalse(provider1.isPrimaryClusterId); assertTrue(provider2.isPrimaryClusterId); - assertEquals(2, storageService.size(ItemCategory.TOPOLOGY)); + assertEquals(2, storageService.size(Topology.class)); assertEquals("cluster-a.cluster-xyz.us-east-2.rds.amazonaws.com", RdsMultiAzDbClusterListProvider.suggestedPrimaryClusterIdCache.get(provider1.clusterId)); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java index f8218ffe9..599ca5b3b 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java @@ -50,7 +50,6 @@ import software.amazon.jdbc.hostavailability.HostAvailabilityStrategy; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.util.monitoring.MonitorService; -import software.amazon.jdbc.util.storage.ItemCategory; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -124,6 +123,7 @@ public void testRun() throws InterruptedException { Region.US_EAST_1, TimeUnit.MILLISECONDS.toNanos(50), mockRdsClientFunc); + monitor.start(); // Wait for 2 run cycles. The first will return an unexpected number of endpoints in the API response, the second // will return the expected number of endpoints (one). @@ -132,7 +132,7 @@ public void testRun() throws InterruptedException { monitor.stop(); ArgumentCaptor captor = ArgumentCaptor.forClass(AllowedAndBlockedHosts.class); - verify(mockStorageService).set(eq(ItemCategory.ALLOWED_AND_BLOCKED_HOSTS), eq(host.getHost()), captor.capture()); + verify(mockStorageService).set(eq(host.getHost()), captor.capture()); assertEquals(staticMembersSet, captor.getValue().getAllowedHostIds()); assertNull(captor.getValue().getBlockedHostIds()); From 1f36aecfe7706f287b7447275de4e6f5e34175e9 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 12 Mar 2025 15:20:48 -0700 Subject: [PATCH 059/149] Small fixes, cleanup, update docs --- .../jdbc/util/events/DataAccessEvent.java | 10 +++ .../amazon/jdbc/util/events/Event.java | 4 +- .../jdbc/util/events/EventPublisher.java | 10 ++- .../jdbc/util/events/EventSubscriber.java | 8 +- .../util/events/PeriodicEventPublisher.java | 10 ++- .../events/WriterChangedExampleEvent.java | 35 -------- .../jdbc/util/monitoring/AbstractMonitor.java | 19 +++- .../amazon/jdbc/util/monitoring/Monitor.java | 10 +-- .../util/monitoring/MonitorErrorResponse.java | 5 +- .../jdbc/util/monitoring/MonitorService.java | 14 +-- .../util/monitoring/MonitorServiceImpl.java | 69 +++++++++++---- .../jdbc/util/monitoring/MonitorSettings.java | 13 +++ .../jdbc/util/monitoring/MonitorState.java | 14 +++ .../jdbc/util/storage/ExpirationCache.java | 70 ++++++++++----- .../util/storage/ExternallyManagedCache.java | 88 ++++++++++++++++++- .../jdbc/util/storage/StorageService.java | 7 +- ..._advanced_jdbc_wrapper_messages.properties | 9 +- 17 files changed, 285 insertions(+), 110 deletions(-) delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/events/WriterChangedExampleEvent.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java index b3ffe1866..0a9b236e2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java @@ -19,10 +19,20 @@ import java.util.Objects; import org.checkerframework.checker.nullness.qual.NonNull; +/** + * A class defining an occurrence of data access. The class specifies the class of the data that was accessed and the + * key for the data. + */ public class DataAccessEvent implements Event { protected @NonNull Class dataClass; protected @NonNull Object key; + /** + * Constructor for a DataAccessEvent. + * + * @param dataClass the class of the data that was accessed. + * @param key the key for the data that was accessed. + */ public DataAccessEvent(@NonNull Class dataClass, @NonNull Object key) { this.dataClass = dataClass; this.key = key; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/Event.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/Event.java index 891972603..2714f6786 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/Event.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/Event.java @@ -16,6 +16,8 @@ package software.amazon.jdbc.util.events; -// A marker interface for events that need to be communicated between different components. +/** + * A marker interface for events that need to be communicated between different components. + */ public interface Event { } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java index 694d07895..33c11f439 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java @@ -18,9 +18,13 @@ import java.util.Set; +/** + * An event publisher that publishes events to subscribers. Subscribers can specify which types of events they would + * like to receive. + */ public interface EventPublisher { /** - * Register the given subscriber for the given event classes. + * Registers the given subscriber for the given event classes. * * @param subscriber the subscriber to be notified when the given event classes occur. * @param eventClasses the classes of event that the subscriber should be notified of. @@ -28,7 +32,7 @@ public interface EventPublisher { void subscribe(EventSubscriber subscriber, Set> eventClasses); /** - * Unsubscribe the subscriber from the given event classes. + * Unsubscribes the given subscriber from the given event classes. * * @param subscriber the subscriber to unsubscribe from the given event classes. * @param eventClasses the classes of events that the subscriber wants to unsubscribe from. @@ -36,7 +40,7 @@ public interface EventPublisher { void unsubscribe(EventSubscriber subscriber, Set> eventClasses); /** - * Publish an event. All subscribers to the given event class will be notified of the event. + * Publishes an event. All subscribers to the given event class will be notified of the event. * * @param event the event to publish. */ diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java index 4cf7a25fd..b371dbc87 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java @@ -16,9 +16,15 @@ package software.amazon.jdbc.util.events; +import java.util.Set; + +/** + * An event subscriber. Subscribers can subscribe to a publisher's events using + * {@link EventPublisher#subscribe(EventSubscriber, Set)}. + */ public interface EventSubscriber { /** - * Process an event. This method will only be called on this subscriber if it has subscribed to the event class via + * Processes an event. This method will only be called on this subscriber if it has subscribed to the event class via * {@link EventPublisher#subscribe}. * * @param event the event to process. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java index dff7f674d..cda64dd5e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java @@ -24,6 +24,10 @@ import java.util.concurrent.TimeUnit; import software.amazon.jdbc.util.StringUtils; +/** + * An event publisher that periodically sends out all messages received during the latest interval of time. Messages are + * recorded in a set so that messages of equivalent value are not duplicated in the same message batch. + */ public class PeriodicEventPublisher implements EventPublisher { protected static final long DEFAULT_MESSAGE_INTERVAL_NANOS = TimeUnit.SECONDS.toNanos(30); protected final Map, Set> subscribers = new ConcurrentHashMap<>(); @@ -43,7 +47,11 @@ public PeriodicEventPublisher() { this(DEFAULT_MESSAGE_INTERVAL_NANOS); } - + /** + * Constructs a PeriodicEventPublisher instance and submits a thread to periodically send message batches. + * + * @param messageIntervalNanos the rate at which messages batches should be sent, in nanoseconds. + */ public PeriodicEventPublisher(long messageIntervalNanos) { cleanupExecutor.scheduleAtFixedRate( this::sendMessages, messageIntervalNanos, messageIntervalNanos, TimeUnit.NANOSECONDS); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/WriterChangedExampleEvent.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/WriterChangedExampleEvent.java deleted file mode 100644 index 07a08924f..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/WriterChangedExampleEvent.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.util.events; - -public class WriterChangedExampleEvent implements Event { - final String newWriterUrl; - final String oldWriterUrl; - - public WriterChangedExampleEvent(String newWriterUrl, String oldWriterUrl) { - this.newWriterUrl = newWriterUrl; - this.oldWriterUrl = oldWriterUrl; - } - - public String getNewWriterUrl() { - return newWriterUrl; - } - - public String getOldWriterUrl() { - return oldWriterUrl; - } -} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index bcbd86246..41dc4f403 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -19,9 +19,13 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.logging.Logger; +import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.StringUtils; +/** + * An AbstractMonitor that implements common monitor logic. + */ public abstract class AbstractMonitor implements Monitor, Runnable { private static final Logger LOGGER = Logger.getLogger(AbstractMonitor.class.getName()); protected final MonitorService monitorService; @@ -29,7 +33,7 @@ public abstract class AbstractMonitor implements Monitor, Runnable { final Thread monitoringThread = new Thread(runnableTarget); monitoringThread.setDaemon(true); if (!StringUtils.isNullOrEmpty(monitoringThread.getName())) { - monitoringThread.setName(monitoringThread.getName() + "-" + getMonitorSuffix()); + monitoringThread.setName(monitoringThread.getName() + "-" + getMonitorNameSuffix()); } return monitoringThread; }); @@ -48,6 +52,11 @@ public void start() { this.monitorExecutor.shutdown(); } + /** + * Starts the monitor workflow, making sure to set the initial state of the monitor. The monitor's workflow is wrapped + * in a try-catch so that unexpected exceptions are reported to the monitor service and the monitor's state is updated + * to {@link MonitorState#ERROR}. + */ @Override public void run() { try { @@ -76,7 +85,13 @@ public boolean canDispose() { return true; } - private String getMonitorSuffix() { + /** + * Forms the suffix for the monitor thread name by abbreviating the concrete class name. For example, a + * {@link CustomEndpointMonitorImpl} will have a suffix of "cemi". + * + * @return the suffix for the monitor thread name. + */ + private String getMonitorNameSuffix() { return this.getClass().getSimpleName().replaceAll("[a-z]", "").toLowerCase(); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index af3e0a38e..b2d7e0eb7 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -18,30 +18,30 @@ public interface Monitor { /** - * Submit this monitor in a separate thread to begin its monitoring tasks. + * Submits this monitor in a separate thread to begin its monitoring tasks. */ void start(); /** - * Execute the monitoring loop for this monitor. This method should be called in the run() method of the thread + * Executes the monitoring loop for this monitor. This method should be called in the run() method of the thread * submitted during the call to {@link #start()}. */ void monitor(); /** - * Stop the monitoring tasks for this monitor and close all resources. + * Stops the monitoring tasks for this monitor and close all resources. */ void stop(); /** - * Get the timestamp for the last action performed by this monitor, in nanoseconds. + * Gets the timestamp for the last action performed by this monitor, in nanoseconds. * * @return the timestamp for the last action performed by this monitor, in nanoseconds. */ long getLastActivityTimestampNanos(); /** - * Get the current state of this monitor. + * Gets the current state of this monitor. * * @return the current state of this monitor. */ diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorErrorResponse.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorErrorResponse.java index 9c0b2a9d8..e4fb99bc8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorErrorResponse.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorErrorResponse.java @@ -16,7 +16,10 @@ package software.amazon.jdbc.util.monitoring; +/** + * An enum defining which action to perform if a {@link Monitor} enters an error state or is discovered to be stuck. + */ public enum MonitorErrorResponse { NO_ACTION, - RESTART + RECREATE } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 3e6ab9a12..472cbebe0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -23,10 +23,10 @@ public interface MonitorService { /** - * Register a new monitor type with the monitor service. This method needs to be called before adding new types of - * monitors to the monitor service, so that the monitor service knows when to dispose of a monitor. - * Expected monitor types ("topology" and "customEndpoint") will be added automatically during driver initialization, - * but this method can be called by users if they want to add a new monitor type. + * Registers a new monitor type with the monitor service. This method needs to be called before adding new types of + * monitors to the monitor service, so that the monitor service knows when to dispose of a monitor. Expected monitor + * types will be added automatically during driver initialization, but this method can be called by users if they want + * to add a new monitor type. * * @param monitorClass the class of the monitor, eg `CustomEndpointMonitorImpl.class`. * @param expirationTimeoutNanos how long a monitor should be stored without use before being considered expired, in @@ -34,7 +34,7 @@ public interface MonitorService { * monitor will be stopped. * @param heartbeatTimeoutNanos a duration in nanoseconds defining the maximum amount of time that a monitor should * take between updating its last-updated timestamp. If a monitor has not updated its - * last-updated timestamp within this value it will be considered stuck. + * last-updated timestamp within this duration it will be considered stuck. * @param errorResponses a Set defining actions to take if the monitor is in an error state. * @param shouldDisposeFunc a function defining whether an item should be stopped if expired. If `null` is * passed, the monitor will always be stopped if the monitor is expired. @@ -61,8 +61,8 @@ void registerMonitorTypeIfAbsent( T runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier); /** - * Process a monitor error. The monitor service will respond to the error based on the monitor error responses defined - * when the monitor type was registered. + * Processes a monitor error. The monitor service will respond to the error based on the monitor error responses + * defined when the monitor type was registered. * * @param monitor the monitor that encountered the unexpected exception. * @param exception the unexpected exception that occurred. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 8a98133a7..2733f561a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -46,9 +46,9 @@ public class MonitorServiceImpl implements MonitorService, EventSubscriber { private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(1); protected static final Map, CacheContainer> monitorCaches = new ConcurrentHashMap<>(); + protected static final Map, Supplier> defaultSuppliers; protected static final AtomicBoolean isInitialized = new AtomicBoolean(false); protected static final ReentrantLock initLock = new ReentrantLock(); - protected static final Map, Supplier> defaultSuppliers; protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { final Thread thread = new Thread(r); thread.setDaemon(true); @@ -58,12 +58,10 @@ public class MonitorServiceImpl implements MonitorService, EventSubscriber { return thread; })); - protected final EventPublisher publisher; - static { Map, Supplier> suppliers = new HashMap<>(); Set resetErrorResponse = - new HashSet<>(Collections.singletonList(MonitorErrorResponse.RESTART)); + new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)); MonitorSettings defaultSettings = new MonitorSettings( TimeUnit.MINUTES.toNanos(5), TimeUnit.MINUTES.toNanos(1), resetErrorResponse); @@ -72,10 +70,20 @@ public class MonitorServiceImpl implements MonitorService, EventSubscriber { defaultSuppliers = Collections.unmodifiableMap(suppliers); } + protected final EventPublisher publisher; + public MonitorServiceImpl(EventPublisher publisher) { this(DEFAULT_CLEANUP_INTERVAL_NANOS, publisher); } + /** + * Constructs a MonitorServiceImpl instance, subscribes to the given publisher's data access events, and submits a + * cleanup thread to supervise submitted monitors. + * + * @param cleanupIntervalNanos the interval at which the cleanup thread should check on submitted monitors, in + * nanoseconds. + * @param publisher the publisher to subscribe to for data access events. + */ public MonitorServiceImpl(long cleanupIntervalNanos, EventPublisher publisher) { this.publisher = publisher; this.publisher.subscribe(this, new HashSet<>(Collections.singletonList(DataAccessEvent.class))); @@ -129,31 +137,33 @@ protected void checkMonitors() { // Monitor has been inactive for longer than the inactive timeout and is considered stuck. LOGGER.fine( Messages.get("MonitorServiceImpl.monitorStuck", - new Object[]{removedItem.getMonitor(), TimeUnit.NANOSECONDS.toSeconds(inactiveTimeoutNanos)})); + new Object[] {removedItem.getMonitor(), TimeUnit.NANOSECONDS.toSeconds(inactiveTimeoutNanos)})); handleMonitorError(container, key, removedItem); continue; } removedItem = cache.removeExpiredIf(key, mi -> mi.getMonitor().canDispose()); if (removedItem != null) { + LOGGER.fine( + Messages.get("MonitorServiceImpl.removedExpiredMonitor", new Object[] {removedItem.getMonitor()})); removedItem.getMonitor().stop(); } } } } - private void handleMonitorError( + protected void handleMonitorError( CacheContainer cacheContainer, Object key, - MonitorItem oldMonitorItem) { - Monitor monitor = oldMonitorItem.getMonitor(); + MonitorItem errorMonitorItem) { + Monitor monitor = errorMonitorItem.getMonitor(); monitor.stop(); Set errorResponses = cacheContainer.getSettings().getErrorResponses(); - if (errorResponses.contains(MonitorErrorResponse.RESTART)) { - LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[]{monitor})); + if (errorResponses.contains(MonitorErrorResponse.RECREATE)) { + LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[] {monitor})); cacheContainer.getCache().computeIfAbsent(key, k -> { - MonitorItem newMonitorItem = new MonitorItem(oldMonitorItem.getMonitorSupplier()); + MonitorItem newMonitorItem = new MonitorItem(errorMonitorItem.getMonitorSupplier()); newMonitorItem.getMonitor().start(); return newMonitorItem; }); @@ -187,12 +197,12 @@ public T runIfAbsent(Class monitorClass, Object key, Supp cacheContainer = monitorCaches.computeIfAbsent(monitorClass, k -> supplier.get()); } - Monitor monitor = - cacheContainer.getCache().computeIfAbsent(key, k -> { - MonitorItem monitorItem = new MonitorItem(monitorSupplier); - monitorItem.getMonitor().start(); - return monitorItem; - }).getMonitor(); + Monitor monitor = cacheContainer.getCache().computeIfAbsent(key, k -> { + MonitorItem monitorItem = new MonitorItem(monitorSupplier); + monitorItem.getMonitor().start(); + return monitorItem; + }).getMonitor(); + if (monitorClass.isInstance(monitor)) { return monitorClass.cast(monitor); } @@ -205,7 +215,8 @@ public T runIfAbsent(Class monitorClass, Object key, Supp public void reportMonitorError(Monitor monitor, Exception exception) { CacheContainer cacheContainer = monitorCaches.get(monitor.getClass()); if (cacheContainer == null) { - LOGGER.fine(Messages.get("MonitorServiceImpl.unregisteredMonitorError", new Object[] {monitor, exception})); + LOGGER.fine( + Messages.get("MonitorServiceImpl.monitorErrorForUnregisteredType", new Object[] {monitor, exception})); return; } @@ -272,15 +283,27 @@ public void processEvent(Event event) { continue; } + // The data produced by the monitor in this cache with this key has been accessed recently, so we extend the + // monitor's expiration. container.getCache().extendExpiration(accessEvent.getKey()); } } + /** + * A container that holds a cache of monitors of a given type with the related settings and info for that type. + */ protected static class CacheContainer { private @NonNull final MonitorSettings settings; private @NonNull final ExternallyManagedCache cache; private @Nullable final Class producedDataClass; + /** + * Constructs a CacheContainer instance. As part of the constructor, a new cache will be created based on the given + * settings. + * + * @param settings the settings for the cache and monitor type. + * @param producedDataClass the class of the data produced by the monitor type, if it produces any data. + */ public CacheContainer(@NonNull final MonitorSettings settings, @Nullable Class producedDataClass) { this.settings = settings; this.cache = new ExternallyManagedCache<>(true, settings.getExpirationTimeoutNanos()); @@ -300,10 +323,20 @@ public CacheContainer(@NonNull final MonitorSettings settings, @Nullable Class monitorSupplier; private @NonNull final Monitor monitor; + /** + * Constructs a MonitorItem instance. As part of the constructor, a new monitor will be created using the given + * monitor supplier. + * + * @param monitorSupplier a supplier lambda that produces a monitor. + */ protected MonitorItem(@NonNull Supplier monitorSupplier) { this.monitorSupplier = monitorSupplier; this.monitor = monitorSupplier.get(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java index 8fd030ebe..e97dafede 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java @@ -19,11 +19,24 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; +/** + * A class defining settings for a monitor or monitor type. + */ public class MonitorSettings { private final long expirationTimeoutNanos; private final long inactiveTimeoutNanos; private @NonNull final Set errorResponses; + /** + * Constructs a MonitorSettings instance. + * + * @param expirationTimeoutNanos the amount of time that a monitor should sit in a cache before being considered + * expired. + * @param inactiveTimeoutNanos a duration in nanoseconds defining the maximum amount of time that a monitor should + * take between updating its last-updated timestamp. If a monitor has not updated its + * last-updated timestamp within this duration it will be considered stuck. + * @param errorResponses a Set defining actions to take if the monitor is in an error state. + */ public MonitorSettings( long expirationTimeoutNanos, long inactiveTimeoutNanos, @NonNull Set errorResponses) { this.expirationTimeoutNanos = expirationTimeoutNanos; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java index 62790e1f5..9d57f233e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java @@ -16,8 +16,22 @@ package software.amazon.jdbc.util.monitoring; +/** + * Represents the different states of a monitor. + */ public enum MonitorState { + /** + * The monitor is running. + */ RUNNING, + + /** + * The monitor has stopped running in an expected manner. + */ STOPPED, + + /** + * The monitor has stopped due to an unexpected error. + */ ERROR } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index 3da6ed398..497e05623 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -28,6 +28,12 @@ import software.amazon.jdbc.util.ItemDisposalFunc; import software.amazon.jdbc.util.ShouldDisposeFunc; +/** + * A cache that can be used to store values that expire after a configured period of time. + * + * @param the type of the keys in the cache. + * @param the type of the values in the cache. + */ public class ExpirationCache { private static final Logger LOGGER = Logger.getLogger(ExpirationCache.class.getName()); protected static final long DEFAULT_TIME_TO_LIVE_NANOS = TimeUnit.MINUTES.toNanos(5); @@ -41,6 +47,18 @@ public ExpirationCache() { this(false, DEFAULT_TIME_TO_LIVE_NANOS, null, null); } + /** + * Constructs an ExpirationCache instance. + * + * @param isRenewableExpiration controls whether an item's expiration should be renewed when retrieved. If the item is + * expired when it is retrieved and isRenewableExpiration is true, the item's expiration + * will be renewed and the item will be returned. + * @param timeToLiveNanos the duration that the item should sit in the cache before being considered expired, in + * nanoseconds. + * @param shouldDisposeFunc a function defining the conditions under which an expired entry should be cleaned up. + * @param itemDisposalFunc a function defining how to dispose of an item when it is removed. If null is passed, + * the item will be removed without performing any additional operations. + */ public ExpirationCache( final boolean isRenewableExpiration, final long timeToLiveNanos, @@ -53,10 +71,10 @@ public ExpirationCache( } /** - * Store the given value at the given key. + * Stores the given value at the given key. * - * @param key the key at which the value should be stored - * @param value the value to be stored + * @param key the key at which the value should be stored. + * @param value the value to store. * @return the previous value stored at the given key, or null if there was no previous value. If the previous value * is expired it will be disposed and returned. */ @@ -82,10 +100,10 @@ public ExpirationCache( * If a value does not exist for the given key or the existing value is expired and non-renewable, stores the value * returned by the given mapping function, unless the function returns null, in which case the key will be removed. * - * @param key the key for the new or existing value - * @param mappingFunction the function to call to compute a new value + * @param key the key for the new or existing value. + * @param mappingFunction the function to call to compute a new value. * @return the current (existing or computed) value associated with the specified key, or null if the computed value - * is null + * is null. */ public @Nullable V computeIfAbsent( final K key, @@ -130,8 +148,8 @@ public ExpirationCache( /** * Retrieves the value stored at the given key. * - * @param key the key from which to retrieve the value - * @return the value stored at the given key, or null if there is no existing value + * @param key the key from which to retrieve the value. + * @return the value stored at the given key, or null if there is no existing value. */ public @Nullable V get(final K key) { final CacheItem cacheItem = cache.get(key); @@ -156,7 +174,7 @@ public boolean exists(final K key) { /** * Removes and disposes of the value stored at the given key. * - * @param key the key associated with the value to be removed and disposed + * @param key the key associated with the value to be removed and disposed. * @return the value removed from the cache, or null if the key does not exist in the cache. If the value was expired, * it will still be returned. */ @@ -177,6 +195,9 @@ public boolean exists(final K key) { return cacheItem.item; } + /** + * Removes and disposes of all expired entries in the cache. + */ public void removeExpiredEntries() { cache.forEach((key, value) -> { try { @@ -187,6 +208,12 @@ public void removeExpiredEntries() { }); } + /** + * Removes and disposes of the item stored at the given key if it is expired and the {@link ShouldDisposeFunc} + * (if defined) returns true for the item. Otherwise, does nothing. + * + * @param key the key for the value to check for removal. + */ public void removeIfExpired(K key) { // A list is used to store the cached item for later disposal since lambdas require references to outer variables // to be final. This allows us to dispose of the item after it has been removed and the cache has been unlocked, @@ -213,7 +240,7 @@ public void removeIfExpired(K key) { } /** - * Remove and dispose of all entries in the cache. + * Removes and disposes of all entries in the cache. */ public void clear() { for (K key : cache.keySet()) { @@ -223,9 +250,9 @@ public void clear() { } /** - * Get a map copy of all entries in the cache, including expired entries. + * Gets a map copy of all entries in the cache, including expired entries. * - * @return a map copy of all entries in the cache, including expired entries + * @return a map copy of all entries in the cache, including expired entries. */ public Map getEntries() { final Map entries = new HashMap<>(); @@ -237,22 +264,25 @@ public Map getEntries() { } /** - * Get the current size of the cache, including expired entries. + * Gets the current size of the cache, including expired entries. * - * @return the current size of the cache, including expired entries + * @return the current size of the cache, including expired entries. */ public int size() { return this.cache.size(); } + /** + * A container class that holds a cache value together with the time at which the value should be considered expired. + */ protected class CacheItem { private final V item; private long expirationTimeNanos; /** - * CacheItem constructor. + * Constructs a CacheItem. * - * @param item the item value + * @param item the item value. * @param expirationTimeNanos the time at which a CacheItem should be considered expired. */ protected CacheItem(final V item, final long expirationTimeNanos) { @@ -261,12 +291,10 @@ protected CacheItem(final V item, final long expirationTimeNanos) { } /** - * Determines if a cache item should be cleaned up. An item should be cleaned up if it has past - * its expiration time and {@link ShouldDisposeFunc} (if defined) indicates that it should be - * cleaned up. + * Determines if a cache item should be cleaned up. An item should be cleaned up if it has past its expiration time + * and the {@link ShouldDisposeFunc} (if defined) indicates that it should be cleaned up. * - * @return true if the cache item should be cleaned up at cleanup time. Otherwise, returns - * false. + * @return true if the cache item should be cleaned up at cleanup time. Otherwise, returns false. */ protected boolean shouldCleanup() { final boolean isExpired = this.expirationTimeNanos != 0 && System.nanoTime() > this.expirationTimeNanos; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java index 4b8d74c27..12df78b13 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java @@ -27,19 +27,44 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +/** + * A cache with expiration functionality that does not automatically remove expired entries. The removal of expired + * entries is instead handled by an external class. This class is similar to {@link ExpirationCache}, but allows users + * to manually renew item expiration and provides more control over the conditions in which items are removed. Disposal + * of removed items should be handled outside of this class. + * + * @param the type of the keys in the cache. + * @param the type of the values in the cache. + */ public class ExternallyManagedCache { private static final Logger LOGGER = Logger.getLogger(ExpirationCache.class.getName()); protected final Map cache = new ConcurrentHashMap<>(); protected final boolean isRenewableExpiration; protected final long timeToLiveNanos; + /** + * Constructs an externally managed cache. + * + * @param isRenewableExpiration controls whether an item's expiration should be renewed when retrieved. If the item is + * expired when it is retrieved and isRenewableExpiration is true, the item's expiration + * will be renewed and the item will be returned. + * @param timeToLiveNanos the duration that the item should sit in the cache before being considered expired, in + * nanoseconds. + */ public ExternallyManagedCache(boolean isRenewableExpiration, long timeToLiveNanos) { this.isRenewableExpiration = isRenewableExpiration; this.timeToLiveNanos = timeToLiveNanos; } + /** + * Stores the given value in the cache at the given key. + * + * @param key the key for the value. + * @param value the value to store. + * @return the previous value stored at the key, or null if there was no value stored at the key. + */ public @Nullable V put(@NonNull K key, @NonNull V value) { - CacheItem cacheItem = this.cache.put(key, new CacheItem(value, timeToLiveNanos)); + CacheItem cacheItem = this.cache.put(key, new CacheItem(value, System.nanoTime() + timeToLiveNanos)); if (cacheItem == null) { return null; } @@ -47,6 +72,15 @@ public ExternallyManagedCache(boolean isRenewableExpiration, long timeToLiveNano return cacheItem.item; } + /** + * If a value does not exist for the given key, stores the value returned by the given mapping function, unless the + * function returns null, in which case the key will be removed. If the + * + * @param key the key for the new or existing value. + * @param mappingFunction the function to call to compute a new value. + * @return the current (existing or computed) value associated with the specified key, or null if the computed value + * is null. + */ public @NonNull V computeIfAbsent(K key, Function mappingFunction) { final CacheItem cacheItem = cache.compute( key, @@ -58,24 +92,40 @@ public ExternallyManagedCache(boolean isRenewableExpiration, long timeToLiveNano System.nanoTime() + this.timeToLiveNanos); } - // The existing value is non-expired or renewable. Keep the existing value. + // TODO: what if the object is expired and non-renewable? The caller needs the old value so that it can + // dispose of it, but this could be confusing since computeIfAbsent returns the current value in other + // classes. if (this.isRenewableExpiration) { valueItem.extendExpiration(); } + // The existing value is non-expired or renewable. Keep the existing value. return valueItem; }); return cacheItem.item; } + /** + * Extends the expiration of the item stored at the given key, if it exists. + * + * @param key the key for the value whose expiration should be extended. + */ public void extendExpiration(K key) { final CacheItem cacheItem = cache.get(key); + // TODO: should we log if the key doesn't exist? + if (cacheItem != null) { cacheItem.extendExpiration(); } } + /** + * Removes the value stored at the given key from the cache. + * + * @param key the key for the value to be removed. + * @return the value that was removed, or null if the key did not exist. + */ public @Nullable V remove(K key) { CacheItem cacheItem = cache.remove(key); if (cacheItem == null) { @@ -85,6 +135,14 @@ public void extendExpiration(K key) { return cacheItem.item; } + /** + * Removes the value stored at the given key if the given predicate returns true for the value. Otherwise, does + * nothing. + * + * @param key the key for the value to assess for removal. + * @param predicate a predicate lambda that defines the condition under which the value should be removed. + * @return the removed value, or null if no value was removed. + */ public @Nullable V removeIf(K key, Predicate predicate) { // The function only returns a value if it was removed. A list is used to store the removed item since lambdas // require references to outer variables to be final. @@ -107,6 +165,15 @@ public void extendExpiration(K key) { } } + /** + * Removes the value stored at the given key if it is expired and the given predicate returns true for the value. + * Otherwise, does nothing. + * + * @param key the key for the value to assess for removal. + * @param predicate a predicate lambda that defines the condition under which the value should be removed if it is + * also expired. + * @return the removed value, or null if no value was removed. + */ public @Nullable V removeExpiredIf(K key, Predicate predicate) { // The function only returns a value if it was removed. A list is used to store the removed item since lambdas // require references to outer variables to be final. @@ -129,6 +196,11 @@ public void extendExpiration(K key) { } } + /** + * Gets a map copy of all entries in the cache, including expired entries. + * + * @return a map copy of all entries in the cache, including expired entries. + */ public Map getEntries() { final Map entries = new HashMap<>(); for (final Map.Entry entry : this.cache.entrySet()) { @@ -138,14 +210,17 @@ public Map getEntries() { return entries; } + /** + * A container class that holds a cache value together with the time at which the value should be considered expired. + */ protected class CacheItem { private final @NonNull V item; private long expirationTimeNanos; /** - * CacheItem constructor. + * Constructs a CacheItem. * - * @param item the item value + * @param item the item value. * @param expirationTimeNanos the amount of time before a CacheItem should be marked as expired. */ protected CacheItem(@NonNull final V item, final long expirationTimeNanos) { @@ -153,6 +228,11 @@ protected CacheItem(@NonNull final V item, final long expirationTimeNanos) { this.expirationTimeNanos = expirationTimeNanos; } + /** + * Indicates whether this item is expired. + * + * @return true if this item is expired, otherwise returns false. + */ protected boolean isExpired() { return System.nanoTime() >= expirationTimeNanos; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java index fec14e23e..c54b90a3b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java @@ -23,10 +23,9 @@ public interface StorageService { /** - * Register a new item class with the storage service. This method needs to be called before adding new classes of - * items to the service, so that the service knows when and how to dispose of the item. Expected item classes - * (`Topology.class` and `AllowedAndBlockedHosts.class`) will be added automatically during driver initialization, but - * this method can be called to add new classes of items. + * Registers a new item class with the storage service. This method needs to be called before adding new classes of + * items to the service, so that the service knows when and how to dispose of the item. Expected item classes will be + * added automatically during driver initialization, but this method can be called to add new classes of items. * * @param itemClass the class of the item that will be stored, eg `CustomEndpointInfo.class`. * @param isRenewableExpiration controls whether the item's expiration should be renewed if the item is fetched, diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 0b48c96b4..30caae467 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -139,10 +139,8 @@ CustomEndpointMonitorImpl.unexpectedNumberOfEndpoints=Unexpected number of custo # Custom Endpoint Plugin CustomEndpointPlugin.timedOutWaitingForCustomEndpointInfo=The custom endpoint plugin timed out after {0}ms while waiting for custom endpoint info for host ''{1}''. -CustomEndpointPlugin.closeMonitors=Closing custom endpoint monitors. Active custom endpoint monitors will be stopped, closed, and removed from the monitors cache. CustomEndpointPlugin.connectionRequestToCustomEndpoint=Detected a connection request to a custom endpoint URL: ''{0}''. CustomEndpointPlugin.errorParsingEndpointIdentifier=Unable to parse custom endpoint identifier from URL: ''{0}''. -CustomEndpointPlugin.foundInfoInCache=Done waiting for custom endpoint info for ''{0}'':\n{1} CustomEndpointPlugin.interruptedThread=The custom endpoint plugin was interrupted while waiting for custom endpoint info for host ''{0}''. CustomEndpointPlugin.unableToDetermineRegion=Unable to determine connection region. If you are using a non-standard RDS URL, please set the ''{0}'' property. CustomEndpointPlugin.waitingForCustomEndpointInfo=Custom endpoint info for ''{0}'' was not found. Waiting {1}ms for the endpoint monitor to fetch info... @@ -295,17 +293,14 @@ MonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should # monitoring.MonitorServiceImpl MonitorServiceImpl.monitorErrorForMissingMonitor=Monitor ''{0}'' reported an error to the monitor service, but the monitor service could not find the monitor under its registered monitors. Reported error: ''{1}''. -MonitorServiceImpl.monitorErrorForNullCache=Monitor ''{0}'' reported an error to the monitor service, but the cache for the monitor type was null. Reported error: ''{1}''. MonitorServiceImpl.monitorStuck=Monitor ''{0}'' has not been updated within the inactive timeout of {1} seconds. MonitorServiceImpl.monitorTypeNotRegistered=The given monitor class ''{0}'' is not registered. Please register the monitor class before running monitors of that class with the monitor service. +MonitorServiceImpl.removedExpiredMonitor=Removed expired monitor: ''{0}''. MonitorServiceImpl.restartingMonitor=Restarting monitor: ''{0}''. MonitorServiceImpl.stopAndRemoveMissingMonitorType=The monitor service received a request to stop a monitor with type ''{0}'' and key ''{1}'', but the monitor service does not have any monitors registered under the given type. Please ensure monitors are registered under the correct type. MonitorServiceImpl.stopAndRemoveMonitorsMissingType=The monitor service received a request to stop all monitors with type ''{0}'', but the monitor service does not have any monitors registered under the given type. Please ensure monitors are registered under the correct type. -MonitorServiceImpl.stopAndRemoveMonitorsNullCache=The monitor service received a request to stop all monitors with type ''{0}'', but the cache for the given type is null. Please ensure monitors are registered under the correct type. -MonitorServiceImpl.stopAndRemoveNullCache=The monitor service received a request to stop a monitor with type ''{0}'' and key ''{1}'', but the cache for the given type is null. Please ensure monitors are registered under the correct type. - MonitorServiceImpl.unexpectedMonitorClass=Monitor type mismatch - the monitor ''{0}'' was unexpectedly found under the ''{1}'' monitor class category. Please verify that monitors are submitted under their concrete class. -MonitorServiceImpl.unregisteredMonitorError=Monitor ''{0}'' reported an error to the monitor service, but the monitor type has not been registered. Reported error: ''{1}''. +MonitorServiceImpl.monitorErrorForUnregisteredType=Monitor ''{0}'' reported an error to the monitor service, but the monitor type has not been registered. Reported error: ''{1}''. NodeMonitoringThread.detectedWriter=Writer detected by node monitoring thread: ''{0}''. NodeMonitoringThread.invalidWriterQuery=The writer topology query is invalid: {0} From eb48888bc1a68ff95cdef3c34a0745aa63fa0ae0 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 14 Mar 2025 13:51:47 -0700 Subject: [PATCH 060/149] Remove isRenewableExpiration option for ExternallyManagedCache, cleanup --- .../CustomEndpointMonitorImpl.java | 1 + .../amazon/jdbc/util/events/EventPublisher.java | 2 +- .../jdbc/util/events/PeriodicEventPublisher.java | 3 +++ .../jdbc/util/monitoring/AbstractMonitor.java | 8 ++++---- .../amazon/jdbc/util/monitoring/Monitor.java | 2 +- .../jdbc/util/monitoring/MonitorService.java | 4 ---- .../jdbc/util/monitoring/MonitorServiceImpl.java | 4 +--- .../util/storage/ExternallyManagedCache.java | 16 ++-------------- 8 files changed, 13 insertions(+), 27 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index 5de5f4cf5..1d1ca17da 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -110,6 +110,7 @@ public void monitor() { while (!this.stop.get() && !Thread.currentThread().isInterrupted()) { try { long start = System.nanoTime(); + this.lastActivityTimestampNanos = System.nanoTime(); final Filter customEndpointFilter = Filter.builder().name("db-cluster-endpoint-type").values("custom").build(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java index 33c11f439..21e460a6e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventPublisher.java @@ -27,7 +27,7 @@ public interface EventPublisher { * Registers the given subscriber for the given event classes. * * @param subscriber the subscriber to be notified when the given event classes occur. - * @param eventClasses the classes of event that the subscriber should be notified of. + * @param eventClasses the classes of events that the subscriber should be notified of. */ void subscribe(EventSubscriber subscriber, Set> eventClasses); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java index cda64dd5e..cff2ff7d9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java @@ -27,6 +27,9 @@ /** * An event publisher that periodically sends out all messages received during the latest interval of time. Messages are * recorded in a set so that messages of equivalent value are not duplicated in the same message batch. + * + * TODO: should duplicate events be allowed? Data access events may happen frequently so duplicates could result in + * unnecessary repeated processing. */ public class PeriodicEventPublisher implements EventPublisher { protected static final long DEFAULT_MESSAGE_INTERVAL_NANOS = TimeUnit.SECONDS.toNanos(30); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index 41dc4f403..c6d5c0ac1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -38,12 +38,12 @@ public abstract class AbstractMonitor implements Monitor, Runnable { return monitoringThread; }); - protected long lastUsedTimestampNanos; + protected long lastActivityTimestampNanos; protected MonitorState state; protected AbstractMonitor(MonitorService monitorService) { this.monitorService = monitorService; - this.lastUsedTimestampNanos = System.nanoTime(); + this.lastActivityTimestampNanos = System.nanoTime(); } @Override @@ -61,7 +61,7 @@ public void start() { public void run() { try { this.state = MonitorState.RUNNING; - this.lastUsedTimestampNanos = System.nanoTime(); + this.lastActivityTimestampNanos = System.nanoTime(); monitor(); } catch (Exception e) { LOGGER.fine(Messages.get("AbstractMonitor.unexpectedError", new Object[]{this, e})); @@ -72,7 +72,7 @@ public void run() { @Override public long getLastActivityTimestampNanos() { - return this.lastUsedTimestampNanos; + return this.lastActivityTimestampNanos; } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index b2d7e0eb7..c8eb5387f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -29,7 +29,7 @@ public interface Monitor { void monitor(); /** - * Stops the monitoring tasks for this monitor and close all resources. + * Stops the monitoring tasks for this monitor and closes all resources. */ void stop(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 472cbebe0..2da8801dd 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -19,7 +19,6 @@ import java.util.Set; import java.util.function.Supplier; import org.checkerframework.checker.nullness.qual.Nullable; -import software.amazon.jdbc.util.ShouldDisposeFunc; public interface MonitorService { /** @@ -36,8 +35,6 @@ public interface MonitorService { * take between updating its last-updated timestamp. If a monitor has not updated its * last-updated timestamp within this duration it will be considered stuck. * @param errorResponses a Set defining actions to take if the monitor is in an error state. - * @param shouldDisposeFunc a function defining whether an item should be stopped if expired. If `null` is - * passed, the monitor will always be stopped if the monitor is expired. * @param producedDataClass the class of data produced by the monitor. */ void registerMonitorTypeIfAbsent( @@ -45,7 +42,6 @@ void registerMonitorTypeIfAbsent( long expirationTimeoutNanos, long heartbeatTimeoutNanos, Set errorResponses, - @Nullable ShouldDisposeFunc shouldDisposeFunc, @Nullable Class producedDataClass); /** diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 2733f561a..77a0063e1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -34,7 +34,6 @@ import software.amazon.jdbc.AllowedAndBlockedHosts; import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ShouldDisposeFunc; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.events.DataAccessEvent; import software.amazon.jdbc.util.events.Event; @@ -176,7 +175,6 @@ public void registerMonitorTypeIfAbsent( long expirationTimeoutNanos, long heartbeatTimeoutNanos, Set errorResponses, - @Nullable ShouldDisposeFunc shouldDisposeFunc, @Nullable Class producedDataClass) { monitorCaches.computeIfAbsent( monitorClass, @@ -306,7 +304,7 @@ protected static class CacheContainer { */ public CacheContainer(@NonNull final MonitorSettings settings, @Nullable Class producedDataClass) { this.settings = settings; - this.cache = new ExternallyManagedCache<>(true, settings.getExpirationTimeoutNanos()); + this.cache = new ExternallyManagedCache<>(settings.getExpirationTimeoutNanos()); this.producedDataClass = producedDataClass; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java index 12df78b13..31c548cfd 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java @@ -39,20 +39,15 @@ public class ExternallyManagedCache { private static final Logger LOGGER = Logger.getLogger(ExpirationCache.class.getName()); protected final Map cache = new ConcurrentHashMap<>(); - protected final boolean isRenewableExpiration; protected final long timeToLiveNanos; /** * Constructs an externally managed cache. * - * @param isRenewableExpiration controls whether an item's expiration should be renewed when retrieved. If the item is - * expired when it is retrieved and isRenewableExpiration is true, the item's expiration - * will be renewed and the item will be returned. * @param timeToLiveNanos the duration that the item should sit in the cache before being considered expired, in * nanoseconds. */ - public ExternallyManagedCache(boolean isRenewableExpiration, long timeToLiveNanos) { - this.isRenewableExpiration = isRenewableExpiration; + public ExternallyManagedCache(long timeToLiveNanos) { this.timeToLiveNanos = timeToLiveNanos; } @@ -92,14 +87,7 @@ public ExternallyManagedCache(boolean isRenewableExpiration, long timeToLiveNano System.nanoTime() + this.timeToLiveNanos); } - // TODO: what if the object is expired and non-renewable? The caller needs the old value so that it can - // dispose of it, but this could be confusing since computeIfAbsent returns the current value in other - // classes. - if (this.isRenewableExpiration) { - valueItem.extendExpiration(); - } - - // The existing value is non-expired or renewable. Keep the existing value. + valueItem.extendExpiration(); return valueItem; }); From 77139f5e6a4ee2836976ab049759b6908999b913 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 15 Apr 2025 15:19:18 -0700 Subject: [PATCH 061/149] Adapt topology monitors to monitor service --- .../java/software/amazon/jdbc/Driver.java | 1 - .../monitoring/ClusterTopologyMonitor.java | 3 +- .../ClusterTopologyMonitorImpl.java | 30 +++++---- .../MonitoringRdsHostListProvider.java | 63 ++++++++----------- .../MonitoringRdsMultiAzHostListProvider.java | 15 +++-- .../MultiAzClusterTopologyMonitorImpl.java | 12 ++-- .../jdbc/util/monitoring/MonitorService.java | 22 +++++++ .../util/monitoring/MonitorServiceImpl.java | 45 +++++++++++++ .../util/storage/ExternallyManagedCache.java | 21 ++++++- ..._advanced_jdbc_wrapper_messages.properties | 3 +- 10 files changed, 148 insertions(+), 67 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index 606353b8c..898291ab4 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -425,7 +425,6 @@ public static void releaseResources() { ConnectionProviderManager.releaseResources(); HikariPoolsHolder.closeAllPools(); HostResponseTimeServiceImpl.closeAllMonitors(); - MonitoringRdsHostListProvider.closeAllMonitors(); clearCaches(); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitor.java index 686dd5049..75ccb5ba9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitor.java @@ -22,8 +22,9 @@ import java.util.concurrent.TimeoutException; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.HostSpec; +import software.amazon.jdbc.util.monitoring.Monitor; -public interface ClusterTopologyMonitor extends AutoCloseable, Runnable { +public interface ClusterTopologyMonitor extends Monitor, Runnable { boolean canDispose(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 6c4bef0ef..ff5e8f3b4 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -51,13 +51,15 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SynchronousExecutor; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.monitoring.AbstractMonitor; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.Topology; -public class ClusterTopologyMonitorImpl implements ClusterTopologyMonitor { +public class ClusterTopologyMonitorImpl extends AbstractMonitor implements ClusterTopologyMonitor { private static final Logger LOGGER = Logger.getLogger(ClusterTopologyMonitorImpl.class.getName()); @@ -116,24 +118,23 @@ public class ClusterTopologyMonitorImpl implements ClusterTopologyMonitor { }); public ClusterTopologyMonitorImpl( + final ServiceContainer serviceContainer, final String clusterId, - final StorageService storageService, final HostSpec initialHostSpec, final Properties properties, - final PluginService pluginService, - final HostListProviderService hostListProviderService, final HostSpec clusterInstanceTemplate, final long refreshRateNano, final long highRefreshRateNano, final String topologyQuery, final String writerTopologyQuery, final String nodeIdQuery) { + super(serviceContainer.getMonitorService()); this.clusterId = clusterId; - this.storageService = storageService; + this.storageService = serviceContainer.getStorageService(); + this.pluginService = serviceContainer.getPluginService(); + this.hostListProviderService = serviceContainer.getHostListProviderService(); this.initialHostSpec = initialHostSpec; - this.pluginService = pluginService; - this.hostListProviderService = hostListProviderService; this.clusterInstanceTemplate = clusterInstanceTemplate; this.properties = properties; this.refreshRateNano = refreshRateNano; @@ -265,7 +266,7 @@ private List getStoredHosts() { } @Override - public void close() throws Exception { + public void stop() { this.stop.set(true); this.nodeThreadsStop.set(true); @@ -276,7 +277,13 @@ public void close() throws Exception { } // Waiting for 30s gives a thread enough time to exit monitoring loop and close database connection. - if (!this.monitorExecutor.awaitTermination(30, TimeUnit.SECONDS)) { + try { + if (!this.monitorExecutor.awaitTermination(30, TimeUnit.SECONDS)) { + LOGGER.fine(Messages.get("ClusterTopologyMonitorImpl.interrupted")); + this.monitorExecutor.shutdownNow(); + Thread.currentThread().interrupt(); + } + } catch (InterruptedException e) { this.monitorExecutor.shutdownNow(); } @@ -284,13 +291,14 @@ public void close() throws Exception { } @Override - public void run() { + public void monitor() { try { LOGGER.finest(() -> Messages.get( "ClusterTopologyMonitorImpl.startMonitoringThread", new Object[]{this.initialHostSpec.getHost()})); - while (!this.stop.get()) { + while (!this.stop.get() && !Thread.currentThread().isInterrupted()) { + this.lastActivityTimestampNanos = System.nanoTime(); if (this.isInPanicMode()) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index 271f57ce1..26346521d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -31,7 +31,7 @@ import software.amazon.jdbc.cleanup.CanReleaseResources; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider; import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.Topology; public class MonitoringRdsHostListProvider extends RdsHostListProvider @@ -48,22 +48,12 @@ public class MonitoringRdsHostListProvider extends RdsHostListProvider protected static final long CACHE_CLEANUP_NANO = TimeUnit.MINUTES.toNanos(1); protected static final long MONITOR_EXPIRATION_NANO = TimeUnit.MINUTES.toNanos(15); - protected static final SlidingExpirationCacheWithCleanupThread monitors = - new SlidingExpirationCacheWithCleanupThread<>( - ClusterTopologyMonitor::canDispose, - (monitor) -> { - try { - monitor.close(); - } catch (Exception ex) { - // ignore - } - }, - CACHE_CLEANUP_NANO); - static { PropertyDefinition.registerPluginProperties(MonitoringRdsHostListProvider.class); } + protected final ServiceContainer serviceContainer; + protected final MonitorService monitorService; protected final PluginService pluginService; protected final long highRefreshRateNano; protected final String writerTopologyQuery; @@ -77,6 +67,8 @@ public MonitoringRdsHostListProvider( final String isReaderQuery, final String writerTopologyQuery) { super(properties, originalUrl, serviceContainer, topologyQuery, nodeIdQuery, isReaderQuery); + this.serviceContainer = serviceContainer; + this.monitorService = serviceContainer.getMonitorService(); this.pluginService = serviceContainer.getPluginService(); this.writerTopologyQuery = writerTopologyQuery; this.highRefreshRateNano = TimeUnit.MILLISECONDS.toNanos( @@ -87,41 +79,35 @@ public static void clearCache() { clearAll(); } - public static void closeAllMonitors() { - monitors.getEntries().values().forEach(monitor -> { - try { - monitor.close(); - } catch (Exception ex) { - // ignore - } - }); - monitors.clear(); - clearCache(); - } - @Override protected void init() throws SQLException { super.init(); } protected ClusterTopologyMonitor initMonitor() { - return monitors.computeIfAbsent(this.clusterId, - (key) -> new ClusterTopologyMonitorImpl( - key, storageService, this.initialHostSpec, this.properties, this.pluginService, - this.hostListProviderService, this.clusterInstanceTemplate, - this.refreshRateNano, this.highRefreshRateNano, + return monitorService.runIfAbsent( + ClusterTopologyMonitorImpl.class, + this.clusterId, + () -> new ClusterTopologyMonitorImpl( + this.serviceContainer, + this.clusterId, + this.initialHostSpec, + this.properties, + this.clusterInstanceTemplate, + this.refreshRateNano, + this.highRefreshRateNano, this.topologyQuery, this.writerTopologyQuery, - this.nodeIdQuery), - MONITOR_EXPIRATION_NANO); + this.nodeIdQuery)); } @Override protected List queryForTopology(final Connection conn) throws SQLException { - ClusterTopologyMonitor monitor = monitors.get(this.clusterId, MONITOR_EXPIRATION_NANO); + ClusterTopologyMonitor monitor = monitorService.get(ClusterTopologyMonitorImpl.class, this.clusterId); if (monitor == null) { monitor = this.initMonitor(); } + try { return monitor.forceRefresh(conn, defaultTopologyQueryTimeoutMs); } catch (TimeoutException ex) { @@ -131,12 +117,13 @@ protected List queryForTopology(final Connection conn) throws SQLExcep @Override protected void clusterIdChanged(final String oldClusterId) { - final ClusterTopologyMonitor existingMonitor = monitors.get(oldClusterId, MONITOR_EXPIRATION_NANO); + final ClusterTopologyMonitorImpl existingMonitor = + monitorService.get(ClusterTopologyMonitorImpl.class, oldClusterId); if (existingMonitor != null) { - monitors.computeIfAbsent(this.clusterId, (key) -> existingMonitor, MONITOR_EXPIRATION_NANO); - assert monitors.get(this.clusterId, MONITOR_EXPIRATION_NANO) == existingMonitor; + monitorService.runIfAbsent(ClusterTopologyMonitorImpl.class, this.clusterId, () -> existingMonitor); + assert monitorService.get(ClusterTopologyMonitorImpl.class, this.clusterId) == existingMonitor; existingMonitor.setClusterId(this.clusterId); - monitors.remove(oldClusterId); + monitorService.remove(ClusterTopologyMonitorImpl.class, oldClusterId); } final Topology existingTopology = storageService.get(Topology.class, oldClusterId); @@ -150,7 +137,7 @@ protected void clusterIdChanged(final String oldClusterId) { public List forceRefresh(final boolean shouldVerifyWriter, final long timeoutMs) throws SQLException, TimeoutException { - ClusterTopologyMonitor monitor = monitors.get(this.clusterId, MONITOR_EXPIRATION_NANO); + ClusterTopologyMonitor monitor = monitorService.get(ClusterTopologyMonitorImpl.class, this.clusterId); if (monitor == null) { monitor = this.initMonitor(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java index 4f3f2295b..877dfd42d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java @@ -50,17 +50,20 @@ public MonitoringRdsMultiAzHostListProvider( @Override protected ClusterTopologyMonitor initMonitor() { - return monitors.computeIfAbsent(this.clusterId, - (key) -> new MultiAzClusterTopologyMonitorImpl( - key, storageService, this.initialHostSpec, this.properties, this.pluginService, - this.hostListProviderService, this.clusterInstanceTemplate, + return monitorService.runIfAbsent(MultiAzClusterTopologyMonitorImpl.class, + this.clusterId, + () -> new MultiAzClusterTopologyMonitorImpl( + this.serviceContainer, + this.clusterId, + this.initialHostSpec, + this.properties, + this.clusterInstanceTemplate, this.refreshRateNano, this.highRefreshRateNano, this.topologyQuery, this.writerTopologyQuery, this.nodeIdQuery, this.fetchWriterNodeQuery, - this.fetchWriterNodeColumnName), - MONITOR_EXPIRATION_NANO); + this.fetchWriterNodeColumnName)); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java index bb2924813..67210a405 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java @@ -24,11 +24,9 @@ import java.time.Instant; import java.util.Properties; import java.util.logging.Logger; -import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.StringUtils; -import software.amazon.jdbc.util.storage.StorageService; public class MultiAzClusterTopologyMonitorImpl extends ClusterTopologyMonitorImpl { @@ -38,12 +36,10 @@ public class MultiAzClusterTopologyMonitorImpl extends ClusterTopologyMonitorImp protected final String fetchWriterNodeColumnName; public MultiAzClusterTopologyMonitorImpl( + final ServiceContainer serviceContainer, final String clusterId, - final StorageService storageService, final HostSpec initialHostSpec, final Properties properties, - final PluginService pluginService, - final HostListProviderService hostListProviderService, final HostSpec clusterInstanceTemplate, final long refreshRateNano, final long highRefreshRateNano, @@ -52,8 +48,8 @@ public MultiAzClusterTopologyMonitorImpl( final String nodeIdQuery, final String fetchWriterNodeQuery, final String fetchWriterNodeColumnName) { - super(clusterId, storageService, initialHostSpec, properties, pluginService, hostListProviderService, - clusterInstanceTemplate, refreshRateNano, highRefreshRateNano, topologyQuery, writerTopologyQuery, nodeIdQuery); + super(serviceContainer, clusterId, initialHostSpec, properties, clusterInstanceTemplate, refreshRateNano, + highRefreshRateNano, topologyQuery, writerTopologyQuery, nodeIdQuery); this.fetchWriterNodeQuery = fetchWriterNodeQuery; this.fetchWriterNodeColumnName = fetchWriterNodeColumnName; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 2da8801dd..96f6f9d67 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -56,6 +56,16 @@ void registerMonitorTypeIfAbsent( */ T runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier); + /** + * Get the monitor stored at the given key. + * + * @param monitorClass the expected class of the monitor. + * @param key the key for the monitor. + * @return the monitor stored at the given key. + */ + @Nullable + T get(Class monitorClass, Object key); + /** * Processes a monitor error. The monitor service will respond to the error based on the monitor error responses * defined when the monitor type was registered. @@ -65,6 +75,18 @@ void registerMonitorTypeIfAbsent( */ void reportMonitorError(Monitor monitor, Exception exception); + /** + * Removes the monitor stored at the given key. If the expected monitor class does not match the actual monitor class + * no action will be performed. + * + * @param monitorClass the expected class of the monitor. + * @param key the key for the monitor. + * @return the monitor that was removed. Returns null if there was no monitor at the given key or the expected monitor + * class did not match the actual monitor class. + */ + @Nullable + T remove(Class monitorClass, Object key); + /** * Stops the given monitor and removes it from the monitor service. * diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 77a0063e1..feab1d630 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -32,6 +32,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.AllowedAndBlockedHosts; +import software.amazon.jdbc.hostlistprovider.monitoring.ClusterTopologyMonitorImpl; import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.StringUtils; @@ -40,6 +41,7 @@ import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.events.EventSubscriber; import software.amazon.jdbc.util.storage.ExternallyManagedCache; +import software.amazon.jdbc.util.storage.Topology; public class MonitorServiceImpl implements MonitorService, EventSubscriber { private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); @@ -66,6 +68,9 @@ public class MonitorServiceImpl implements MonitorService, EventSubscriber { suppliers.put( CustomEndpointMonitorImpl.class, () -> new CacheContainer(defaultSettings, AllowedAndBlockedHosts.class)); + MonitorSettings topologySettings = + new MonitorSettings(TimeUnit.MINUTES.toNanos(5), TimeUnit.MINUTES.toNanos(3), resetErrorResponse); + suppliers.put(ClusterTopologyMonitorImpl.class, () -> new CacheContainer(topologySettings, Topology.class)); defaultSuppliers = Collections.unmodifiableMap(suppliers); } @@ -209,6 +214,30 @@ public T runIfAbsent(Class monitorClass, Object key, Supp Messages.get("MonitorServiceImpl.unexpectedMonitorClass", new Object[] {monitorClass, monitor})); } + @Override + public @Nullable T get(Class monitorClass, Object key) { + CacheContainer cacheContainer = monitorCaches.get(monitorClass); + if (cacheContainer == null) { + return null; + } + + MonitorItem item = cacheContainer.getCache().get(key); + if (item == null) { + return null; + } + + Monitor monitor = item.getMonitor(); + if (monitorClass.isInstance(monitor)) { + return monitorClass.cast(monitor); + } + + LOGGER.fine( + Messages.get( + "MonitorServiceImpl.monitorClassMismatch", + new Object[]{key, monitorClass, monitor, monitor.getClass()})); + return null; + } + @Override public void reportMonitorError(Monitor monitor, Exception exception) { CacheContainer cacheContainer = monitorCaches.get(monitor.getClass()); @@ -230,6 +259,22 @@ public void reportMonitorError(Monitor monitor, Exception exception) { LOGGER.finest(Messages.get("MonitorServiceImpl.monitorErrorForMissingMonitor", new Object[] {monitor, exception})); } + @Override + public T remove(Class monitorClass, Object key) { + CacheContainer cacheContainer = monitorCaches.get(monitorClass); + if (cacheContainer == null) { + return null; + } + + MonitorItem item = cacheContainer.getCache().removeIf( + key, monitorItem -> monitorClass.isInstance(monitorItem.getMonitor())); + if (item == null) { + return null; + } + + return monitorClass.cast(item.getMonitor()); + } + @Override public void stopAndRemove(Class monitorClass, Object key) { CacheContainer cacheContainer = monitorCaches.get(monitorClass); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java index 31c548cfd..17a54fc8f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java @@ -67,9 +67,28 @@ public ExternallyManagedCache(long timeToLiveNanos) { return cacheItem.item; } + /** + * Get the value stored at the given key. If the value is expired, null will be returned. + * + * @param key the key for the value. + * @return the value stored at the given key, or null if the value is expired. + */ + public @Nullable V get(@NonNull K key) { + CacheItem cacheItem = this.cache.get(key); + if (cacheItem == null) { + return null; + } + + if (cacheItem.isExpired()) { + return null; + } + + return cacheItem.item; + } + /** * If a value does not exist for the given key, stores the value returned by the given mapping function, unless the - * function returns null, in which case the key will be removed. If the + * function returns null, in which case the key will be removed. * * @param key the key for the new or existing value. * @param mappingFunction the function to call to compute a new value. diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 30caae467..a5e256dad 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -292,7 +292,9 @@ MonitorImpl.stopMonitoringThread=Stop monitoring thread for {0}. MonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should not be empty. # monitoring.MonitorServiceImpl +MonitorServiceImpl.monitorClassMismatch=The monitor stored at ''{0}'' did not have the expected type. The expected type was ''{1}'', but the monitor ''{2}'' had a type of ''{3}''. . MonitorServiceImpl.monitorErrorForMissingMonitor=Monitor ''{0}'' reported an error to the monitor service, but the monitor service could not find the monitor under its registered monitors. Reported error: ''{1}''. +MonitorServiceImpl.monitorErrorForUnregisteredType=Monitor ''{0}'' reported an error to the monitor service, but the monitor type has not been registered. Reported error: ''{1}''. MonitorServiceImpl.monitorStuck=Monitor ''{0}'' has not been updated within the inactive timeout of {1} seconds. MonitorServiceImpl.monitorTypeNotRegistered=The given monitor class ''{0}'' is not registered. Please register the monitor class before running monitors of that class with the monitor service. MonitorServiceImpl.removedExpiredMonitor=Removed expired monitor: ''{0}''. @@ -300,7 +302,6 @@ MonitorServiceImpl.restartingMonitor=Restarting monitor: ''{0}''. MonitorServiceImpl.stopAndRemoveMissingMonitorType=The monitor service received a request to stop a monitor with type ''{0}'' and key ''{1}'', but the monitor service does not have any monitors registered under the given type. Please ensure monitors are registered under the correct type. MonitorServiceImpl.stopAndRemoveMonitorsMissingType=The monitor service received a request to stop all monitors with type ''{0}'', but the monitor service does not have any monitors registered under the given type. Please ensure monitors are registered under the correct type. MonitorServiceImpl.unexpectedMonitorClass=Monitor type mismatch - the monitor ''{0}'' was unexpectedly found under the ''{1}'' monitor class category. Please verify that monitors are submitted under their concrete class. -MonitorServiceImpl.monitorErrorForUnregisteredType=Monitor ''{0}'' reported an error to the monitor service, but the monitor type has not been registered. Reported error: ''{1}''. NodeMonitoringThread.detectedWriter=Writer detected by node monitoring thread: ''{0}''. NodeMonitoringThread.invalidWriterQuery=The writer topology query is invalid: {0} From d943913c3a2aeeebbc249f611a8c73d9efdb869a Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 16 Apr 2025 09:43:05 -0700 Subject: [PATCH 062/149] Undo changes to integration test files --- wrapper/build.gradle.kts | 4 ++-- wrapper/src/test/build.gradle.kts | 2 +- .../host/TestEnvironmentConfiguration.java | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/wrapper/build.gradle.kts b/wrapper/build.gradle.kts index 231ca9d20..533b9ff47 100644 --- a/wrapper/build.gradle.kts +++ b/wrapper/build.gradle.kts @@ -347,7 +347,7 @@ tasks.register("test-all-aurora") { systemProperty("test-no-performance", "true") systemProperty("test-no-mariadb-engine", "true") systemProperty("test-no-graalvm", "true") -// systemProperty("test-no-openjdk8", "true") + systemProperty("test-no-openjdk8", "true") systemProperty("test-no-multi-az", "true") } } @@ -360,7 +360,7 @@ tasks.register("test-all-multi-az") { systemProperty("test-no-performance", "true") systemProperty("test-no-mariadb-engine", "true") systemProperty("test-no-graalvm", "true") -// systemProperty("test-no-openjdk8", "true") + systemProperty("test-no-openjdk8", "true") systemProperty("test-no-aurora", "true") } } diff --git a/wrapper/src/test/build.gradle.kts b/wrapper/src/test/build.gradle.kts index 36ca4fda0..89e63ddbf 100644 --- a/wrapper/src/test/build.gradle.kts +++ b/wrapper/src/test/build.gradle.kts @@ -100,5 +100,5 @@ tasks.register("in-container") { // modify below filter to select specific integration tests // see https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/testing/TestFilter.html - filter.includeTestsMatching("*.test_writerFailover_writerReelected") + filter.includeTestsMatching("integration.container.tests.*") } diff --git a/wrapper/src/test/java/integration/host/TestEnvironmentConfiguration.java b/wrapper/src/test/java/integration/host/TestEnvironmentConfiguration.java index b72106c5d..b20b17523 100644 --- a/wrapper/src/test/java/integration/host/TestEnvironmentConfiguration.java +++ b/wrapper/src/test/java/integration/host/TestEnvironmentConfiguration.java @@ -27,17 +27,17 @@ public class TestEnvironmentConfiguration { public boolean noPerformance = Boolean.parseBoolean(System.getProperty("test-no-performance", "false")); public boolean noMysqlEngine = - Boolean.parseBoolean(System.getProperty("test-no-mysql-engine", "true")); + Boolean.parseBoolean(System.getProperty("test-no-mysql-engine", "false")); public boolean noMysqlDriver = - Boolean.parseBoolean(System.getProperty("test-no-mysql-driver", "true")); + Boolean.parseBoolean(System.getProperty("test-no-mysql-driver", "false")); public boolean noPgEngine = Boolean.parseBoolean(System.getProperty("test-no-pg-engine", "false")); public boolean noPgDriver = Boolean.parseBoolean(System.getProperty("test-no-pg-driver", "false")); public boolean noMariadbEngine = - Boolean.parseBoolean(System.getProperty("test-no-mariadb-engine", "true")); + Boolean.parseBoolean(System.getProperty("test-no-mariadb-engine", "false")); public boolean noMariadbDriver = - Boolean.parseBoolean(System.getProperty("test-no-mariadb-driver", "true")); + Boolean.parseBoolean(System.getProperty("test-no-mariadb-driver", "false")); public boolean noFailover = Boolean.parseBoolean(System.getProperty("test-no-failover", "false")); public boolean noIam = @@ -47,24 +47,24 @@ public class TestEnvironmentConfiguration { public boolean noHikari = Boolean.parseBoolean(System.getProperty("test-no-hikari", "false")); public boolean noGraalVm = - Boolean.parseBoolean(System.getProperty("test-no-graalvm", "true")); + Boolean.parseBoolean(System.getProperty("test-no-graalvm", "false")); public boolean noOpenJdk = Boolean.parseBoolean(System.getProperty("test-no-openjdk", "false")); public boolean noOpenJdk8 = Boolean.parseBoolean(System.getProperty("test-no-openjdk8", "false")); public boolean noOpenJdk11 = - Boolean.parseBoolean(System.getProperty("test-no-openjdk11", "true")); + Boolean.parseBoolean(System.getProperty("test-no-openjdk11", "false")); public boolean testHibernateOnly = Boolean.parseBoolean(System.getProperty("test-hibernate-only", "false")); public boolean testAutoscalingOnly = Boolean.parseBoolean(System.getProperty("test-autoscaling-only", "false")); public boolean noInstances1 = - Boolean.parseBoolean(System.getProperty("test-no-instances-1", "true")); + Boolean.parseBoolean(System.getProperty("test-no-instances-1", "false")); public boolean noInstances2 = - Boolean.parseBoolean(System.getProperty("test-no-instances-2", "true")); + Boolean.parseBoolean(System.getProperty("test-no-instances-2", "false")); public boolean noInstances3 = - Boolean.parseBoolean(System.getProperty("test-no-instances-3", "true")); + Boolean.parseBoolean(System.getProperty("test-no-instances-3", "false")); public boolean noInstances5 = Boolean.parseBoolean(System.getProperty("test-no-instances-5", "false")); From 7edb8229062e77be562f619cbc0b4a4c5e6a8189 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 16 Apr 2025 10:00:44 -0700 Subject: [PATCH 063/149] Fix checkstyle error --- .../amazon/jdbc/util/events/PeriodicEventPublisher.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java index cff2ff7d9..335af6ca9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java @@ -27,15 +27,14 @@ /** * An event publisher that periodically sends out all messages received during the latest interval of time. Messages are * recorded in a set so that messages of equivalent value are not duplicated in the same message batch. - * - * TODO: should duplicate events be allowed? Data access events may happen frequently so duplicates could result in - * unnecessary repeated processing. */ public class PeriodicEventPublisher implements EventPublisher { protected static final long DEFAULT_MESSAGE_INTERVAL_NANOS = TimeUnit.SECONDS.toNanos(30); protected final Map, Set> subscribers = new ConcurrentHashMap<>(); // ConcurrentHashMap.newKeySet() is the recommended way to get a concurrent set. A set is used to prevent duplicate // event messages from being sent in the same message batch. + // TODO: should duplicate events be allowed? Data access events may happen frequently so duplicates could result in + // a lot of unnecessary repeated processing. protected final Set eventMessages = ConcurrentHashMap.newKeySet(); protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { final Thread thread = new Thread(r); From cbe984d51f8c4f8ba0c8d47c8b20ce1f92df483c Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 17 Apr 2025 17:14:53 -0700 Subject: [PATCH 064/149] ConnectionService wip --- .../jdbc/benchmarks/PluginBenchmarks.java | 35 +++++++++---- .../testplugin/TestConnectionWrapper.java | 9 ++-- .../java/software/amazon/jdbc/Driver.java | 8 +++ .../amazon/jdbc/ds/AwsWrapperDataSource.java | 6 +++ .../amazon/jdbc/util/ServiceContainer.java | 5 ++ .../jdbc/util/ServiceContainerImpl.java | 23 ++++++++- .../util/connection/ConnectionService.java | 34 +++++++++++++ .../connection/ConnectionServiceImpl.java | 50 +++++++++++++++++++ .../jdbc/wrapper/ConnectionWrapper.java | 7 ++- 9 files changed, 160 insertions(+), 17 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java index 6e25fc9a5..09147875a 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java @@ -62,6 +62,7 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.GaugeCallable; @@ -92,6 +93,7 @@ public class PluginBenchmarks { @Mock private StorageService mockStorageService; @Mock private MonitorService mockMonitorService; + @Mock private ConnectionService mockConnectionService; @Mock private PluginService mockPluginService; @Mock private Dialect mockDialect; @Mock private ConnectionPluginManager mockConnectionPluginManager; @@ -170,7 +172,8 @@ public ConnectionWrapper initAndReleaseWithExecutionTimePlugin() throws SQLExcep mockHostListProviderService, mockPluginManagerService, mockStorageService, - mockMonitorService)) { + mockMonitorService, + mockConnectionService)) { wrapper.releaseResources(); return wrapper; } @@ -187,7 +190,8 @@ public ConnectionWrapper initAndReleaseWithAuroraHostListPlugin() throws SQLExce mockHostListProviderService, mockPluginManagerService, mockStorageService, - mockMonitorService)) { + mockMonitorService, + mockConnectionService)) { wrapper.releaseResources(); return wrapper; } @@ -204,7 +208,8 @@ public ConnectionWrapper initAndReleaseWithExecutionTimeAndAuroraHostListPlugins mockHostListProviderService, mockPluginManagerService, mockStorageService, - mockMonitorService)) { + mockMonitorService, + mockConnectionService)) { wrapper.releaseResources(); return wrapper; } @@ -221,7 +226,8 @@ public ConnectionWrapper initAndReleaseWithReadWriteSplittingPlugin() throws SQL mockHostListProviderService, mockPluginManagerService, mockStorageService, - mockMonitorService)) { + mockMonitorService, + mockConnectionService)) { wrapper.releaseResources(); return wrapper; } @@ -239,7 +245,8 @@ public ConnectionWrapper initAndReleaseWithAuroraHostListAndReadWriteSplittingPl mockHostListProviderService, mockPluginManagerService, mockStorageService, - mockMonitorService)) { + mockMonitorService, + mockConnectionService)) { wrapper.releaseResources(); return wrapper; } @@ -259,7 +266,8 @@ public ConnectionWrapper initAndReleaseWithReadWriteSplittingPlugin_internalConn mockHostListProviderService, mockPluginManagerService, mockStorageService, - mockMonitorService)) { + mockMonitorService, + mockConnectionService)) { wrapper.releaseResources(); ConnectionProviderManager.releaseResources(); Driver.resetCustomConnectionProvider(); @@ -282,7 +290,8 @@ public ConnectionWrapper initAndReleaseWithAuroraHostListAndReadWriteSplittingPl mockHostListProviderService, mockPluginManagerService, mockStorageService, - mockMonitorService)) { + mockMonitorService, + mockConnectionService)) { wrapper.releaseResources(); ConnectionProviderManager.releaseResources(); Driver.resetCustomConnectionProvider(); @@ -301,7 +310,8 @@ public Statement executeStatementBaseline() throws SQLException { mockHostListProviderService, mockPluginManagerService, mockStorageService, - mockMonitorService); + mockMonitorService, + mockConnectionService); Statement statement = wrapper.createStatement()) { return statement; } @@ -319,7 +329,8 @@ public ResultSet executeStatementWithExecutionTimePlugin() throws SQLException { mockHostListProviderService, mockPluginManagerService, mockStorageService, - mockMonitorService); + mockMonitorService, + mockConnectionService); Statement statement = wrapper.createStatement(); ResultSet resultSet = statement.executeQuery("some sql")) { return resultSet; @@ -338,7 +349,8 @@ public ResultSet executeStatementWithTelemetryDisabled() throws SQLException { mockHostListProviderService, mockPluginManagerService, mockStorageService, - mockMonitorService); + mockMonitorService, + mockConnectionService); Statement statement = wrapper.createStatement(); ResultSet resultSet = statement.executeQuery("some sql")) { return resultSet; @@ -357,7 +369,8 @@ public ResultSet executeStatementWithTelemetry() throws SQLException { mockHostListProviderService, mockPluginManagerService, mockStorageService, - mockMonitorService); + mockMonitorService, + mockConnectionService); Statement statement = wrapper.createStatement(); ResultSet resultSet = statement.executeQuery("some sql")) { return resultSet; diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java index 6bcedf942..218a45a69 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java @@ -23,6 +23,7 @@ import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -31,7 +32,8 @@ // Test class allowing for mocks to be used with ConnectionWrapper logic public class TestConnectionWrapper extends ConnectionWrapper { - public TestConnectionWrapper(@NonNull Properties props, + public TestConnectionWrapper( + @NonNull Properties props, @NonNull String url, @NonNull ConnectionPluginManager connectionPluginManager, @NonNull final TelemetryFactory telemetryFactory, @@ -39,9 +41,10 @@ public TestConnectionWrapper(@NonNull Properties props, @NonNull HostListProviderService hostListProviderService, @NonNull PluginManagerService pluginManagerService, @NonNull StorageService storageService, - @NonNull MonitorService monitorService) + @NonNull MonitorService monitorService, + @NonNull ConnectionService connectionService) throws SQLException { super(props, url, connectionPluginManager, telemetryFactory, pluginService, hostListProviderService, - pluginManagerService, storageService, monitorService); + pluginManagerService, storageService, monitorService, connectionService); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index 898291ab4..dc07eb9ab 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -65,6 +65,8 @@ import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.ServiceContainerImpl; import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.connection.ConnectionService; +import software.amazon.jdbc.util.connection.ConnectionServiceImpl; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.events.PeriodicEventPublisher; import software.amazon.jdbc.util.monitoring.MonitorService; @@ -228,6 +230,12 @@ public Connection connect(final String url, final Properties info) throws SQLExc } ServiceContainer serviceContainer = new ServiceContainerImpl(storageService, monitorService, telemetryFactory); + ConnectionService connectionService = new ConnectionServiceImpl( + serviceContainer, + defaultConnectionProvider, + targetDriverDialect); + serviceContainer.setConnectionService(connectionService); + return new ConnectionWrapper( serviceContainer, props, diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index b14681281..98c95acc6 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -54,6 +54,8 @@ import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; +import software.amazon.jdbc.util.connection.ConnectionService; +import software.amazon.jdbc.util.connection.ConnectionServiceImpl; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.events.PeriodicEventPublisher; import software.amazon.jdbc.util.monitoring.MonitorService; @@ -265,6 +267,10 @@ ConnectionWrapper createConnectionWrapper( final @Nullable ConfigurationProfile configurationProfile, final TelemetryFactory telemetryFactory) throws SQLException { ServiceContainer serviceContainer = new ServiceContainerImpl(storageService, monitorService, telemetryFactory); + ConnectionService connectionService = + new ConnectionServiceImpl(serviceContainer, defaultProvider, targetDriverDialect); + serviceContainer.setConnectionService(connectionService); + return new ConnectionWrapper( serviceContainer, props, diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java index bab4eaf59..a6ab308e8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java @@ -20,6 +20,7 @@ import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -31,6 +32,8 @@ public interface ServiceContainer { TelemetryFactory getTelemetryFactory(); + ConnectionService getConnectionService(); + ConnectionPluginManager getConnectionPluginManager(); HostListProviderService getHostListProviderService(); @@ -45,6 +48,8 @@ public interface ServiceContainer { void setTelemetryFactory(TelemetryFactory telemetryFactory); + void setConnectionService(ConnectionService connectionService); + void setConnectionPluginManager(ConnectionPluginManager connectionPluginManager); void setHostListProviderService(HostListProviderService hostListProviderService); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java index d05fdfffa..dd5431b06 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java @@ -20,6 +20,7 @@ import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -28,6 +29,7 @@ public class ServiceContainerImpl implements ServiceContainer { private StorageService storageService; private MonitorService monitorService; private TelemetryFactory telemetryFactory; + private ConnectionService connectionService; private ConnectionPluginManager connectionPluginManager; private HostListProviderService hostListProviderService; private PluginService pluginService; @@ -36,12 +38,14 @@ public class ServiceContainerImpl implements ServiceContainer { public ServiceContainerImpl( StorageService storageService, MonitorService monitorService, - ConnectionPluginManager connectionPluginManager, TelemetryFactory telemetryFactory, + ConnectionService connectionService, + ConnectionPluginManager connectionPluginManager, HostListProviderService hostListProviderService, PluginService pluginService, PluginManagerService pluginManagerService) { this(storageService, monitorService, telemetryFactory); + this.connectionService = connectionService; this.connectionPluginManager = connectionPluginManager; this.hostListProviderService = hostListProviderService; this.pluginService = pluginService; @@ -57,30 +61,42 @@ public ServiceContainerImpl( this.telemetryFactory = telemetryFactory; } + @Override public StorageService getStorageService() { return this.storageService; } + @Override public MonitorService getMonitorService() { return this.monitorService; } + @Override public TelemetryFactory getTelemetryFactory() { return this.telemetryFactory; } + @Override + public ConnectionService getConnectionService() { + return connectionService; + } + + @Override public ConnectionPluginManager getConnectionPluginManager() { return this.connectionPluginManager; } + @Override public HostListProviderService getHostListProviderService() { return this.hostListProviderService; } + @Override public PluginService getPluginService() { return this.pluginService; } + @Override public PluginManagerService getPluginManagerService() { return this.pluginManagerService; } @@ -100,6 +116,11 @@ public void setTelemetryFactory(TelemetryFactory telemetryFactory) { this.telemetryFactory = telemetryFactory; } + @Override + public void setConnectionService(ConnectionService connectionService) { + this.connectionService = connectionService; + } + @Override public void setConnectionPluginManager(ConnectionPluginManager connectionPluginManager) { this.connectionPluginManager = connectionPluginManager; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java new file mode 100644 index 000000000..6a26434df --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java @@ -0,0 +1,34 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.connection; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Properties; + +public interface ConnectionService { + /** + * Creates an auxiliary connection. Auxiliary connections are driver-internal connections that accomplish various + * specific tasks such as monitoring a host's availability, checking the topology information for a cluster, etc. + * + * @param underlyingDriverConnString the connection string for the underlying driver and database, eg + * "jdbc:postgresql://mydb.cluster-xyz.us-east-1.rds.amazonaws.com/some_db". + * @param props the properties for the auxiliary connection. + * @return a new connection to the given connection string using the given props. + */ + Connection createAuxiliaryConnection(String underlyingDriverConnString, Properties props) throws SQLException; +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java new file mode 100644 index 000000000..db68d9acc --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -0,0 +1,50 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.connection; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Properties; +import software.amazon.jdbc.ConnectionProvider; +import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.wrapper.ConnectionWrapper; + +public class ConnectionServiceImpl implements ConnectionService { + protected ServiceContainer serviceContainer; + protected ConnectionProvider connectionProvider; + protected TargetDriverDialect driverDialect; + + public ConnectionServiceImpl( + ServiceContainer serviceContainer, ConnectionProvider connectionProvider, TargetDriverDialect driverDialect) { + this.serviceContainer = serviceContainer; + this.connectionProvider = connectionProvider; + this.driverDialect = driverDialect; + } + + @Override + public Connection createAuxiliaryConnection(String underlyingDriverConnString, Properties props) throws SQLException { + return new ConnectionWrapper( + this.serviceContainer, + props, + underlyingDriverConnString, + this.connectionProvider, + null, + this.driverDialect, null + ); + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index d32085041..92629d85c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -57,6 +57,7 @@ import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; +import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -132,7 +133,8 @@ protected ConnectionWrapper( @NonNull final HostListProviderService hostListProviderService, @NonNull final PluginManagerService pluginManagerService, @NonNull final StorageService storageService, - @NonNull final MonitorService monitorService) + @NonNull final MonitorService monitorService, + @NonNull final ConnectionService connectionService) throws SQLException { if (StringUtils.isNullOrEmpty(url)) { @@ -142,8 +144,9 @@ protected ConnectionWrapper( ServiceContainer serviceContainer = new ServiceContainerImpl( storageService, monitorService, - connectionPluginManager, telemetryFactory, + connectionService, + connectionPluginManager, hostListProviderService, pluginService, pluginManagerService From 3b3d413c3cb0e5dd2dcf61dac9fede1447a9f2ae Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 21 Apr 2025 09:03:31 -0700 Subject: [PATCH 065/149] wip --- .../main/java/software/amazon/jdbc/ConnectionPlugin.java | 4 ++++ .../java/software/amazon/jdbc/ConnectionPluginManager.java | 4 ++++ .../monitoring/ClusterTopologyMonitorImpl.java | 6 ++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java index d2d72b05c..b17ad042b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java @@ -23,6 +23,7 @@ import java.util.Map; import java.util.Properties; import java.util.Set; +import software.amazon.jdbc.util.connection.ConnectionService; /** * Interface for connection plugins. This class implements ways to execute a JDBC method and to clean up resources used @@ -93,7 +94,10 @@ Connection connect( * @return a {@link Connection} to the requested host * @throws SQLException if there was an error establishing a {@link Connection} to the requested * host + * + * @deprecated Use {@link ConnectionService#createAuxiliaryConnection(String, Properties)} instead. */ + @Deprecated Connection forceConnect( final String driverProtocol, final HostSpec hostSpec, diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java index dbef6942c..52bc9b8a9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java @@ -53,6 +53,7 @@ import software.amazon.jdbc.util.SqlMethodAnalyzer; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.WrapperUtils; +import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryTraceLevel; @@ -420,7 +421,10 @@ public Connection connect( * @return a {@link Connection} to the requested host * @throws SQLException if there was an error establishing a {@link Connection} to the requested * host + * + * @deprecated Use {@link ConnectionService#createAuxiliaryConnection(String, Properties)} instead. */ + @Deprecated public Connection forceConnect( final String driverProtocol, final HostSpec hostSpec, diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 3d024b3b8..7c22b8d98 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -56,6 +56,7 @@ import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SynchronousExecutor; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.AbstractMonitor; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.Topology; @@ -68,7 +69,6 @@ public class ClusterTopologyMonitorImpl extends AbstractMonitor implements Clust protected static final Executor networkTimeoutExecutor = new SynchronousExecutor(); protected static final RdsUtils rdsHelper = new RdsUtils(); - protected static final int defaultTopologyQueryTimeoutMs = 1000; protected static final int closeConnectionNetworkTimeoutMs = 500; @@ -86,6 +86,7 @@ public class ClusterTopologyMonitorImpl extends AbstractMonitor implements Clust protected final PluginService pluginService; protected final HostSpec initialHostSpec; protected final StorageService storageService; + protected final ConnectionService connectionService; protected final String topologyQuery; protected final String nodeIdQuery; protected final String writerTopologyQuery; @@ -135,6 +136,7 @@ public ClusterTopologyMonitorImpl( this.clusterId = clusterId; this.storageService = serviceContainer.getStorageService(); + this.connectionService = serviceContainer.getConnectionService(); this.pluginService = serviceContainer.getPluginService(); this.hostListProviderService = serviceContainer.getHostListProviderService(); this.initialHostSpec = initialHostSpec; @@ -513,7 +515,7 @@ protected List openAnyConnectionAndUpdateTopology() { // open a new connection try { - conn = this.pluginService.forceConnect(this.initialHostSpec, this.monitoringProperties); + conn = this.connectionService.createAuxiliaryConnection(this.initialHostSpec, this.monitoringProperties); } catch (SQLException ex) { // can't connect return null; From f02112e941fcf1435c7bb2c3275efbf1f764542b Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 21 Apr 2025 15:29:02 -0700 Subject: [PATCH 066/149] ConnectionService wip - tests passing --- .../amazon/jdbc/ConnectionPlugin.java | 2 +- .../amazon/jdbc/ConnectionPluginManager.java | 2 +- .../java/software/amazon/jdbc/Driver.java | 7 -- .../amazon/jdbc/ds/AwsWrapperDataSource.java | 6 -- .../ClusterTopologyMonitorImpl.java | 2 +- .../efm/HostMonitoringConnectionPlugin.java | 21 ++--- ...HostMonitoringConnectionPluginFactory.java | 2 +- .../amazon/jdbc/plugin/efm/MonitorImpl.java | 14 ++-- .../jdbc/plugin/efm/MonitorServiceImpl.java | 14 ++-- .../efm2/HostMonitoringConnectionPlugin.java | 21 ++--- ...HostMonitoringConnectionPluginFactory.java | 2 +- .../amazon/jdbc/plugin/efm2/MonitorImpl.java | 24 +++--- .../jdbc/plugin/efm2/MonitorServiceImpl.java | 13 +-- .../ClusterAwareReaderFailoverHandler.java | 34 ++++---- .../ClusterAwareWriterFailoverHandler.java | 16 ++-- .../failover/FailoverConnectionPlugin.java | 17 ++-- .../FailoverConnectionPluginFactory.java | 2 +- .../limitless/LimitlessConnectionPlugin.java | 3 +- .../limitless/LimitlessRouterMonitor.java | 16 ++-- .../limitless/LimitlessRouterServiceImpl.java | 9 ++- .../FastestResponseStrategyPlugin.java | 11 +-- .../FastestResponseStrategyPluginFactory.java | 2 +- .../HostResponseTimeServiceImpl.java | 26 +++--- .../NodeResponseTimeMonitor.java | 12 ++- .../util/connection/ConnectionService.java | 8 +- .../connection/ConnectionServiceImpl.java | 17 +++- .../jdbc/wrapper/ConnectionWrapper.java | 8 ++ .../ConnectionPluginChainBuilderTests.java | 5 ++ .../jdbc/ConnectionPluginManagerTests.java | 6 +- .../jdbc/plugin/efm/ConcurrencyTests.java | 4 +- .../HostMonitoringConnectionPluginTest.java | 16 ++-- .../jdbc/plugin/efm/MonitorImplTest.java | 15 +++- .../plugin/efm/MonitorServiceImplTest.java | 7 +- ...ultiThreadedDefaultMonitorServiceTest.java | 7 +- ...ClusterAwareReaderFailoverHandlerTest.java | 39 +++++---- ...ClusterAwareWriterFailoverHandlerTest.java | 80 ++++++++++++------- .../FailoverConnectionPluginTest.java | 6 +- 37 files changed, 271 insertions(+), 225 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java index b17ad042b..9506eb7b1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java @@ -95,7 +95,7 @@ Connection connect( * @throws SQLException if there was an error establishing a {@link Connection} to the requested * host * - * @deprecated Use {@link ConnectionService#createAuxiliaryConnection(String, Properties)} instead. + * @deprecated Use {@link ConnectionService#createAuxiliaryConnection(HostSpec, Properties)} instead. */ @Deprecated Connection forceConnect( diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java index 52bc9b8a9..f7fe36c2b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java @@ -422,7 +422,7 @@ public Connection connect( * @throws SQLException if there was an error establishing a {@link Connection} to the requested * host * - * @deprecated Use {@link ConnectionService#createAuxiliaryConnection(String, Properties)} instead. + * @deprecated Use {@link ConnectionService#createAuxiliaryConnection(HostSpec, Properties)} instead. */ @Deprecated public Connection forceConnect( diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index dc07eb9ab..29f6e7fd1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -65,8 +65,6 @@ import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.ServiceContainerImpl; import software.amazon.jdbc.util.StringUtils; -import software.amazon.jdbc.util.connection.ConnectionService; -import software.amazon.jdbc.util.connection.ConnectionServiceImpl; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.events.PeriodicEventPublisher; import software.amazon.jdbc.util.monitoring.MonitorService; @@ -230,11 +228,6 @@ public Connection connect(final String url, final Properties info) throws SQLExc } ServiceContainer serviceContainer = new ServiceContainerImpl(storageService, monitorService, telemetryFactory); - ConnectionService connectionService = new ConnectionServiceImpl( - serviceContainer, - defaultConnectionProvider, - targetDriverDialect); - serviceContainer.setConnectionService(connectionService); return new ConnectionWrapper( serviceContainer, diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index 98c95acc6..b14681281 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -54,8 +54,6 @@ import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; -import software.amazon.jdbc.util.connection.ConnectionService; -import software.amazon.jdbc.util.connection.ConnectionServiceImpl; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.events.PeriodicEventPublisher; import software.amazon.jdbc.util.monitoring.MonitorService; @@ -267,10 +265,6 @@ ConnectionWrapper createConnectionWrapper( final @Nullable ConfigurationProfile configurationProfile, final TelemetryFactory telemetryFactory) throws SQLException { ServiceContainer serviceContainer = new ServiceContainerImpl(storageService, monitorService, telemetryFactory); - ConnectionService connectionService = - new ConnectionServiceImpl(serviceContainer, defaultProvider, targetDriverDialect); - serviceContainer.setConnectionService(connectionService); - return new ConnectionWrapper( serviceContainer, props, diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 7c22b8d98..08429b9c0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -847,7 +847,7 @@ public void run() { if (connection == null) { try { - connection = this.monitor.pluginService.forceConnect( + connection = this.monitor.connectionService.createAuxiliaryConnection( hostSpec, this.monitor.monitoringProperties); this.monitor.pluginService.setAvailability( hostSpec.asAliases(), HostAvailability.AVAILABLE); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPlugin.java index fdb01a7a4..fffcae2a2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPlugin.java @@ -39,6 +39,7 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SubscribedMethodHelper; /** @@ -93,30 +94,20 @@ public class HostMonitoringConnectionPlugin extends AbstractConnectionPlugin /** * Initialize the node monitoring plugin. * - * @param pluginService A service allowing the plugin to retrieve the current active connection - * and its connection settings. + * @param serviceContainer The service container for the services required by this class. * @param properties The property set used to initialize the active connection. */ public HostMonitoringConnectionPlugin( - final @NonNull PluginService pluginService, final @NonNull Properties properties) { - this(pluginService, properties, () -> new MonitorServiceImpl(pluginService), new RdsUtils()); + final @NonNull ServiceContainer serviceContainer, final @NonNull Properties properties) { + this(serviceContainer, properties, () -> new MonitorServiceImpl(serviceContainer), new RdsUtils()); } HostMonitoringConnectionPlugin( - final @NonNull PluginService pluginService, + final @NonNull ServiceContainer serviceContainer, final @NonNull Properties properties, final @NonNull Supplier monitorServiceSupplier, final RdsUtils rdsHelper) { - if (pluginService == null) { - throw new IllegalArgumentException("pluginService"); - } - if (properties == null) { - throw new IllegalArgumentException("properties"); - } - if (monitorServiceSupplier == null) { - throw new IllegalArgumentException("monitorServiceSupplier"); - } - this.pluginService = pluginService; + this.pluginService = serviceContainer.getPluginService(); this.properties = properties; this.monitorServiceSupplier = monitorServiceSupplier; this.rdsHelper = rdsHelper; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginFactory.java index d4c8d18ae..64ef58025 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginFactory.java @@ -25,6 +25,6 @@ public class HostMonitoringConnectionPluginFactory implements ConnectionPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - return new HostMonitoringConnectionPlugin(pluginService, props); + return new HostMonitoringConnectionPlugin(pluginService.getServiceContainer(), props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java index 5bd55516d..56d379b4e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java @@ -29,7 +29,9 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -60,6 +62,7 @@ static class ConnectionStatus { final Queue activeContexts = new ConcurrentLinkedQueue<>(); private final Queue newContexts = new ConcurrentLinkedQueue<>(); + private final ConnectionService connectionService; private final PluginService pluginService; private final TelemetryFactory telemetryFactory; private final Properties properties; @@ -77,7 +80,7 @@ static class ConnectionStatus { /** * Store the monitoring configuration for a connection. * - * @param pluginService A service for creating new connections. + * @param serviceContainer The service container for the services required by this class. * @param hostSpec The {@link HostSpec} of the server this {@link MonitorImpl} * instance is monitoring. * @param properties The {@link Properties} containing additional monitoring @@ -89,13 +92,14 @@ static class ConnectionStatus { * that initialized this class. */ public MonitorImpl( - final @NonNull PluginService pluginService, + final @NonNull ServiceContainer serviceContainer, @NonNull final HostSpec hostSpec, @NonNull final Properties properties, final long monitorDisposalTimeMillis, @NonNull final MonitorThreadContainer threadContainer) { - this.pluginService = pluginService; - this.telemetryFactory = pluginService.getTelemetryFactory(); + this.pluginService = serviceContainer.getPluginService(); + this.connectionService = serviceContainer.getConnectionService(); + this.telemetryFactory = serviceContainer.getTelemetryFactory(); this.hostSpec = hostSpec; this.properties = properties; this.monitorDisposalTimeMillis = monitorDisposalTimeMillis; @@ -332,7 +336,7 @@ ConnectionStatus checkConnectionStatus(final long shortestFailureDetectionInterv LOGGER.finest(() -> "Opening a monitoring connection to " + this.hostSpec.getUrl()); startNano = this.getCurrentTimeNano(); - this.monitoringConn = this.pluginService.forceConnect(this.hostSpec, monitoringConnProperties); + this.monitoringConn = this.connectionService.createAuxiliaryConnection(this.hostSpec, monitoringConnProperties); LOGGER.finest(() -> "Opened monitoring connection: " + this.monitoringConn); return new ConnectionStatus(true, this.getCurrentTimeNano() - startNano); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorServiceImpl.java index 2763f8a34..ae20645e9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorServiceImpl.java @@ -26,8 +26,8 @@ import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.AwsWrapperProperty; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -45,7 +45,6 @@ public class MonitorServiceImpl implements MonitorService { "600000", // 10min "Interval in milliseconds for a monitor to be considered inactive and to be disposed."); - private final PluginService pluginService; private MonitorThreadContainer threadContainer; final MonitorInitializer monitorInitializer; @@ -54,12 +53,12 @@ public class MonitorServiceImpl implements MonitorService { final TelemetryFactory telemetryFactory; final TelemetryCounter abortedConnectionsCounter; - public MonitorServiceImpl(final @NonNull PluginService pluginService) { + public MonitorServiceImpl(final @NonNull ServiceContainer serviceContainer) { this( - pluginService, + serviceContainer, (hostSpec, properties, monitorService) -> new MonitorImpl( - pluginService, + serviceContainer, hostSpec, properties, MONITOR_DISPOSAL_TIME_MS.getLong(properties), @@ -74,11 +73,10 @@ public MonitorServiceImpl(final @NonNull PluginService pluginService) { } MonitorServiceImpl( - final PluginService pluginService, + final ServiceContainer serviceContainer, final MonitorInitializer monitorInitializer, final ExecutorServiceInitializer executorServiceInitializer) { - this.pluginService = pluginService; - this.telemetryFactory = pluginService.getTelemetryFactory(); + this.telemetryFactory = serviceContainer.getTelemetryFactory(); this.abortedConnectionsCounter = telemetryFactory.createCounter("efm.connections.aborted"); this.monitorInitializer = monitorInitializer; this.threadContainer = MonitorThreadContainer.getInstance(executorServiceInitializer); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPlugin.java index c234bbd9a..f864af252 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPlugin.java @@ -38,6 +38,7 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SubscribedMethodHelper; /** @@ -92,30 +93,20 @@ public class HostMonitoringConnectionPlugin extends AbstractConnectionPlugin /** * Initialize the node monitoring plugin. * - * @param pluginService A service allowing the plugin to retrieve the current active connection - * and its connection settings. + * @param serviceContainer The service container for the services required by this class. * @param properties The property set used to initialize the active connection. */ public HostMonitoringConnectionPlugin( - final @NonNull PluginService pluginService, final @NonNull Properties properties) { - this(pluginService, properties, () -> new MonitorServiceImpl(pluginService), new RdsUtils()); + final @NonNull ServiceContainer serviceContainer, final @NonNull Properties properties) { + this(serviceContainer, properties, () -> new MonitorServiceImpl(serviceContainer), new RdsUtils()); } HostMonitoringConnectionPlugin( - final @NonNull PluginService pluginService, + final @NonNull ServiceContainer serviceContainer, final @NonNull Properties properties, final @NonNull Supplier monitorServiceSupplier, final RdsUtils rdsHelper) { - if (pluginService == null) { - throw new IllegalArgumentException("pluginService"); - } - if (properties == null) { - throw new IllegalArgumentException("properties"); - } - if (monitorServiceSupplier == null) { - throw new IllegalArgumentException("monitorServiceSupplier"); - } - this.pluginService = pluginService; + this.pluginService = serviceContainer.getPluginService(); this.properties = properties; this.monitorServiceSupplier = monitorServiceSupplier; this.rdsHelper = rdsHelper; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPluginFactory.java index 0dfdb79ca..f58692ae9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPluginFactory.java @@ -25,6 +25,6 @@ public class HostMonitoringConnectionPluginFactory implements ConnectionPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - return new HostMonitoringConnectionPlugin(pluginService, props); + return new HostMonitoringConnectionPlugin(pluginService.getServiceContainer(), props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java index be8e13130..e693579f2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java @@ -39,7 +39,9 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -62,6 +64,7 @@ public class MonitorImpl implements Monitor { private final Map>> newContexts = new ConcurrentHashMap<>(); private final PluginService pluginService; + private final ConnectionService connectionService; private final TelemetryFactory telemetryFactory; private final Properties properties; private final HostSpec hostSpec; @@ -84,20 +87,18 @@ public class MonitorImpl implements Monitor { private final TelemetryGauge newContextsSizeGauge; private final TelemetryGauge activeContextsSizeGauge; - private final TelemetryGauge nodeHealtyGauge; + private final TelemetryGauge nodeHealthyGauge; private final TelemetryCounter abortedConnectionsCounter; /** * Store the monitoring configuration for a connection. * - * @param pluginService A service for creating new connections. - * @param hostSpec The {@link HostSpec} of the server this {@link MonitorImpl} - * instance is monitoring. - * @param properties The {@link Properties} containing additional monitoring - * configuration. + * @param serviceContainer The service container for the services required by this class. + * @param hostSpec The {@link HostSpec} of the server this {@link MonitorImpl} instance is monitoring. + * @param properties The {@link Properties} containing additional monitoring configuration. */ public MonitorImpl( - final @NonNull PluginService pluginService, + final @NonNull ServiceContainer serviceContainer, final @NonNull HostSpec hostSpec, final @NonNull Properties properties, final int failureDetectionTimeMillis, @@ -105,8 +106,9 @@ public MonitorImpl( final int failureDetectionCount, final TelemetryCounter abortedConnectionsCounter) { - this.pluginService = pluginService; - this.telemetryFactory = pluginService.getTelemetryFactory(); + this.pluginService = serviceContainer.getPluginService(); + this.connectionService = serviceContainer.getConnectionService(); + this.telemetryFactory = serviceContainer.getTelemetryFactory(); this.hostSpec = hostSpec; this.properties = properties; this.failureDetectionTimeNano = TimeUnit.MILLISECONDS.toNanos(failureDetectionTimeMillis); @@ -126,7 +128,7 @@ public MonitorImpl( String.format("efm2.activeContexts.size.%s", hostId), () -> (long) this.activeContexts.size()); - this.nodeHealtyGauge = telemetryFactory.createGauge( + this.nodeHealthyGauge = telemetryFactory.createGauge( String.format("efm2.nodeHealthy.%s", hostId), () -> this.nodeUnhealthy ? 0L : 1L); @@ -356,7 +358,7 @@ boolean checkConnectionStatus() { }); LOGGER.finest(() -> "Opening a monitoring connection to " + this.hostSpec.getUrl()); - this.monitoringConn = this.pluginService.forceConnect(this.hostSpec, monitoringConnProperties); + this.monitoringConn = this.connectionService.createAuxiliaryConnection(this.hostSpec, monitoringConnProperties); LOGGER.finest(() -> "Opened monitoring connection: " + this.monitoringConn); return true; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java index f0f10dd0a..7f348acec 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java @@ -28,6 +28,7 @@ import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -66,9 +67,9 @@ public class MonitorServiceImpl implements MonitorService { protected final TelemetryFactory telemetryFactory; protected final TelemetryCounter abortedConnectionsCounter; - public MonitorServiceImpl(final @NonNull PluginService pluginService) { + public MonitorServiceImpl(final @NonNull ServiceContainer serviceContainer) { this( - pluginService, + serviceContainer, (hostSpec, properties, failureDetectionTimeMillis, @@ -76,7 +77,7 @@ public MonitorServiceImpl(final @NonNull PluginService pluginService) { failureDetectionCount, abortedConnectionsCounter) -> new MonitorImpl( - pluginService, + serviceContainer, hostSpec, properties, failureDetectionTimeMillis, @@ -86,10 +87,10 @@ public MonitorServiceImpl(final @NonNull PluginService pluginService) { } MonitorServiceImpl( - final @NonNull PluginService pluginService, + final @NonNull ServiceContainer serviceContainer, final @NonNull MonitorInitializer monitorInitializer) { - this.pluginService = pluginService; - this.telemetryFactory = pluginService.getTelemetryFactory(); + this.pluginService = serviceContainer.getPluginService(); + this.telemetryFactory = serviceContainer.getTelemetryFactory(); this.abortedConnectionsCounter = telemetryFactory.createCounter("efm2.connections.aborted"); this.monitorInitializer = monitorInitializer; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java index 26ceb5acd..308794078 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java @@ -38,7 +38,9 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.connection.ConnectionService; /** * An implementation of ReaderFailoverHandler. @@ -63,18 +65,19 @@ public class ClusterAwareReaderFailoverHandler implements ReaderFailoverHandler protected int timeoutMs; protected boolean isStrictReaderRequired; protected final PluginService pluginService; + protected final ConnectionService connectionService; /** * ClusterAwareReaderFailoverHandler constructor. * - * @param pluginService A provider for creating new connections. + * @param serviceContainer The service container for the services required by this class. * @param initialConnectionProps The initial connection properties to copy over to the new reader. */ public ClusterAwareReaderFailoverHandler( - final PluginService pluginService, + final ServiceContainer serviceContainer, final Properties initialConnectionProps) { this( - pluginService, + serviceContainer, initialConnectionProps, DEFAULT_FAILOVER_TIMEOUT, DEFAULT_READER_CONNECT_TIMEOUT, @@ -84,20 +87,21 @@ public ClusterAwareReaderFailoverHandler( /** * ClusterAwareReaderFailoverHandler constructor. * - * @param pluginService A provider for creating new connections. - * @param initialConnectionProps The initial connection properties to copy over to the new reader. - * @param maxFailoverTimeoutMs Maximum allowed time for the entire reader failover process. - * @param timeoutMs Maximum allowed time in milliseconds for each reader connection attempt during - * the reader failover process. + * @param serviceContainer The service container for the services required by this class. + * @param initialConnectionProps The initial connection properties to copy over to the new reader. + * @param maxFailoverTimeoutMs Maximum allowed time for the entire reader failover process. + * @param timeoutMs Maximum allowed time in milliseconds for each reader connection attempt during + * the reader failover process. * @param isStrictReaderRequired When true, it disables adding a writer to a list of nodes to connect */ public ClusterAwareReaderFailoverHandler( - final PluginService pluginService, + final ServiceContainer serviceContainer, final Properties initialConnectionProps, final int maxFailoverTimeoutMs, final int timeoutMs, final boolean isStrictReaderRequired) { - this.pluginService = pluginService; + this.pluginService = serviceContainer.getPluginService(); + this.connectionService = serviceContainer.getConnectionService(); this.initialConnectionProps = initialConnectionProps; this.maxFailoverTimeoutMs = maxFailoverTimeoutMs; this.timeoutMs = timeoutMs; @@ -277,8 +281,8 @@ public List getReaderHostsByPriority(final List hosts) { final int numOfReaders = activeReaders.size() + downHostList.size(); if (writerHost != null && (numOfReaders == 0 - || this.pluginService.getDialect().getFailoverRestrictions() - .contains(FailoverRestriction.ENABLE_WRITER_IN_TASK_B))) { + || this.pluginService.getDialect().getFailoverRestrictions() + .contains(FailoverRestriction.ENABLE_WRITER_IN_TASK_B))) { hostsByPriority.add(writerHost); } @@ -389,7 +393,7 @@ public ReaderFailoverResult call() { final Properties copy = new Properties(); copy.putAll(initialConnectionProps); - final Connection conn = pluginService.forceConnect(this.newHost, copy); + final Connection conn = connectionService.createAuxiliaryConnection(this.newHost, copy); pluginService.setAvailability(this.newHost.asAliases(), HostAvailability.AVAILABLE); if (this.isStrictReaderRequired) { @@ -400,7 +404,7 @@ public ReaderFailoverResult call() { LOGGER.fine( Messages.get( "ClusterAwareReaderFailoverHandler.readerRequired", - new Object[]{ this.newHost.getUrl(), role })); + new Object[] {this.newHost.getUrl(), role})); try { conn.close(); @@ -411,7 +415,7 @@ public ReaderFailoverResult call() { return FAILED_READER_FAILOVER_RESULT; } } catch (SQLException e) { - LOGGER.fine(Messages.get("ClusterAwareReaderFailoverHandler.errorGettingHostRole", new Object[]{e})); + LOGGER.fine(Messages.get("ClusterAwareReaderFailoverHandler.errorGettingHostRole", new Object[] {e})); try { conn.close(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java index a942d9dbf..da030a5b3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java @@ -37,7 +37,9 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.connection.ConnectionService; /** * An implementation of WriterFailoverHandler. @@ -56,28 +58,30 @@ public class ClusterAwareWriterFailoverHandler implements WriterFailoverHandler protected int reconnectWriterIntervalMs = 5000; // 5 sec protected Properties initialConnectionProps; protected PluginService pluginService; + protected ConnectionService connectionService; protected ReaderFailoverHandler readerFailoverHandler; private static final WriterFailoverResult DEFAULT_RESULT = new WriterFailoverResult(false, false, null, null, "None"); public ClusterAwareWriterFailoverHandler( - final PluginService pluginService, + final ServiceContainer serviceContainer, final ReaderFailoverHandler readerFailoverHandler, final Properties initialConnectionProps) { - this.pluginService = pluginService; + this.pluginService = serviceContainer.getPluginService(); + this.connectionService = serviceContainer.getConnectionService(); this.readerFailoverHandler = readerFailoverHandler; this.initialConnectionProps = initialConnectionProps; } public ClusterAwareWriterFailoverHandler( - final PluginService pluginService, + final ServiceContainer serviceContainer, final ReaderFailoverHandler readerFailoverHandler, final Properties initialConnectionProps, final int failoverTimeoutMs, final int readTopologyIntervalMs, final int reconnectWriterIntervalMs) { this( - pluginService, + serviceContainer, readerFailoverHandler, initialConnectionProps); this.maxFailoverTimeoutMs = failoverTimeoutMs; @@ -257,7 +261,7 @@ public WriterFailoverResult call() { conn.close(); } - conn = pluginService.forceConnect(this.originalWriterHost, initialConnectionProps); + conn = connectionService.createAuxiliaryConnection(this.originalWriterHost, initialConnectionProps); pluginService.forceRefreshHostList(conn); latestTopology = pluginService.getAllHosts(); @@ -464,7 +468,7 @@ private boolean connectToWriter(final HostSpec writerCandidate) { new Object[] {writerCandidate.getUrl()})); try { // connect to the new writer - this.currentConnection = pluginService.forceConnect(writerCandidate, initialConnectionProps); + this.currentConnection = connectionService.createAuxiliaryConnection(writerCandidate, initialConnectionProps); pluginService.setAvailability(writerCandidate.asAliases(), HostAvailability.AVAILABLE); return true; } catch (final SQLException exception) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPlugin.java index feafda3f8..fdf81c3f6 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPlugin.java @@ -47,6 +47,7 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.SubscribedMethodHelper; import software.amazon.jdbc.util.Utils; @@ -88,7 +89,8 @@ public class FailoverConnectionPlugin extends AbstractConnectionPlugin { static final String METHOD_CLOSE = "Connection.close"; static final String METHOD_IS_CLOSED = "Connection.isClosed"; - private final PluginService pluginService; + protected final ServiceContainer serviceContainer; + protected final PluginService pluginService; protected final Properties properties; protected boolean enableFailoverSetting; protected boolean enableConnectFailover; @@ -182,15 +184,16 @@ public class FailoverConnectionPlugin extends AbstractConnectionPlugin { PropertyDefinition.registerPluginProperties(FailoverConnectionPlugin.class); } - public FailoverConnectionPlugin(final PluginService pluginService, final Properties properties) { - this(pluginService, properties, new RdsUtils()); + public FailoverConnectionPlugin(final ServiceContainer serviceContainer, final Properties properties) { + this(serviceContainer, properties, new RdsUtils()); } FailoverConnectionPlugin( - final PluginService pluginService, + final ServiceContainer serviceContainer, final Properties properties, final RdsUtils rdsHelper) { - this.pluginService = pluginService; + this.serviceContainer = serviceContainer; + this.pluginService = serviceContainer.getPluginService(); this.properties = properties; this.rdsHelper = rdsHelper; @@ -287,14 +290,14 @@ public void initHostProvider( initHostProviderFunc, () -> new ClusterAwareReaderFailoverHandler( - this.pluginService, + this.serviceContainer, this.properties, this.failoverTimeoutMsSetting, this.failoverReaderConnectTimeoutMsSetting, this.failoverMode == FailoverMode.STRICT_READER), () -> new ClusterAwareWriterFailoverHandler( - this.pluginService, + this.serviceContainer, this.readerFailoverHandler, this.properties, this.failoverTimeoutMsSetting, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginFactory.java index 75d445710..e82d951b6 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginFactory.java @@ -25,6 +25,6 @@ public class FailoverConnectionPluginFactory implements ConnectionPluginFactory @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - return new FailoverConnectionPlugin(pluginService, props); + return new FailoverConnectionPlugin(pluginService.getServiceContainer(), props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPlugin.java index 1da725f15..2ec2ef9c5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPlugin.java @@ -34,7 +34,6 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.plugin.AbstractConnectionPlugin; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.PropertyUtils; public class LimitlessConnectionPlugin extends AbstractConnectionPlugin { @@ -86,7 +85,7 @@ public Set getSubscribedMethods() { public LimitlessConnectionPlugin(final PluginService pluginService, final @NonNull Properties properties) { this(pluginService, properties, - () -> new LimitlessRouterServiceImpl(pluginService)); + () -> new LimitlessRouterServiceImpl(pluginService.getServiceContainer())); } public LimitlessConnectionPlugin( diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java index 289d97c94..0be6f2dfe 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java @@ -28,12 +28,12 @@ import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.RoundRobinHostSelector; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryTraceLevel; @@ -49,7 +49,7 @@ public class LimitlessRouterMonitor implements AutoCloseable, Runnable { protected final SlidingExpirationCacheWithCleanupThread> limitlessRouterCache; protected final String limitlessRouterCacheKey; protected final @NonNull Properties props; - protected final @NonNull PluginService pluginService; + protected final @NonNull ConnectionService connectionService; protected final LimitlessQueryHelper queryHelper; protected final TelemetryFactory telemetryFactory; protected Connection monitoringConn = null; @@ -63,13 +63,13 @@ public class LimitlessRouterMonitor implements AutoCloseable, Runnable { private final AtomicBoolean stopped = new AtomicBoolean(false); public LimitlessRouterMonitor( - final @NonNull PluginService pluginService, + final @NonNull ServiceContainer serviceContainer, final @NonNull HostSpec hostSpec, final @NonNull SlidingExpirationCacheWithCleanupThread> limitlessRouterCache, final @NonNull String limitlessRouterCacheKey, final @NonNull Properties props, final int intervalMs) { - this.pluginService = pluginService; + this.connectionService = serviceContainer.getConnectionService(); this.hostSpec = hostSpec; this.limitlessRouterCache = limitlessRouterCache; this.limitlessRouterCacheKey = limitlessRouterCacheKey; @@ -86,8 +86,8 @@ public LimitlessRouterMonitor( this.props.setProperty(LimitlessConnectionPlugin.WAIT_FOR_ROUTER_INFO.name, "false"); this.intervalMs = intervalMs; - this.telemetryFactory = this.pluginService.getTelemetryFactory(); - this.queryHelper = new LimitlessQueryHelper(this.pluginService); + this.telemetryFactory = serviceContainer.getTelemetryFactory(); + this.queryHelper = new LimitlessQueryHelper(serviceContainer.getPluginService()); this.threadPool.submit(this); this.threadPool.shutdown(); // No more task are accepted by pool. } @@ -169,7 +169,7 @@ private void openConnection() throws SQLException { LOGGER.finest(() -> Messages.get( "LimitlessRouterMonitor.openingConnection", new Object[] {this.hostSpec.getUrl()})); - this.monitoringConn = this.pluginService.forceConnect(this.hostSpec, this.props); + this.monitoringConn = this.connectionService.createAuxiliaryConnection(this.hostSpec, this.props); LOGGER.finest(() -> Messages.get( "LimitlessRouterMonitor.openedConnection", new Object[] {this.monitoringConn})); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java index c9f956155..bac65a471 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java @@ -32,6 +32,7 @@ import software.amazon.jdbc.RoundRobinHostSelector; import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.wrapper.HighestWeightHostSelector; @@ -71,22 +72,22 @@ public class LimitlessRouterServiceImpl implements LimitlessRouterService { CACHE_CLEANUP_NANO ); - public LimitlessRouterServiceImpl(final @NonNull PluginService pluginService) { + public LimitlessRouterServiceImpl(final @NonNull ServiceContainer serviceContainer) { this( - pluginService, + serviceContainer.getPluginService(), (hostSpec, routerCache, routerCacheKey, props, intervalMs) -> new LimitlessRouterMonitor( - pluginService, + serviceContainer, hostSpec, routerCache, routerCacheKey, props, intervalMs), - new LimitlessQueryHelper(pluginService)); + new LimitlessQueryHelper(serviceContainer.getPluginService())); } public LimitlessRouterServiceImpl( diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java index 2b317fbd4..0f9972b51 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java @@ -40,6 +40,7 @@ import software.amazon.jdbc.RandomHostSelector; import software.amazon.jdbc.plugin.AbstractConnectionPlugin; import software.amazon.jdbc.util.CacheMap; +import software.amazon.jdbc.util.ServiceContainer; public class FastestResponseStrategyPlugin extends AbstractConnectionPlugin { @@ -80,21 +81,21 @@ public class FastestResponseStrategyPlugin extends AbstractConnectionPlugin { PropertyDefinition.registerPluginProperties("frt-"); } - public FastestResponseStrategyPlugin(final PluginService pluginService, final @NonNull Properties properties) { - this(pluginService, + public FastestResponseStrategyPlugin(final ServiceContainer serviceContainer, final @NonNull Properties properties) { + this(serviceContainer, properties, new HostResponseTimeServiceImpl( - pluginService, + serviceContainer, properties, RESPONSE_MEASUREMENT_INTERVAL_MILLIS.getInteger(properties))); } public FastestResponseStrategyPlugin( - final PluginService pluginService, + final ServiceContainer serviceContainer, final @NonNull Properties properties, final @NonNull HostResponseTimeService hostResponseTimeService) { - this.pluginService = pluginService; + this.pluginService = serviceContainer.getPluginService(); this.properties = properties; this.hostResponseTimeService = hostResponseTimeService; this.cacheExpirationNano = TimeUnit.MILLISECONDS.toNanos( diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPluginFactory.java index 87a1d766b..b3465a3a1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPluginFactory.java @@ -25,6 +25,6 @@ public class FastestResponseStrategyPluginFactory implements ConnectionPluginFac @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - return new FastestResponseStrategyPlugin(pluginService, props); + return new FastestResponseStrategyPlugin(pluginService.getServiceContainer(), props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/HostResponseTimeServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/HostResponseTimeServiceImpl.java index 1632be0b9..b77dd8ba0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/HostResponseTimeServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/HostResponseTimeServiceImpl.java @@ -26,19 +26,19 @@ import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryGauge; public class HostResponseTimeServiceImpl implements HostResponseTimeService { - private static final Logger LOGGER = - Logger.getLogger(HostResponseTimeServiceImpl.class.getName()); + private static final Logger LOGGER = Logger.getLogger(HostResponseTimeServiceImpl.class.getName()); protected static final long CACHE_EXPIRATION_NANO = TimeUnit.MINUTES.toNanos(10); protected static final long CACHE_CLEANUP_NANO = TimeUnit.MINUTES.toNanos(1); + protected static final ReentrantLock cacheLock = new ReentrantLock(); protected static final SlidingExpirationCacheWithCleanupThread monitoringNodes = new SlidingExpirationCacheWithCleanupThread<>( (monitor) -> true, @@ -50,28 +50,24 @@ public class HostResponseTimeServiceImpl implements HostResponseTimeService { } }, CACHE_CLEANUP_NANO); - protected static final ReentrantLock cacheLock = new ReentrantLock(); - - protected int intervalMs; - - protected List hosts = new ArrayList<>(); - protected final @NonNull PluginService pluginService; + protected final @NonNull ServiceContainer serviceContainer; protected final @NonNull Properties props; - protected final TelemetryFactory telemetryFactory; private final TelemetryGauge nodeCountGauge; + protected List hosts = new ArrayList<>(); + protected int intervalMs; + public HostResponseTimeServiceImpl( - final @NonNull PluginService pluginService, + final @NonNull ServiceContainer serviceContainer, final @NonNull Properties props, int intervalMs) { - - this.pluginService = pluginService; + this.serviceContainer = serviceContainer; this.props = props; this.intervalMs = intervalMs; - this.telemetryFactory = this.pluginService.getTelemetryFactory(); + this.telemetryFactory = serviceContainer.getTelemetryFactory(); this.nodeCountGauge = telemetryFactory.createGauge("frt.nodes.count", () -> (long) monitoringNodes.size()); @@ -102,7 +98,7 @@ public void setHosts(final @NonNull List hosts) { try { monitoringNodes.computeIfAbsent( hostSpec.getUrl(), - (key) -> new NodeResponseTimeMonitor(this.pluginService, hostSpec, this.props, this.intervalMs), + (key) -> new NodeResponseTimeMonitor(this.serviceContainer, hostSpec, this.props, this.intervalMs), CACHE_EXPIRATION_NANO); } finally { cacheLock.unlock(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java index 602f8d075..b83b6647b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java @@ -32,7 +32,9 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryGauge; @@ -55,6 +57,7 @@ public class NodeResponseTimeMonitor implements AutoCloseable, Runnable { private final @NonNull Properties props; private final @NonNull PluginService pluginService; + private final @NonNull ConnectionService connectionService; private final TelemetryFactory telemetryFactory; private final TelemetryGauge responseTimeMsGauge; @@ -69,16 +72,17 @@ public class NodeResponseTimeMonitor implements AutoCloseable, Runnable { }); public NodeResponseTimeMonitor( - final @NonNull PluginService pluginService, + final @NonNull ServiceContainer serviceContainer, final @NonNull HostSpec hostSpec, final @NonNull Properties props, int intervalMs) { - this.pluginService = pluginService; + this.pluginService = serviceContainer.getPluginService(); + this.connectionService = serviceContainer.getConnectionService(); this.hostSpec = hostSpec; this.props = props; this.intervalMs = intervalMs; - this.telemetryFactory = this.pluginService.getTelemetryFactory(); + this.telemetryFactory = serviceContainer.getTelemetryFactory(); final String nodeId = StringUtils.isNullOrEmpty(this.hostSpec.getHostId()) ? this.hostSpec.getHost() @@ -215,7 +219,7 @@ private void openConnection() { LOGGER.finest(() -> Messages.get( "NodeResponseTimeMonitor.openingConnection", new Object[] {this.hostSpec.getUrl()})); - this.monitoringConn = this.pluginService.forceConnect(this.hostSpec, monitoringConnProperties); + this.monitoringConn = this.connectionService.createAuxiliaryConnection(this.hostSpec, monitoringConnProperties); LOGGER.finest(() -> Messages.get( "NodeResponseTimeMonitor.openedConnection", new Object[] {this.monitoringConn})); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java index 6a26434df..caa5926c4 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java @@ -19,16 +19,16 @@ import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; +import software.amazon.jdbc.HostSpec; public interface ConnectionService { /** * Creates an auxiliary connection. Auxiliary connections are driver-internal connections that accomplish various * specific tasks such as monitoring a host's availability, checking the topology information for a cluster, etc. * - * @param underlyingDriverConnString the connection string for the underlying driver and database, eg - * "jdbc:postgresql://mydb.cluster-xyz.us-east-1.rds.amazonaws.com/some_db". + * @param hostSpec the hostSpec containing the host information for the auxiliary connection. * @param props the properties for the auxiliary connection. - * @return a new connection to the given connection string using the given props. + * @return a new connection to the given host using the given props. */ - Connection createAuxiliaryConnection(String underlyingDriverConnString, Properties props) throws SQLException; + Connection createAuxiliaryConnection(HostSpec hostSpec, Properties props) throws SQLException; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index db68d9acc..578e1c9bd 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -20,6 +20,8 @@ import java.sql.SQLException; import java.util.Properties; import software.amazon.jdbc.ConnectionProvider; +import software.amazon.jdbc.HostSpec; +import software.amazon.jdbc.targetdriverdialect.ConnectInfo; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.wrapper.ConnectionWrapper; @@ -28,23 +30,30 @@ public class ConnectionServiceImpl implements ConnectionService { protected ServiceContainer serviceContainer; protected ConnectionProvider connectionProvider; protected TargetDriverDialect driverDialect; + protected String targetDriverProtocol; public ConnectionServiceImpl( - ServiceContainer serviceContainer, ConnectionProvider connectionProvider, TargetDriverDialect driverDialect) { + ServiceContainer serviceContainer, + ConnectionProvider connectionProvider, + TargetDriverDialect driverDialect, + String targetDriverProtocol) { this.serviceContainer = serviceContainer; this.connectionProvider = connectionProvider; this.driverDialect = driverDialect; + this.targetDriverProtocol = targetDriverProtocol; } @Override - public Connection createAuxiliaryConnection(String underlyingDriverConnString, Properties props) throws SQLException { + public Connection createAuxiliaryConnection(HostSpec hostSpec, Properties props) throws SQLException { + ConnectInfo connectInfo = this.driverDialect.prepareConnectInfo(this.targetDriverProtocol, hostSpec, props); return new ConnectionWrapper( this.serviceContainer, props, - underlyingDriverConnString, + connectInfo.url, this.connectionProvider, null, - this.driverDialect, null + this.driverDialect, + null ); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index 92629d85c..fd380ea06 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -58,6 +58,7 @@ import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; import software.amazon.jdbc.util.connection.ConnectionService; +import software.amazon.jdbc.util.connection.ConnectionServiceImpl; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -116,6 +117,13 @@ public ConnectionWrapper( serviceContainer.setPluginService(pluginService); serviceContainer.setPluginManagerService(pluginService); + ConnectionService connectionService = new ConnectionServiceImpl( + serviceContainer, + defaultConnectionProvider, + targetDriverDialect, + this.targetDriverProtocol); + serviceContainer.setConnectionService(connectionService); + init(props, serviceContainer); if (PropertyDefinition.LOG_UNCLOSED_CONNECTIONS.getBoolean(props)) { diff --git a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java index 6866b0435..d504e8505 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java @@ -39,12 +39,14 @@ import software.amazon.jdbc.plugin.efm2.HostMonitoringConnectionPlugin; import software.amazon.jdbc.plugin.failover.FailoverConnectionPlugin; import software.amazon.jdbc.plugin.iam.IamAuthConnectionPlugin; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class ConnectionPluginChainBuilderTests { @Mock ConnectionProvider mockConnectionProvider; + @Mock ServiceContainer mockServiceContainer; @Mock PluginService mockPluginService; @Mock PluginManagerService mockPluginManagerService; @Mock TelemetryFactory mockTelemetryFactory; @@ -60,6 +62,9 @@ void afterEach() throws Exception { @BeforeEach void beforeEach() { closeable = MockitoAnnotations.openMocks(this); + when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); + when(mockServiceContainer.getTelemetryFactory()).thenReturn(mockTelemetryFactory); + when(mockPluginService.getServiceContainer()).thenReturn(mockServiceContainer); when(mockPluginService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockTelemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(mockTelemetryContext); when(mockTelemetryFactory.openTelemetryContext(eq(null), any())).thenReturn(mockTelemetryContext); diff --git a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java index e75bde363..48fdb805e 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java @@ -21,11 +21,9 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -64,6 +62,7 @@ import software.amazon.jdbc.plugin.failover.FailoverConnectionPlugin; import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.profile.ConfigurationProfileBuilder; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.WrapperUtils; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -78,6 +77,7 @@ public class ConnectionPluginManagerTests { @Mock ConnectionWrapper mockConnectionWrapper; @Mock TelemetryFactory mockTelemetryFactory; @Mock TelemetryContext mockTelemetryContext; + @Mock ServiceContainer mockServiceContainer; @Mock PluginService mockPluginService; @Mock PluginManagerService mockPluginManagerService; ConfigurationProfile configurationProfile = ConfigurationProfileBuilder.get().withName("test").build(); @@ -92,6 +92,8 @@ void cleanUp() throws Exception { @BeforeEach void init() { closeable = MockitoAnnotations.openMocks(this); + when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); + when(mockPluginService.getServiceContainer()).thenReturn(mockServiceContainer); when(mockPluginService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockTelemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(mockTelemetryContext); when(mockTelemetryFactory.openTelemetryContext(eq(null), any())).thenReturn(mockTelemetryContext); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java index 3010e6e77..0271bb6cd 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java @@ -141,7 +141,7 @@ public synchronized String format(LogRecord lr) { PluginService pluginService = new TestPluginService(hostSpec, connection); final HostMonitoringConnectionPlugin targetPlugin = - new HostMonitoringConnectionPlugin(pluginService, properties); + new HostMonitoringConnectionPlugin(pluginService.getServiceContainer(), properties); final Logger threadLogger = Logger.getLogger("software.amazon.jdbc.plugin.efm"); threadLogger.setLevel(logLevel); @@ -228,7 +228,7 @@ public synchronized String format(LogRecord lr) { final PluginService pluginService = new TestPluginService(hostSpec, connection); final HostMonitoringConnectionPlugin targetPlugin = - new HostMonitoringConnectionPlugin(pluginService, properties); + new HostMonitoringConnectionPlugin(pluginService.getServiceContainer(), properties); for (int i = 0; i < 10; i++) { executor.submit(() -> { diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java index 895fabfe3..21fc16425 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java @@ -64,6 +64,7 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; +import software.amazon.jdbc.util.ServiceContainer; class HostMonitoringConnectionPluginTest { @@ -74,6 +75,7 @@ class HostMonitoringConnectionPluginTest { static final int FAILURE_DETECTION_INTERVAL = 100; static final int FAILURE_DETECTION_COUNT = 5; private static final Object[] EMPTY_ARGS = {}; + @Mock ServiceContainer mockServiceContainer; @Mock PluginService pluginService; @Mock Dialect mockDialect; @Mock Connection connection; @@ -94,7 +96,7 @@ class HostMonitoringConnectionPluginTest { /** * Generate different sets of method arguments where one argument is null to ensure {@link - * software.amazon.jdbc.plugin.efm.HostMonitoringConnectionPlugin#HostMonitoringConnectionPlugin(PluginService, + * software.amazon.jdbc.plugin.efm.HostMonitoringConnectionPlugin#HostMonitoringConnectionPlugin(ServiceContainer, * Properties)} can handle null arguments correctly. * * @return different sets of arguments. @@ -134,6 +136,7 @@ void initDefaultMockReturns() throws Exception { .thenReturn(context); when(context.getLock()).thenReturn(mockReentrantLock); + when(mockServiceContainer.getPluginService()).thenReturn(pluginService); when(pluginService.getCurrentConnection()).thenReturn(connection); when(pluginService.getCurrentHostSpec()).thenReturn(hostSpec); when(pluginService.getDialect()).thenReturn(mockDialect); @@ -155,16 +158,7 @@ void initDefaultMockReturns() throws Exception { } private void initializePlugin() { - plugin = new HostMonitoringConnectionPlugin(pluginService, properties, supplier, rdsUtils); - } - - @ParameterizedTest - @MethodSource("generateNullArguments") - void test_initWithNullArguments( - final PluginService pluginService, final Properties properties) { - assertThrows( - IllegalArgumentException.class, - () -> new HostMonitoringConnectionPlugin(pluginService, properties)); + plugin = new HostMonitoringConnectionPlugin(mockServiceContainer, properties, supplier, rdsUtils); } @Test diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java index 558e2afba..6ece833d8 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java @@ -49,13 +49,17 @@ import org.mockito.MockitoAnnotations; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; class MonitorImplTest { + @Mock ServiceContainer serviceContainer; @Mock PluginService pluginService; + @Mock ConnectionService connectionService; @Mock Connection connection; @Mock HostSpec hostSpec; @Mock Properties properties; @@ -89,15 +93,18 @@ void init() throws SQLException { .thenReturn(LONG_INTERVAL_MILLIS); when(booleanProperty.getStringValue()).thenReturn(Boolean.TRUE.toString()); when(longProperty.getValue()).thenReturn(SHORT_INTERVAL_MILLIS); - when(pluginService.forceConnect(any(HostSpec.class), any(Properties.class))).thenReturn(connection); - when(pluginService.getTelemetryFactory()).thenReturn(telemetryFactory); + when(serviceContainer.getPluginService()).thenReturn(pluginService); + when(serviceContainer.getConnectionService()).thenReturn(connectionService); + when(serviceContainer.getTelemetryFactory()).thenReturn(telemetryFactory); + when(connectionService.createAuxiliaryConnection(any(HostSpec.class), any(Properties.class))) + .thenReturn(connection); when(telemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(telemetryContext); when(telemetryFactory.openTelemetryContext(eq(null), any())).thenReturn(telemetryContext); when(telemetryFactory.createCounter(anyString())).thenReturn(telemetryCounter); when(executorServiceInitializer.createExecutorService()).thenReturn(executorService); threadContainer = MonitorThreadContainer.getInstance(executorServiceInitializer); - monitor = spy(new MonitorImpl(pluginService, hostSpec, properties, 0L, threadContainer)); + monitor = spy(new MonitorImpl(serviceContainer, hostSpec, properties, 0L, threadContainer)); } @AfterEach @@ -112,7 +119,7 @@ void test_5_isConnectionHealthyWithNoExistingConnection() throws SQLException { final MonitorImpl.ConnectionStatus status = monitor.checkConnectionStatus(SHORT_INTERVAL_MILLIS); - verify(pluginService).forceConnect(any(HostSpec.class), any(Properties.class)); + verify(connectionService).createAuxiliaryConnection(any(HostSpec.class), any(Properties.class)); assertTrue(status.isValid); assertTrue(status.elapsedTimeNano >= 0); } diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorServiceImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorServiceImplTest.java index ce70f0f12..36bf9e476 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorServiceImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorServiceImplTest.java @@ -46,6 +46,7 @@ import software.amazon.jdbc.HostSpecBuilder; import software.amazon.jdbc.PluginService; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -65,6 +66,7 @@ class MonitorServiceImplTest { @Mock private Future task; @Mock private HostSpec hostSpec; @Mock private JdbcConnection connection; + @Mock private ServiceContainer serviceContainer; @Mock private PluginService pluginService; @Mock private TelemetryFactory telemetryFactory; @Mock private TelemetryCounter telemetryCounter; @@ -81,7 +83,8 @@ void init() { closeable = MockitoAnnotations.openMocks(this); contextCaptor = ArgumentCaptor.forClass(MonitorConnectionContext.class); - when(pluginService.getTelemetryFactory()).thenReturn(telemetryFactory); + when(serviceContainer.getPluginService()).thenReturn(pluginService); + when(serviceContainer.getTelemetryFactory()).thenReturn(telemetryFactory); when(telemetryFactory.createCounter(anyString())).thenReturn(telemetryCounter); when(monitorInitializer.createMonitor( any(HostSpec.class), any(Properties.class), any(MonitorThreadContainer.class))) @@ -92,7 +95,7 @@ void init() { doReturn(task).when(executorService).submit(any(Monitor.class)); threadContainer = MonitorThreadContainer.getInstance(executorServiceInitializer); - monitorService = new MonitorServiceImpl(pluginService, monitorInitializer, executorServiceInitializer); + monitorService = new MonitorServiceImpl(serviceContainer, monitorInitializer, executorServiceInitializer); } @AfterEach diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedDefaultMonitorServiceTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedDefaultMonitorServiceTest.java index c408d1398..dbefbb50f 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedDefaultMonitorServiceTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedDefaultMonitorServiceTest.java @@ -56,6 +56,7 @@ import org.mockito.MockitoAnnotations; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -73,6 +74,7 @@ class MultiThreadedDefaultMonitorServiceTest { @Mock Monitor monitor; @Mock Properties properties; @Mock JdbcConnection connection; + @Mock ServiceContainer serviceContainer; @Mock PluginService pluginService; @Mock TelemetryCounter abortedConnectionsCounter; @@ -112,7 +114,8 @@ void init(TestInfo testInfo) { doNothing().when(monitor).stopMonitoring(stopMonitoringCaptor.capture()); when(properties.getProperty(any(String.class))) .thenReturn(String.valueOf(MONITOR_DISPOSE_TIME)); - when(pluginService.getTelemetryFactory()).thenReturn(telemetryFactory); + when(serviceContainer.getPluginService()).thenReturn(pluginService); + when(serviceContainer.getTelemetryFactory()).thenReturn(telemetryFactory); when(telemetryFactory.createCounter(anyString())).thenReturn(abortedConnectionsCounter); } @@ -410,7 +413,7 @@ private List generateContexts( private List generateServices(final int numServices) { final List services = new ArrayList<>(); for (int i = 0; i < numServices; i++) { - services.add(new MonitorServiceImpl(pluginService, monitorInitializer, executorServiceInitializer)); + services.add(new MonitorServiceImpl(serviceContainer, monitorInitializer, executorServiceInitializer)); } return services; } diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java index 31d58f7b0..4d8766e1c 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java @@ -54,10 +54,14 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; +import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.connection.ConnectionService; class ClusterAwareReaderFailoverHandlerTest { + @Mock ServiceContainer mockServiceContainer; @Mock PluginService mockPluginService; + @Mock ConnectionService mockConnectionService; @Mock Connection mockConnection; private AutoCloseable closeable; @@ -80,6 +84,8 @@ class ClusterAwareReaderFailoverHandlerTest { @BeforeEach void setUp() { closeable = MockitoAnnotations.openMocks(this); + when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); + when(mockServiceContainer.getConnectionService()).thenReturn(mockConnectionService); } @AfterEach @@ -100,11 +106,11 @@ public void testFailover() throws SQLException { for (int i = 0; i < hosts.size(); i++) { if (i != successHostIndex) { final SQLException exception = new SQLException("exception", "08S01", null); - when(mockPluginService.forceConnect(hosts.get(i), properties)) + when(mockConnectionService.createAuxiliaryConnection(hosts.get(i), properties)) .thenThrow(exception); when(mockPluginService.isNetworkException(exception)).thenReturn(true); } else { - when(mockPluginService.forceConnect(hosts.get(i), properties)).thenReturn(mockConnection); + when(mockConnectionService.createAuxiliaryConnection(hosts.get(i), properties)).thenReturn(mockConnection); } } @@ -113,7 +119,7 @@ public void testFailover() throws SQLException { final ReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockPluginService, + mockServiceContainer, properties); final ReaderFailoverResult result = target.failover(hosts, hosts.get(currentHostIndex)); @@ -140,7 +146,7 @@ public void testFailover_timeout() throws SQLException { final List hosts = defaultHosts; final int currentHostIndex = 2; for (HostSpec host : hosts) { - when(mockPluginService.forceConnect(host, properties)) + when(mockConnectionService.createAuxiliaryConnection(host, properties)) .thenAnswer((Answer) invocation -> { Thread.sleep(20000); return mockConnection; @@ -152,7 +158,7 @@ public void testFailover_timeout() throws SQLException { final ReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockPluginService, + mockServiceContainer, properties, 5000, 30000, @@ -175,7 +181,7 @@ public void testFailover_timeout() throws SQLException { public void testFailover_nullOrEmptyHostList() throws SQLException { final ClusterAwareReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockPluginService, + mockServiceContainer, properties); final HostSpec currentHost = new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("writer") .port(1234).build(); @@ -200,14 +206,14 @@ public void testGetReader_connectionSuccess() throws SQLException { final List hosts = defaultHosts.subList(0, 3); // 2 connection attempts (writer not attempted) final HostSpec slowHost = hosts.get(1); final HostSpec fastHost = hosts.get(2); - when(mockPluginService.forceConnect(slowHost, properties)) + when(mockConnectionService.createAuxiliaryConnection(slowHost, properties)) .thenAnswer( (Answer) invocation -> { Thread.sleep(20000); return mockConnection; }); - when(mockPluginService.forceConnect(eq(fastHost), eq(properties))).thenReturn(mockConnection); + when(mockConnectionService.createAuxiliaryConnection(eq(fastHost), eq(properties))).thenReturn(mockConnection); Dialect mockDialect = Mockito.mock(Dialect.class); when(mockDialect.getFailoverRestrictions()).thenReturn(EnumSet.noneOf(FailoverRestriction.class)); @@ -215,7 +221,7 @@ public void testGetReader_connectionSuccess() throws SQLException { final ReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockPluginService, + mockServiceContainer, properties); final ReaderFailoverResult result = target.getReaderConnection(hosts); @@ -234,7 +240,8 @@ public void testGetReader_connectionFailure() throws SQLException { // first connection attempt to return fails // expected test result: failure to get reader final List hosts = defaultHosts.subList(0, 4); // 3 connection attempts (writer not attempted) - when(mockPluginService.forceConnect(any(), eq(properties))).thenThrow(new SQLException("exception", "08S01", null)); + when(mockConnectionService.createAuxiliaryConnection(any(), eq(properties))) + .thenThrow(new SQLException("exception", "08S01", null)); Dialect mockDialect = Mockito.mock(Dialect.class); when(mockDialect.getFailoverRestrictions()).thenReturn(EnumSet.noneOf(FailoverRestriction.class)); @@ -244,7 +251,7 @@ public void testGetReader_connectionFailure() throws SQLException { final ReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockPluginService, + mockServiceContainer, properties); final ReaderFailoverResult result = target.getReaderConnection(hosts); @@ -259,7 +266,7 @@ public void testGetReader_connectionAttemptsTimeout() throws SQLException { // first connection attempt to return times out // expected test result: failure to get reader final List hosts = defaultHosts.subList(0, 3); // 2 connection attempts (writer not attempted) - when(mockPluginService.forceConnect(any(), eq(properties))) + when(mockConnectionService.createAuxiliaryConnection(any(), eq(properties))) .thenAnswer( (Answer) invocation -> { @@ -277,7 +284,7 @@ public void testGetReader_connectionAttemptsTimeout() throws SQLException { final ClusterAwareReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockPluginService, + mockServiceContainer, properties, 60000, 1000, @@ -298,7 +305,7 @@ public void testGetHostTuplesByPriority() { final ClusterAwareReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockPluginService, + mockServiceContainer, properties); final List hostsByPriority = target.getHostsByPriority(originalHosts); @@ -340,7 +347,7 @@ public void testGetReaderTuplesByPriority() { final ClusterAwareReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockPluginService, + mockServiceContainer, properties); final List hostsByPriority = target.getReaderHostsByPriority(originalHosts); @@ -377,7 +384,7 @@ public void testHostFailoverStrictReaderEnabled() { when(mockPluginService.getDialect()).thenReturn(mockDialect); final ClusterAwareReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockPluginService, + mockServiceContainer, properties, DEFAULT_FAILOVER_TIMEOUT, DEFAULT_READER_CONNECT_TIMEOUT, diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java index 515325f7c..c0452273d 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java @@ -50,10 +50,14 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; +import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.connection.ConnectionService; class ClusterAwareWriterFailoverHandlerTest { + @Mock ServiceContainer mockServiceContainer; @Mock PluginService mockPluginService; + @Mock ConnectionService mockConnectionService; @Mock Connection mockConnection; @Mock ReaderFailoverHandler mockReaderFailover; @Mock Connection mockWriterConnection; @@ -78,6 +82,9 @@ class ClusterAwareWriterFailoverHandlerTest { @BeforeEach void setUp() { closeable = MockitoAnnotations.openMocks(this); + when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); + when(mockServiceContainer.getConnectionService()).thenReturn(mockConnectionService); + when(mockPluginService.getServiceContainer()).thenReturn(mockServiceContainer); writer.addAlias("writer-host"); newWriterHost.addAlias("new-writer-host"); readerA.addAlias("reader-a-host"); @@ -91,9 +98,9 @@ void tearDown() throws Exception { @Test public void testReconnectToWriter_taskBReaderException() throws SQLException { - when(mockPluginService.forceConnect(refEq(writer), eq(properties))).thenReturn(mockConnection); - when(mockPluginService.forceConnect(refEq(readerA), eq(properties))).thenThrow(SQLException.class); - when(mockPluginService.forceConnect(refEq(readerB), eq(properties))).thenThrow(SQLException.class); + when(mockConnectionService.createAuxiliaryConnection(refEq(writer), eq(properties))).thenReturn(mockConnection); + when(mockConnectionService.createAuxiliaryConnection(refEq(readerA), eq(properties))).thenThrow(SQLException.class); + when(mockConnectionService.createAuxiliaryConnection(refEq(readerB), eq(properties))).thenThrow(SQLException.class); when(mockPluginService.getAllHosts()).thenReturn(topology); @@ -104,7 +111,7 @@ public void testReconnectToWriter_taskBReaderException() throws SQLException { final ClusterAwareWriterFailoverHandler target = new ClusterAwareWriterFailoverHandler( - mockPluginService, + mockServiceContainer, mockReaderFailover, properties, 5000, @@ -130,9 +137,11 @@ public void testReconnectToWriter_taskBReaderException() throws SQLException { */ @Test public void testReconnectToWriter_SlowReaderA() throws SQLException { - when(mockPluginService.forceConnect(refEq(writer), eq(properties))).thenReturn(mockWriterConnection); - when(mockPluginService.forceConnect(refEq(readerB), eq(properties))).thenThrow(SQLException.class); - when(mockPluginService.forceConnect(refEq(newWriterHost), eq(properties))).thenReturn(mockNewWriterConnection); + when(mockConnectionService.createAuxiliaryConnection(refEq(writer), eq(properties))) + .thenReturn(mockWriterConnection); + when(mockConnectionService.createAuxiliaryConnection(refEq(readerB), eq(properties))).thenThrow(SQLException.class); + when(mockConnectionService.createAuxiliaryConnection(refEq(newWriterHost), eq(properties))) + .thenReturn(mockNewWriterConnection); when(mockPluginService.getAllHosts()).thenReturn(topology).thenReturn(newTopology); when(mockReaderFailover.getReaderConnection(ArgumentMatchers.anyList())) @@ -148,7 +157,7 @@ public void testReconnectToWriter_SlowReaderA() throws SQLException { final ClusterAwareWriterFailoverHandler target = new ClusterAwareWriterFailoverHandler( - mockPluginService, + mockServiceContainer, mockReaderFailover, properties, 60000, @@ -174,14 +183,14 @@ public void testReconnectToWriter_SlowReaderA() throws SQLException { */ @Test public void testReconnectToWriter_taskBDefers() throws SQLException { - when(mockPluginService.forceConnect(refEq(writer), eq(properties))) + when(mockConnectionService.createAuxiliaryConnection(refEq(writer), eq(properties))) .thenAnswer( (Answer) invocation -> { Thread.sleep(5000); return mockWriterConnection; }); - when(mockPluginService.forceConnect(refEq(readerB), eq(properties))).thenThrow(SQLException.class); + when(mockConnectionService.createAuxiliaryConnection(refEq(readerB), eq(properties))).thenThrow(SQLException.class); when(mockPluginService.getAllHosts()).thenReturn(topology); @@ -193,7 +202,7 @@ public void testReconnectToWriter_taskBDefers() throws SQLException { final ClusterAwareWriterFailoverHandler target = new ClusterAwareWriterFailoverHandler( - mockPluginService, + mockServiceContainer, mockReaderFailover, properties, 60000, @@ -220,16 +229,19 @@ public void testReconnectToWriter_taskBDefers() throws SQLException { */ @Test public void testConnectToReaderA_SlowWriter() throws SQLException { - when(mockPluginService.forceConnect(refEq(writer), eq(properties))) + when(mockConnectionService.createAuxiliaryConnection(refEq(writer), eq(properties))) .thenAnswer( (Answer) invocation -> { Thread.sleep(5000); return mockWriterConnection; }); - when(mockPluginService.forceConnect(refEq(readerA), eq(properties))).thenReturn(mockReaderAConnection); - when(mockPluginService.forceConnect(refEq(readerB), eq(properties))).thenReturn(mockReaderBConnection); - when(mockPluginService.forceConnect(refEq(newWriterHost), eq(properties))).thenReturn(mockNewWriterConnection); + when(mockConnectionService.createAuxiliaryConnection(refEq(readerA), eq(properties))) + .thenReturn(mockReaderAConnection); + when(mockConnectionService.createAuxiliaryConnection(refEq(readerB), eq(properties))) + .thenReturn(mockReaderBConnection); + when(mockConnectionService.createAuxiliaryConnection(refEq(newWriterHost), eq(properties))) + .thenReturn(mockNewWriterConnection); when(mockPluginService.getAllHosts()).thenReturn(newTopology); @@ -241,7 +253,7 @@ public void testConnectToReaderA_SlowWriter() throws SQLException { final ClusterAwareWriterFailoverHandler target = new ClusterAwareWriterFailoverHandler( - mockPluginService, + mockServiceContainer, mockReaderFailover, properties, 60000, @@ -268,10 +280,12 @@ public void testConnectToReaderA_SlowWriter() throws SQLException { */ @Test public void testConnectToReaderA_taskADefers() throws SQLException { - when(mockPluginService.forceConnect(writer, properties)).thenReturn(mockConnection); - when(mockPluginService.forceConnect(refEq(readerA), eq(properties))).thenReturn(mockReaderAConnection); - when(mockPluginService.forceConnect(refEq(readerB), eq(properties))).thenReturn(mockReaderBConnection); - when(mockPluginService.forceConnect(refEq(newWriterHost), eq(properties))) + when(mockConnectionService.createAuxiliaryConnection(writer, properties)).thenReturn(mockConnection); + when(mockConnectionService.createAuxiliaryConnection(refEq(readerA), eq(properties))) + .thenReturn(mockReaderAConnection); + when(mockConnectionService.createAuxiliaryConnection(refEq(readerB), eq(properties))) + .thenReturn(mockReaderBConnection); + when(mockConnectionService.createAuxiliaryConnection(refEq(newWriterHost), eq(properties))) .thenAnswer( (Answer) invocation -> { @@ -290,7 +304,7 @@ public void testConnectToReaderA_taskADefers() throws SQLException { final ClusterAwareWriterFailoverHandler target = new ClusterAwareWriterFailoverHandler( - mockPluginService, + mockServiceContainer, mockReaderFailover, properties, 60000, @@ -318,16 +332,18 @@ public void testConnectToReaderA_taskADefers() throws SQLException { */ @Test public void testFailedToConnect_failoverTimeout() throws SQLException { - when(mockPluginService.forceConnect(refEq(writer), eq(properties))) + when(mockConnectionService.createAuxiliaryConnection(refEq(writer), eq(properties))) .thenAnswer( (Answer) invocation -> { Thread.sleep(30000); return mockWriterConnection; }); - when(mockPluginService.forceConnect(refEq(readerA), eq(properties))).thenReturn(mockReaderAConnection); - when(mockPluginService.forceConnect(refEq(readerB), eq(properties))).thenReturn(mockReaderBConnection); - when(mockPluginService.forceConnect(refEq(newWriterHost), eq(properties))) + when(mockConnectionService.createAuxiliaryConnection(refEq(readerA), eq(properties))) + .thenReturn(mockReaderAConnection); + when(mockConnectionService.createAuxiliaryConnection(refEq(readerB), eq(properties))) + .thenReturn(mockReaderBConnection); + when(mockConnectionService.createAuxiliaryConnection(refEq(newWriterHost), eq(properties))) .thenAnswer( (Answer) invocation -> { @@ -344,7 +360,7 @@ public void testFailedToConnect_failoverTimeout() throws SQLException { final ClusterAwareWriterFailoverHandler target = new ClusterAwareWriterFailoverHandler( - mockPluginService, + mockServiceContainer, mockReaderFailover, properties, 5000, @@ -375,10 +391,12 @@ public void testFailedToConnect_failoverTimeout() throws SQLException { @Test public void testFailedToConnect_taskAException_taskBWriterException() throws SQLException { final SQLException exception = new SQLException("exception", "08S01", null); - when(mockPluginService.forceConnect(refEq(writer), eq(properties))).thenThrow(exception); - when(mockPluginService.forceConnect(refEq(readerA), eq(properties))).thenReturn(mockReaderAConnection); - when(mockPluginService.forceConnect(refEq(readerB), eq(properties))).thenReturn(mockReaderBConnection); - when(mockPluginService.forceConnect(refEq(newWriterHost), eq(properties))).thenThrow(exception); + when(mockConnectionService.createAuxiliaryConnection(refEq(writer), eq(properties))).thenThrow(exception); + when(mockConnectionService.createAuxiliaryConnection(refEq(readerA), eq(properties))) + .thenReturn(mockReaderAConnection); + when(mockConnectionService.createAuxiliaryConnection(refEq(readerB), eq(properties))) + .thenReturn(mockReaderBConnection); + when(mockConnectionService.createAuxiliaryConnection(refEq(newWriterHost), eq(properties))).thenThrow(exception); when(mockPluginService.isNetworkException(exception)).thenReturn(true); when(mockPluginService.getAllHosts()).thenReturn(newTopology); @@ -391,7 +409,7 @@ public void testFailedToConnect_taskAException_taskBWriterException() throws SQL final ClusterAwareWriterFailoverHandler target = new ClusterAwareWriterFailoverHandler( - mockPluginService, + mockServiceContainer, mockReaderFailover, properties, 5000, diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginTest.java index 2123848d1..15bcb8560 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginTest.java @@ -60,6 +60,7 @@ import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.hostlistprovider.AuroraHostListProvider; import software.amazon.jdbc.util.RdsUrlType; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.telemetry.GaugeCallable; import software.amazon.jdbc.util.telemetry.TelemetryContext; @@ -78,6 +79,7 @@ class FailoverConnectionPluginTest { new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) .host("reader1").port(1234).role(HostRole.READER).build()); + @Mock ServiceContainer mockServiceContainer; @Mock PluginService mockPluginService; @Mock Connection mockConnection; @Mock HostSpec mockHostSpec; @@ -107,7 +109,9 @@ void cleanUp() throws Exception { void init() throws SQLException { closeable = MockitoAnnotations.openMocks(this); + when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); when(mockPluginService.getHostListProvider()).thenReturn(mockHostListProvider); + when(mockPluginService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockHostListProvider.getRdsUrlType()).thenReturn(RdsUrlType.RDS_WRITER_CLUSTER); when(mockPluginService.getCurrentConnection()).thenReturn(mockConnection); when(mockPluginService.getCurrentHostSpec()).thenReturn(mockHostSpec); @@ -429,7 +433,7 @@ void test_execute_withDirectExecute() throws SQLException { } private void initializePlugin() { - plugin = new FailoverConnectionPlugin(mockPluginService, properties); + plugin = new FailoverConnectionPlugin(mockServiceContainer, properties); plugin.setWriterFailoverHandler(mockWriterFailoverHandler); plugin.setReaderFailoverHandler(mockReaderFailoverHandler); } From 278b13989d1d5e4d37d8f970c80ae0314ba9cca3 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 22 Apr 2025 11:18:00 -0700 Subject: [PATCH 067/149] Remove non-auxiliary plugins for auxiliary connections --- .../jdbc/ConnectionPluginChainBuilder.java | 27 +++++++++++++++++++ .../amazon/jdbc/PropertyDefinition.java | 5 ++++ .../jdbc/plugin/AuxiliaryPluginFactory.java | 27 +++++++++++++++++++ ...SecretsManagerConnectionPluginFactory.java | 2 +- .../ConnectTimeConnectionPluginFactory.java | 2 +- .../dev/DeveloperConnectionPluginFactory.java | 3 ++- .../FederatedAuthPluginFactory.java | 3 ++- .../federatedauth/OktaAuthPluginFactory.java | 4 +-- .../iam/IamAuthConnectionPluginFactory.java | 3 ++- .../connection/ConnectionServiceImpl.java | 4 ++- ..._advanced_jdbc_wrapper_messages.properties | 2 ++ 11 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/plugin/AuxiliaryPluginFactory.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java index 6b41835f8..a27f238da 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java @@ -28,6 +28,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.plugin.AuroraConnectionTrackerPluginFactory; import software.amazon.jdbc.plugin.AuroraInitialConnectionStrategyPluginFactory; +import software.amazon.jdbc.plugin.AuxiliaryPluginFactory; import software.amazon.jdbc.plugin.AwsSecretsManagerConnectionPluginFactory; import software.amazon.jdbc.plugin.ConnectTimeConnectionPluginFactory; import software.amazon.jdbc.plugin.DataCacheConnectionPluginFactory; @@ -163,6 +164,32 @@ public List getPlugins( } if (!pluginFactories.isEmpty()) { + if (PropertyDefinition.IS_AUXILIARY_CONNECTION.getBoolean(props)) { + // The requested connection is an auxiliary connection. Auxiliary connections should only use auxiliary plugins + // because non-auxiliary plugins can interfere with their intended purpose. For example, auxiliary EFM + // connections should not contain the failover plugin because they should always monitor the same instance. + List> auxiliaryPluginFactories = new ArrayList<>(); + List> removedPluginFactories = new ArrayList<>(); + for (Class factory : pluginFactories) { + if (AuxiliaryPluginFactory.class.isAssignableFrom(factory)) { + auxiliaryPluginFactories.add(factory); + } else { + removedPluginFactories.add(factory); + } + } + + if (!removedPluginFactories.isEmpty()) { + pluginFactories = auxiliaryPluginFactories; + String factoriesString = + auxiliaryPluginFactories.stream().map(Class::getSimpleName).collect(Collectors.joining(", ")); + String removedFactoriesString = + removedPluginFactories.stream().map(Class::getSimpleName).collect(Collectors.joining(", ")); + LOGGER.finest( + Messages.get( + "ConnectionPluginChainBuilder.removedNonAuxiliaryPlugins", + new Object[]{removedFactoriesString, factoriesString})); + } + } if (PropertyDefinition.AUTO_SORT_PLUGIN_ORDER.getBoolean(props)) { pluginFactories = this.sortPluginFactories(pluginFactories); diff --git a/wrapper/src/main/java/software/amazon/jdbc/PropertyDefinition.java b/wrapper/src/main/java/software/amazon/jdbc/PropertyDefinition.java index f7ca81ec7..387e31aa9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PropertyDefinition.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PropertyDefinition.java @@ -48,6 +48,11 @@ public class PropertyDefinition { new AwsWrapperProperty( "wrapperPlugins", null, "Comma separated list of connection plugin codes"); + public static final AwsWrapperProperty IS_AUXILIARY_CONNECTION = + new AwsWrapperProperty( + "isAuxiliaryConnection", "false", "Represents whether a connection is a " + + "driver-internal auxiliary connection or not. Auxiliary connections only contain auxiliary plugins."); + public static final AwsWrapperProperty AUTO_SORT_PLUGIN_ORDER = new AwsWrapperProperty( "autoSortWrapperPluginOrder", diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/AuxiliaryPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/AuxiliaryPluginFactory.java new file mode 100644 index 000000000..10a106057 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/AuxiliaryPluginFactory.java @@ -0,0 +1,27 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.plugin; + +import java.util.Properties; +import software.amazon.jdbc.HostSpec; +import software.amazon.jdbc.util.connection.ConnectionService; + +/** + * A marker interface for plugin factories that create plugins that should be included in auxiliary connections created + * with {@link ConnectionService#createAuxiliaryConnection(HostSpec, Properties)}. + */ +public interface AuxiliaryPluginFactory {} diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginFactory.java index a15abf45f..678ae19e6 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginFactory.java @@ -21,7 +21,7 @@ import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; -public class AwsSecretsManagerConnectionPluginFactory implements ConnectionPluginFactory { +public class AwsSecretsManagerConnectionPluginFactory implements ConnectionPluginFactory, AuxiliaryPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { return new AwsSecretsManagerConnectionPlugin(pluginService, props); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/ConnectTimeConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/ConnectTimeConnectionPluginFactory.java index 434b11d33..5ed4ecb37 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/ConnectTimeConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/ConnectTimeConnectionPluginFactory.java @@ -21,7 +21,7 @@ import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; -public class ConnectTimeConnectionPluginFactory implements ConnectionPluginFactory { +public class ConnectTimeConnectionPluginFactory implements ConnectionPluginFactory, AuxiliaryPluginFactory { @Override public ConnectionPlugin getInstance(PluginService pluginService, Properties props) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginFactory.java index 55fffeda8..381d0552c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginFactory.java @@ -20,8 +20,9 @@ import software.amazon.jdbc.ConnectionPlugin; import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.plugin.AuxiliaryPluginFactory; -public class DeveloperConnectionPluginFactory implements ConnectionPluginFactory { +public class DeveloperConnectionPluginFactory implements ConnectionPluginFactory, AuxiliaryPluginFactory { @Override public ConnectionPlugin getInstance(PluginService pluginService, Properties props) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/FederatedAuthPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/FederatedAuthPluginFactory.java index a3042669f..9052ffa6d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/FederatedAuthPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/FederatedAuthPluginFactory.java @@ -21,10 +21,11 @@ import software.amazon.jdbc.ConnectionPlugin; import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.plugin.AuxiliaryPluginFactory; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.StringUtils; -public class FederatedAuthPluginFactory implements ConnectionPluginFactory { +public class FederatedAuthPluginFactory implements ConnectionPluginFactory, AuxiliaryPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/OktaAuthPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/OktaAuthPluginFactory.java index 1061c4c7c..61e573ff5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/OktaAuthPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/OktaAuthPluginFactory.java @@ -21,10 +21,10 @@ import software.amazon.jdbc.ConnectionPlugin; import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.plugin.AuxiliaryPluginFactory; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.StringUtils; -public class OktaAuthPluginFactory implements ConnectionPluginFactory { +public class OktaAuthPluginFactory implements ConnectionPluginFactory, AuxiliaryPluginFactory { @Override public ConnectionPlugin getInstance(PluginService pluginService, Properties props) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPluginFactory.java index 7173ca249..47a0935a0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPluginFactory.java @@ -20,8 +20,9 @@ import software.amazon.jdbc.ConnectionPlugin; import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.plugin.AuxiliaryPluginFactory; -public class IamAuthConnectionPluginFactory implements ConnectionPluginFactory { +public class IamAuthConnectionPluginFactory implements ConnectionPluginFactory, AuxiliaryPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { return new IamAuthConnectionPlugin(pluginService); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index 578e1c9bd..bcd58305f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -21,6 +21,7 @@ import java.util.Properties; import software.amazon.jdbc.ConnectionProvider; import software.amazon.jdbc.HostSpec; +import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.targetdriverdialect.ConnectInfo; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ServiceContainer; @@ -46,9 +47,10 @@ public ConnectionServiceImpl( @Override public Connection createAuxiliaryConnection(HostSpec hostSpec, Properties props) throws SQLException { ConnectInfo connectInfo = this.driverDialect.prepareConnectInfo(this.targetDriverProtocol, hostSpec, props); + PropertyDefinition.IS_AUXILIARY_CONNECTION.set(connectInfo.props, "true"); return new ConnectionWrapper( this.serviceContainer, - props, + connectInfo.props, connectInfo.url, this.connectionProvider, null, diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 1d68bdae0..b6ef28b91 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -91,6 +91,8 @@ ClusterAwareWriterFailoverHandler.taskAEncounteredException=[TaskA] encountered ClusterAwareWriterFailoverHandler.standaloneNode=[TaskB] Host {0} is not yet connected to a cluster. The cluster is still being reconfigured. ClusterAwareWriterFailoverHandler.alreadyWriter=Current reader connection is actually a new writer connection. +ConnectionPluginChainBuilder.removedNonAuxiliaryPlugins=The requested connection is an auxiliary connection, so the following non-auxiliary plugin factories were removed: ''{0}''. The following auxiliary plugin factories will be used: ''{1}''. + # Connection String Host List Provider ConnectionStringHostListProvider.parsedListEmpty=Can''t parse connection string: ''{0}''. ConnectionStringHostListProvider.unsupportedIdentifyConnection=ConnectionStringHostListProvider does not support identifyConnection. From c576cb34f04e81e146000f861047fc6898719afd Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 22 Apr 2025 16:56:24 -0700 Subject: [PATCH 068/149] Fix bug where auxiliary connection was sharing a ServiceContainer with the requesting Connection --- .../connection/ConnectionServiceImpl.java | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index bcd58305f..992c6105b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -22,9 +22,11 @@ import software.amazon.jdbc.ConnectionProvider; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PropertyDefinition; -import software.amazon.jdbc.targetdriverdialect.ConnectInfo; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.ServiceContainerImpl; +import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; import software.amazon.jdbc.wrapper.ConnectionWrapper; public class ConnectionServiceImpl implements ConnectionService { @@ -46,12 +48,26 @@ public ConnectionServiceImpl( @Override public Connection createAuxiliaryConnection(HostSpec hostSpec, Properties props) throws SQLException { - ConnectInfo connectInfo = this.driverDialect.prepareConnectInfo(this.targetDriverProtocol, hostSpec, props); - PropertyDefinition.IS_AUXILIARY_CONNECTION.set(connectInfo.props, "true"); + // TODO: is it necessary to create a copy of the properties or are the passed props already a copy? + final Properties auxiliaryProps = PropertyUtils.copyProperties(props); + final String databaseName = PropertyDefinition.DATABASE.getString(auxiliaryProps) != null + ? PropertyDefinition.DATABASE.getString(auxiliaryProps) + : ""; + final String connString = this.targetDriverProtocol + hostSpec.getUrl() + databaseName; + PropertyDefinition.IS_AUXILIARY_CONNECTION.set(auxiliaryProps, "true"); + + // The auxiliary connection should have its own separate service container since the original container holds + // services that should not be shared between connections (for example, PluginService). + ServiceContainerImpl auxiliaryServiceContainer = new ServiceContainerImpl( + this.serviceContainer.getStorageService(), + this.serviceContainer.getMonitorService(), + new DefaultTelemetryFactory(auxiliaryProps)); + + // TODO: does the auxiliary connection need its own ConnectionProvider/TargetDriverDialect, or is it okay to share? return new ConnectionWrapper( - this.serviceContainer, - connectInfo.props, - connectInfo.url, + auxiliaryServiceContainer, + auxiliaryProps, + connString, this.connectionProvider, null, this.driverDialect, From 85361547b9943acf3d5ca279883cd6b5ca77b491 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 22 Apr 2025 17:12:03 -0700 Subject: [PATCH 069/149] Fix checkstyle --- .../amazon/jdbc/plugin/efm2/MonitorImpl.java | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java index 5f9b011ce..4fde46b12 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java @@ -93,15 +93,14 @@ public class MonitorImpl implements Monitor { /** * Store the monitoring configuration for a connection. * - * @param pluginService A service for creating new connections. - * @param hostSpec The {@link HostSpec} of the server this {@link MonitorImpl} - * instance is monitoring. - * @param properties The {@link Properties} containing additional monitoring - * configuration. - * @param failureDetectionTimeMillis A failure detection time in millis. + * @param serviceContainer The service container for the services required by this class. + * @param hostSpec The {@link HostSpec} of the server this {@link MonitorImpl} instance is + * monitoring. + * @param properties The {@link Properties} containing additional monitoring configuration. + * @param failureDetectionTimeMillis A failure detection time in millis. * @param failureDetectionIntervalMillis A failure detection interval in millis. - * @param failureDetectionCount A failure detection count. - * @param abortedConnectionsCounter Aborted connection telemetry counter. + * @param failureDetectionCount A failure detection count. + * @param abortedConnectionsCounter Aborted connection telemetry counter. */ public MonitorImpl( final @NonNull ServiceContainer serviceContainer, @@ -200,7 +199,7 @@ public void newContextRun() { LOGGER.finest(() -> Messages.get( "MonitorImpl.startMonitoringThreadNewContext", - new Object[]{this.hostSpec.getHost()})); + new Object[] {this.hostSpec.getHost()})); try { while (!this.stopped.get()) { @@ -238,14 +237,14 @@ public void newContextRun() { Level.FINEST, Messages.get( "MonitorImpl.exceptionDuringMonitoringStop", - new Object[]{this.hostSpec.getHost()}), + new Object[] {this.hostSpec.getHost()}), ex); // We want to print full trace stack of the exception. } } LOGGER.finest(() -> Messages.get( "MonitorImpl.stopMonitoringThreadNewContext", - new Object[]{this.hostSpec.getHost()})); + new Object[] {this.hostSpec.getHost()})); } @Override @@ -253,7 +252,7 @@ public void run() { LOGGER.finest(() -> Messages.get( "MonitorImpl.startMonitoringThread", - new Object[]{this.hostSpec.getHost()})); + new Object[] {this.hostSpec.getHost()})); try { while (!this.stopped.get()) { @@ -319,7 +318,7 @@ public void run() { Level.FINEST, Messages.get( "MonitorImpl.exceptionDuringMonitoringStop", - new Object[]{this.hostSpec.getHost()}), + new Object[] {this.hostSpec.getHost()}), ex); // We want to print full trace stack of the exception. } } finally { @@ -335,7 +334,7 @@ public void run() { LOGGER.finest(() -> Messages.get( "MonitorImpl.stopMonitoringThread", - new Object[]{this.hostSpec.getHost()})); + new Object[] {this.hostSpec.getHost()})); } /** From e4df9704470612e270161d8122a479b94661c112 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 23 Apr 2025 10:17:31 -0700 Subject: [PATCH 070/149] Fix CustomEndpointTest --- .../java/integration/container/tests/CustomEndpointTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wrapper/src/test/java/integration/container/tests/CustomEndpointTest.java b/wrapper/src/test/java/integration/container/tests/CustomEndpointTest.java index 677a58366..9c0fdaab4 100644 --- a/wrapper/src/test/java/integration/container/tests/CustomEndpointTest.java +++ b/wrapper/src/test/java/integration/container/tests/CustomEndpointTest.java @@ -44,6 +44,7 @@ import java.util.List; import java.util.Properties; import java.util.Set; +import java.util.UUID; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -77,11 +78,11 @@ @Order(16) public class CustomEndpointTest { private static final Logger LOGGER = Logger.getLogger(CustomEndpointTest.class.getName()); - protected static final String endpointId = "aurora-pg"; + protected static final String endpointId = "test-endpoint-1-" + UUID.randomUUID(); protected static DBClusterEndpoint endpointInfo; protected static final AuroraTestUtility auroraUtil = AuroraTestUtility.getUtility(); - protected static final boolean reuseExistingEndpoint = true; + protected static final boolean reuseExistingEndpoint = false; protected String currentWriter; From 932adee6e2ae46fd8c4f74319e6bbc0ff9455636 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 23 Apr 2025 18:27:38 -0700 Subject: [PATCH 071/149] Fix bug where duplicate topology monitor was being submitted --- .../monitoring/ClusterTopologyMonitorImpl.java | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 08429b9c0..ad5424867 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -111,16 +111,6 @@ public class ClusterTopologyMonitorImpl extends AbstractMonitor implements Clust protected final AtomicReference nodeThreadsReaderConnection = new AtomicReference<>(null); protected final AtomicReference> nodeThreadsLatestTopology = new AtomicReference<>(null); - - protected final ExecutorService monitorExecutor = Executors.newSingleThreadExecutor(runnableTarget -> { - final Thread monitoringThread = new Thread(runnableTarget); - monitoringThread.setDaemon(true); - if (!StringUtils.isNullOrEmpty(monitoringThread.getName())) { - monitoringThread.setName(monitoringThread.getName() + "-m"); - } - return monitoringThread; - }); - public ClusterTopologyMonitorImpl( final ServiceContainer serviceContainer, final String clusterId, @@ -168,9 +158,6 @@ public ClusterTopologyMonitorImpl( PropertyDefinition.CONNECT_TIMEOUT.set( this.monitoringProperties, String.valueOf(defaultConnectionTimeoutMs)); } - - this.monitorExecutor.submit(this); - this.monitorExecutor.shutdown(); // No more tasks are accepted by the pool. } @Override @@ -290,6 +277,7 @@ public void stop() { Thread.currentThread().interrupt(); } } catch (InterruptedException e) { + // TODO: should we add log? this.monitorExecutor.shutdownNow(); } } From d39aecfe12b921da75a893f2496393eee3355a2d Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 24 Apr 2025 10:06:18 -0700 Subject: [PATCH 072/149] fix: use default connection provider if the connection is auxiliary --- .../amazon/jdbc/plugin/DefaultConnectionPlugin.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java index 2d82d865f..08446ce80 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java @@ -42,6 +42,7 @@ import software.amazon.jdbc.OldConnectionSuggestedAction; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.SqlMethodAnalyzer; @@ -170,8 +171,13 @@ public Connection connect( final boolean isInitialConnection, final JdbcCallable connectFunc) throws SQLException { - - ConnectionProvider connProvider = this.connProviderManager.getConnectionProvider(driverProtocol, hostSpec, props); + ConnectionProvider connProvider; + if (PropertyDefinition.IS_AUXILIARY_CONNECTION.getBoolean(props)) { + // Auxiliary connections should connect directly instead of using custom connection providers. + connProvider = this.defaultConnProvider; + } else { + connProvider = this.connProviderManager.getConnectionProvider(driverProtocol, hostSpec, props); + } // It's guaranteed that this plugin is always the last in plugin chain so connectFunc can be // ignored. From a1851ec51c3e3307bbf379a59fba27ad9d8ed4e8 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 24 Apr 2025 12:19:25 -0700 Subject: [PATCH 073/149] Address some TODOs --- .../monitoring/ClusterTopologyMonitorImpl.java | 10 +++++++--- .../jdbc/util/connection/ConnectionServiceImpl.java | 1 - .../amazon/jdbc/util/storage/ExpirationCache.java | 4 ++-- .../jdbc/util/storage/ExternallyManagedCache.java | 5 +++-- .../amazon/jdbc/util/storage/StorageService.java | 1 - .../amazon/jdbc/util/storage/StorageServiceImpl.java | 2 +- .../aws_advanced_jdbc_wrapper_messages.properties | 4 ++++ 7 files changed, 17 insertions(+), 10 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index ad5424867..adcb14905 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -271,13 +271,17 @@ public void stop() { // Waiting for 30s gives a thread enough time to exit monitoring loop and close database connection. try { - if (!this.monitorExecutor.awaitTermination(30, TimeUnit.SECONDS)) { - LOGGER.fine(Messages.get("ClusterTopologyMonitorImpl.interrupted")); + long timeout = 30; + TimeUnit timeoutUnit = TimeUnit.SECONDS; + if (!this.monitorExecutor.awaitTermination(timeout, timeoutUnit)) { + LOGGER.fine( + Messages.get("ClusterTopologyMonitorImpl.awaitTerminationTimeout", new Object[]{timeout, timeoutUnit})); this.monitorExecutor.shutdownNow(); Thread.currentThread().interrupt(); } } catch (InterruptedException e) { - // TODO: should we add log? + LOGGER.fine( + Messages.get("ClusterTopologyMonitorImpl.interruptedWhileTerminating")); this.monitorExecutor.shutdownNow(); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index 992c6105b..aac505dca 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -48,7 +48,6 @@ public ConnectionServiceImpl( @Override public Connection createAuxiliaryConnection(HostSpec hostSpec, Properties props) throws SQLException { - // TODO: is it necessary to create a copy of the properties or are the passed props already a copy? final Properties auxiliaryProps = PropertyUtils.copyProperties(props); final String databaseName = PropertyDefinition.DATABASE.getString(auxiliaryProps) != null ? PropertyDefinition.DATABASE.getString(auxiliaryProps) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index 497e05623..7eba6ed76 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -29,7 +29,8 @@ import software.amazon.jdbc.util.ShouldDisposeFunc; /** - * A cache that can be used to store values that expire after a configured period of time. + * A cache that can be used to store values that expire after a configured period of time. Entries are disposed when + * removed from the cache if an {@link ItemDisposalFunc} is defined. * * @param the type of the keys in the cache. * @param the type of the values in the cache. @@ -88,7 +89,6 @@ public ExpirationCache( } // cacheItem is the previous value associated with the key. - // TODO: should we check expiration or call shouldDisposeFunc here even though the item is definitely being removed? if (this.itemDisposalFunc != null) { this.itemDisposalFunc.dispose(cacheItem.item); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java index 17a54fc8f..faa60b7c3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java @@ -26,6 +26,7 @@ import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import software.amazon.jdbc.util.Messages; /** * A cache with expiration functionality that does not automatically remove expired entries. The removal of expired @@ -120,10 +121,10 @@ public ExternallyManagedCache(long timeToLiveNanos) { */ public void extendExpiration(K key) { final CacheItem cacheItem = cache.get(key); - // TODO: should we log if the key doesn't exist? - if (cacheItem != null) { cacheItem.extendExpiration(); + } else { + LOGGER.finest(Messages.get("ExternallyManagedCache.extendExpirationOnNonExistingKey", new Object[]{key})); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java index c54b90a3b..147938b20 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java @@ -97,6 +97,5 @@ void registerItemClassIfAbsent( // method should potentially be removed at that point as well. @Nullable Map getEntries(Class itemClass); - // TODO: this method is only called by tests. We should evaluate whether we want to keep it or not. int size(Class itemClass); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index e42455f6b..6b36ef0df 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -203,7 +203,7 @@ public void clearAll() { return null; } - // TODO: fix this cast to be type safe, or remove this method after removing the suggestedClusterId logic + // TODO: fix this cast to be type safe and/or remove this method after removing the suggestedClusterId logic return (Map) cache.getEntries(); } diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index b6ef28b91..c99cc6b51 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -172,6 +172,8 @@ DataSource.failedToSetProperty=Failed to set property ''{0}'' on target datasour # Execution Time Connection Plugin ExecutionTimeConnectionPlugin.executionTime=Executed {0} in {1} nanos. +ExternallyManagedCache.extendExpirationOnNonExistingKey=A request was made to extend the expiration of the entry at key {0}, but the key does not exist. + # Failover Connection Plugin Failover.transactionResolutionUnknownError=Transaction resolution unknown. Please re-configure session state if required and try restarting the transaction. Failover.connectionClosedExplicitly=Unable to failover, the connection has been explicitly closed. @@ -416,9 +418,11 @@ NodeResponseTimeMonitor.openingConnection=Opening a Response time connection to NodeResponseTimeMonitor.openedConnection=Opened Response time connection: {0}. # Monitoring RDS HostList Provider +ClusterTopologyMonitorImpl.awaitTerminationTimeout=Awaiting termination of the monitor executor timed out after {0} {1}. ClusterTopologyMonitorImpl.startMonitoringThread=Start cluster topology monitoring thread for ''{0}''. ClusterTopologyMonitorImpl.stopMonitoringThread=Stop cluster topology monitoring thread for ''{0}''. ClusterTopologyMonitorImpl.exceptionDuringMonitoringStop=Stopping cluster topology monitoring after unhandled exception was thrown in monitoring thread for node ''{0}''. +ClusterTopologyMonitorImpl.interruptedWhileTerminating=An InterruptedException occurred while terminating or shutting down the monitor executor. ClusterTopologyMonitorImpl.invalidQuery=An error occurred while attempting to obtain the topology because the topology query was invalid. Please ensure you are connecting to an Aurora or RDS Db cluster. ClusterTopologyMonitorImpl.errorGettingNetworkTimeout=An error occurred while getting the connection network timeout: {0} ClusterTopologyMonitorImpl.invalidTopology=The topology query returned an invalid topology - no writer instance detected. From 35832ceb19aec5fbe847e752cc3074c803f45575 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 24 Apr 2025 15:21:31 -0700 Subject: [PATCH 074/149] cleanup --- wrapper/src/main/java/software/amazon/jdbc/PluginService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java index c7da5083c..f2f2239a3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java @@ -30,6 +30,7 @@ import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; /** @@ -220,9 +221,12 @@ Connection connect(HostSpec hostSpec, Properties props, final @Nullable Connecti * @return a {@link Connection} to the requested host * @throws SQLException if there was an error establishing a {@link Connection} to the requested * host + * @deprecated Use {@link ConnectionService#createAuxiliaryConnection(HostSpec, Properties)} instead. */ + @Deprecated Connection forceConnect(HostSpec hostSpec, Properties props) throws SQLException; + @Deprecated Connection forceConnect( HostSpec hostSpec, Properties props, final @Nullable ConnectionPlugin pluginToSkip) throws SQLException; From 07a6add2aacb005c88e307f4e66f276148d11b85 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 25 Apr 2025 10:33:47 -0700 Subject: [PATCH 075/149] PR cleanup --- .../jdbc/ConnectionPluginChainBuilder.java | 24 ++------- .../amazon/jdbc/PluginServiceImpl.java | 7 ++- .../jdbc/dialect/AuroraMysqlDialect.java | 2 +- .../ClusterTopologyMonitorImpl.java | 3 +- .../CustomEndpointMonitorImpl.java | 46 ++++++++--------- .../customendpoint/CustomEndpointPlugin.java | 50 +++++++++++-------- .../CustomEndpointPluginFactory.java | 2 +- .../amazon/jdbc/plugin/efm/MonitorImpl.java | 2 +- .../ClusterAwareReaderFailoverHandler.java | 6 +-- .../limitless/LimitlessConnectionPlugin.java | 11 ++-- .../LimitlessConnectionPluginFactory.java | 2 +- .../amazon/jdbc/util/ShouldDisposeFunc.java | 3 +- .../util/connection/ConnectionService.java | 2 +- .../connection/ConnectionServiceImpl.java | 4 +- .../jdbc/util/events/DataAccessEvent.java | 4 +- .../util/events/PeriodicEventPublisher.java | 2 +- .../jdbc/util/monitoring/MonitorService.java | 9 ++-- .../util/monitoring/MonitorServiceImpl.java | 10 ++-- .../jdbc/util/monitoring/MonitorSettings.java | 2 +- .../jdbc/util/monitoring/MonitorState.java | 2 +- .../jdbc/util/storage/ExpirationCache.java | 21 +++++--- .../util/storage/ExternallyManagedCache.java | 11 ++-- .../jdbc/util/storage/StorageService.java | 38 +++++++------- .../jdbc/util/storage/StorageServiceImpl.java | 10 ++-- .../amazon/jdbc/util/storage/Topology.java | 7 +-- ..._advanced_jdbc_wrapper_messages.properties | 16 +++--- .../CustomEndpointMonitorImplTest.java | 14 ++++-- .../CustomEndpointPluginTest.java | 7 ++- ...ClusterAwareWriterFailoverHandlerTest.java | 1 - .../LimitlessConnectionPluginTest.java | 14 ++++-- 30 files changed, 166 insertions(+), 166 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java index a27f238da..41e5a302a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java @@ -168,27 +168,9 @@ public List getPlugins( // The requested connection is an auxiliary connection. Auxiliary connections should only use auxiliary plugins // because non-auxiliary plugins can interfere with their intended purpose. For example, auxiliary EFM // connections should not contain the failover plugin because they should always monitor the same instance. - List> auxiliaryPluginFactories = new ArrayList<>(); - List> removedPluginFactories = new ArrayList<>(); - for (Class factory : pluginFactories) { - if (AuxiliaryPluginFactory.class.isAssignableFrom(factory)) { - auxiliaryPluginFactories.add(factory); - } else { - removedPluginFactories.add(factory); - } - } - - if (!removedPluginFactories.isEmpty()) { - pluginFactories = auxiliaryPluginFactories; - String factoriesString = - auxiliaryPluginFactories.stream().map(Class::getSimpleName).collect(Collectors.joining(", ")); - String removedFactoriesString = - removedPluginFactories.stream().map(Class::getSimpleName).collect(Collectors.joining(", ")); - LOGGER.finest( - Messages.get( - "ConnectionPluginChainBuilder.removedNonAuxiliaryPlugins", - new Object[]{removedFactoriesString, factoriesString})); - } + pluginFactories = pluginFactories.stream() + .filter(AuxiliaryPluginFactory.class::isAssignableFrom) + .collect(Collectors.toList()); } if (PropertyDefinition.AUTO_SORT_PLUGIN_ORDER.getBoolean(props)) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index 21d0820e9..89dba6b86 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -54,6 +54,7 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class PluginServiceImpl implements PluginService, CanReleaseResources, @@ -64,6 +65,7 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources, protected static final CacheMap hostAvailabilityExpiringCache = new CacheMap<>(); protected final ServiceContainer serviceContainer; + protected final StorageService storageService; protected final ConnectionPluginManager pluginManager; private final Properties props; private final String originalUrl; @@ -136,6 +138,7 @@ public PluginServiceImpl( @Nullable final ConfigurationProfile configurationProfile, @Nullable final SessionStateService sessionStateService) throws SQLException { this.serviceContainer = serviceContainer; + this.storageService = serviceContainer.getStorageService(); this.pluginManager = serviceContainer.getConnectionPluginManager(); this.props = props; this.originalUrl = originalUrl; @@ -216,7 +219,7 @@ public ServiceContainer getServiceContainer() { @Override @Deprecated public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHosts) { - this.serviceContainer.getStorageService().set(this.initialConnectionHostSpec.getHost(), allowedAndBlockedHosts); + this.storageService.set(this.initialConnectionHostSpec.getHost(), allowedAndBlockedHosts); } @Override @@ -410,7 +413,7 @@ public List getAllHosts() { @Override public List getHosts() { - AllowedAndBlockedHosts hostPermissions = this.serviceContainer.getStorageService().get( + AllowedAndBlockedHosts hostPermissions = this.storageService.get( AllowedAndBlockedHosts.class, this.initialConnectionHostSpec.getHost()); if (hostPermissions == null) { return this.allHosts; diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java index 8279502e9..425dca624 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java @@ -82,7 +82,7 @@ public boolean isDialect(final Connection connection) { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, serviceContainer) -> { + return (properties, initialUrl, serviceContainer) -> { final PluginService pluginService = serviceContainer.getPluginService(); final FailoverConnectionPlugin failover2Plugin = pluginService.getPlugin(FailoverConnectionPlugin.class); diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index adcb14905..8d7b55f36 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -277,9 +277,9 @@ public void stop() { LOGGER.fine( Messages.get("ClusterTopologyMonitorImpl.awaitTerminationTimeout", new Object[]{timeout, timeoutUnit})); this.monitorExecutor.shutdownNow(); - Thread.currentThread().interrupt(); } } catch (InterruptedException e) { + Thread.currentThread().interrupt(); LOGGER.fine( Messages.get("ClusterTopologyMonitorImpl.interruptedWhileTerminating")); this.monitorExecutor.shutdownNow(); @@ -418,7 +418,6 @@ public void monitor() { } catch (final InterruptedException intEx) { Thread.currentThread().interrupt(); - } catch (final Exception ex) { // this should not be reached; log and exit thread if (LOGGER.isLoggable(Level.FINEST)) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index 000400cc0..cfb97788c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -34,11 +34,10 @@ import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.util.CacheMap; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.monitoring.AbstractMonitor; -import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; -import software.amazon.jdbc.util.telemetry.TelemetryFactory; /** * The default custom endpoint monitor implementation. This class uses a background thread to monitor a given custom @@ -65,10 +64,7 @@ public class CustomEndpointMonitorImpl extends AbstractMonitor implements Custom /** * Constructs a CustomEndpointMonitorImpl instance for the host specified by {@code customEndpointHostSpec}. * - * @param monitorService The monitorService used to submit this monitor. - * @param storageService The storage service used to store the set of allowed/blocked hosts according to the - * custom endpoint info. - * @param telemetryFactory The telemetry factory + * @param serviceContainer The service container for the services required by this class. * @param customEndpointHostSpec The host information for the custom endpoint to be monitored. * @param endpointIdentifier An endpoint identifier. * @param region The region of the custom endpoint to be monitored. @@ -78,23 +74,21 @@ public class CustomEndpointMonitorImpl extends AbstractMonitor implements Custom * information. */ public CustomEndpointMonitorImpl( - MonitorService monitorService, - StorageService storageService, - TelemetryFactory telemetryFactory, + ServiceContainer serviceContainer, HostSpec customEndpointHostSpec, String endpointIdentifier, Region region, long refreshRateNano, BiFunction rdsClientFunc) { - super(monitorService); - this.storageService = storageService; + super(serviceContainer.getMonitorService()); + this.storageService = serviceContainer.getStorageService(); this.customEndpointHostSpec = customEndpointHostSpec; this.endpointIdentifier = endpointIdentifier; this.region = region; this.refreshRateNano = refreshRateNano; this.rdsClient = rdsClientFunc.apply(customEndpointHostSpec, this.region); - this.infoChangedCounter = telemetryFactory.createCounter(TELEMETRY_ENDPOINT_INFO_CHANGED); + this.infoChangedCounter = serviceContainer.getTelemetryFactory().createCounter(TELEMETRY_ENDPOINT_INFO_CHANGED); } /** @@ -105,7 +99,7 @@ public void monitor() { LOGGER.fine( Messages.get( "CustomEndpointMonitorImpl.startingMonitor", - new Object[] { this.customEndpointHostSpec.getHost() })); + new Object[] { this.customEndpointHostSpec.getUrl() })); try { while (!this.stop.get() && !Thread.currentThread().isInterrupted()) { @@ -139,7 +133,7 @@ public void monitor() { } CustomEndpointInfo endpointInfo = CustomEndpointInfo.fromDBClusterEndpoint(endpoints.get(0)); - CustomEndpointInfo cachedEndpointInfo = customEndpointInfoCache.get(this.customEndpointHostSpec.getHost()); + CustomEndpointInfo cachedEndpointInfo = customEndpointInfoCache.get(this.customEndpointHostSpec.getUrl()); if (cachedEndpointInfo != null && cachedEndpointInfo.equals(endpointInfo)) { long elapsedTime = System.nanoTime() - start; long sleepDuration = Math.max(0, this.refreshRateNano - elapsedTime); @@ -150,7 +144,7 @@ public void monitor() { LOGGER.fine( Messages.get( "CustomEndpointMonitorImpl.detectedChangeInCustomEndpointInfo", - new Object[] {this.customEndpointHostSpec.getHost(), endpointInfo})); + new Object[] {this.customEndpointHostSpec.getUrl(), endpointInfo})); // The custom endpoint info has changed, so we need to update the set of allowed/blocked hosts. AllowedAndBlockedHosts allowedAndBlockedHosts; @@ -160,9 +154,9 @@ public void monitor() { allowedAndBlockedHosts = new AllowedAndBlockedHosts(null, endpointInfo.getExcludedMembers()); } - this.storageService.set(this.customEndpointHostSpec.getHost(), allowedAndBlockedHosts); + this.storageService.set(this.customEndpointHostSpec.getUrl(), allowedAndBlockedHosts); customEndpointInfoCache.put( - this.customEndpointHostSpec.getHost(), endpointInfo, CUSTOM_ENDPOINT_INFO_EXPIRATION_NANO); + this.customEndpointHostSpec.getUrl(), endpointInfo, CUSTOM_ENDPOINT_INFO_EXPIRATION_NANO); this.infoChangedCounter.inc(); long elapsedTime = System.nanoTime() - start; @@ -175,27 +169,27 @@ public void monitor() { LOGGER.log(Level.SEVERE, Messages.get( "CustomEndpointMonitorImpl.exception", - new Object[]{this.customEndpointHostSpec.getHost()}), e); + new Object[]{this.customEndpointHostSpec.getUrl()}), e); } } } catch (InterruptedException e) { LOGGER.fine( Messages.get( "CustomEndpointMonitorImpl.interrupted", - new Object[]{ this.customEndpointHostSpec.getHost() })); + new Object[]{ this.customEndpointHostSpec.getUrl() })); Thread.currentThread().interrupt(); } finally { - customEndpointInfoCache.remove(this.customEndpointHostSpec.getHost()); + customEndpointInfoCache.remove(this.customEndpointHostSpec.getUrl()); this.rdsClient.close(); LOGGER.fine( Messages.get( "CustomEndpointMonitorImpl.stoppedMonitor", - new Object[]{ this.customEndpointHostSpec.getHost() })); + new Object[]{ this.customEndpointHostSpec.getUrl() })); } } public boolean hasCustomEndpointInfo() { - return customEndpointInfoCache.get(this.customEndpointHostSpec.getHost()) != null; + return customEndpointInfoCache.get(this.customEndpointHostSpec.getUrl()) != null; } /** @@ -206,7 +200,7 @@ public void stop() { LOGGER.fine( Messages.get( "CustomEndpointMonitorImpl.stoppingMonitor", - new Object[]{ this.customEndpointHostSpec.getHost() })); + new Object[]{ this.customEndpointHostSpec.getUrl() })); this.stop.set(true); @@ -216,7 +210,7 @@ public void stop() { LOGGER.info( Messages.get( "CustomEndpointMonitorImpl.monitorTerminationTimeout", - new Object[]{ terminationTimeoutSec, this.customEndpointHostSpec.getHost() })); + new Object[]{ terminationTimeoutSec, this.customEndpointHostSpec.getUrl() })); this.monitorExecutor.shutdownNow(); } @@ -224,12 +218,12 @@ public void stop() { LOGGER.info( Messages.get( "CustomEndpointMonitorImpl.interruptedWhileTerminating", - new Object[]{ this.customEndpointHostSpec.getHost() })); + new Object[]{ this.customEndpointHostSpec.getUrl() })); Thread.currentThread().interrupt(); this.monitorExecutor.shutdownNow(); } finally { - customEndpointInfoCache.remove(this.customEndpointHostSpec.getHost()); + customEndpointInfoCache.remove(this.customEndpointHostSpec.getUrl()); this.rdsClient.close(); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java index 88fb23489..81e4f4eeb 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java @@ -30,16 +30,18 @@ import software.amazon.jdbc.AwsWrapperProperty; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.JdbcCallable; -import software.amazon.jdbc.PluginService; import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.authentication.AwsCredentialsManager; import software.amazon.jdbc.plugin.AbstractConnectionPlugin; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUtils; import software.amazon.jdbc.util.RegionUtils; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SubscribedMethodHelper; import software.amazon.jdbc.util.WrapperUtils; +import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -87,7 +89,10 @@ public class CustomEndpointPlugin extends AbstractConnectionPlugin { PropertyDefinition.registerPluginProperties(CustomEndpointPlugin.class); } - protected final PluginService pluginService; + protected final ServiceContainer serviceContainer; + protected final StorageService storageService; + protected final MonitorService monitorService; + protected final TelemetryFactory telemetryFactory; protected final Properties props; protected final RdsUtils rdsUtils = new RdsUtils(); protected final BiFunction rdsClientFunc; @@ -103,12 +108,12 @@ public class CustomEndpointPlugin extends AbstractConnectionPlugin { /** * Constructs a new CustomEndpointPlugin instance. * - * @param pluginService The plugin service that the custom endpoint plugin should use. - * @param props The properties that the custom endpoint plugin should use. + * @param serviceContainer The service container for the services required by this class. + * @param props The properties that the custom endpoint plugin should use. */ - public CustomEndpointPlugin(final PluginService pluginService, final Properties props) { + public CustomEndpointPlugin(final ServiceContainer serviceContainer, final Properties props) { this( - pluginService, + serviceContainer, props, (hostSpec, region) -> RdsClient.builder() @@ -120,15 +125,19 @@ public CustomEndpointPlugin(final PluginService pluginService, final Properties /** * Constructs a new CustomEndpointPlugin instance. * - * @param pluginService The plugin service that the custom endpoint plugin should use. - * @param props The properties that the custom endpoint plugin should use. - * @param rdsClientFunc The function to call to obtain an {@link RdsClient} instance. + * @param serviceContainer The service container for the services required by this class. + * @param props The properties that the custom endpoint plugin should use. + * @param rdsClientFunc The function to call to obtain an {@link RdsClient} instance. */ public CustomEndpointPlugin( - final PluginService pluginService, + final ServiceContainer serviceContainer, final Properties props, final BiFunction rdsClientFunc) { - this.pluginService = pluginService; + this.serviceContainer = serviceContainer; + this.storageService = serviceContainer.getStorageService(); + this.monitorService = serviceContainer.getMonitorService(); + this.telemetryFactory = serviceContainer.getTelemetryFactory(); + this.props = props; this.rdsClientFunc = rdsClientFunc; @@ -136,7 +145,7 @@ public CustomEndpointPlugin( this.waitOnCachedInfoDurationMs = WAIT_FOR_CUSTOM_ENDPOINT_INFO_TIMEOUT_MS.getInteger(this.props); this.idleMonitorExpirationMs = CUSTOM_ENDPOINT_MONITOR_IDLE_EXPIRATION_MS.getInteger(this.props); - TelemetryFactory telemetryFactory = pluginService.getTelemetryFactory(); + TelemetryFactory telemetryFactory = serviceContainer.getTelemetryFactory(); this.waitForInfoCounter = telemetryFactory.createCounter(TELEMETRY_WAIT_FOR_INFO_COUNTER); } @@ -160,7 +169,7 @@ public Connection connect( this.customEndpointHostSpec = hostSpec; LOGGER.finest( Messages.get( - "CustomEndpointPlugin.connectionRequestToCustomEndpoint", new Object[]{ hostSpec.getHost() })); + "CustomEndpointPlugin.connectionRequestToCustomEndpoint", new Object[] {hostSpec.getHost()})); this.customEndpointId = this.rdsUtils.getRdsClusterId(customEndpointHostSpec.getHost()); if (StringUtils.isNullOrEmpty(customEndpointId)) { @@ -195,13 +204,11 @@ public Connection connect( * @return {@link CustomEndpointMonitor} */ protected CustomEndpointMonitor createMonitorIfAbsent(Properties props) { - return this.pluginService.getServiceContainer().getMonitorService().runIfAbsent( + return this.monitorService.runIfAbsent( CustomEndpointMonitorImpl.class, - this.customEndpointHostSpec.getHost(), + this.customEndpointHostSpec.getUrl(), () -> new CustomEndpointMonitorImpl( - this.pluginService.getServiceContainer().getMonitorService(), - this.pluginService.getServiceContainer().getStorageService(), - this.pluginService.getTelemetryFactory(), + this.serviceContainer, this.customEndpointHostSpec, this.customEndpointId, this.region, @@ -211,7 +218,6 @@ protected CustomEndpointMonitor createMonitorIfAbsent(Properties props) { } - /** * If custom endpoint info does not exist for the current custom endpoint, waits a short time for the info to be * made available by the custom endpoint monitor. This is necessary so that other plugins can rely on accurate custom @@ -229,7 +235,7 @@ protected void waitForCustomEndpointInfo(CustomEndpointMonitor monitor) throws S LOGGER.fine( Messages.get( "CustomEndpointPlugin.waitingForCustomEndpointInfo", - new Object[]{ this.customEndpointHostSpec.getHost(), this.waitOnCachedInfoDurationMs })); + new Object[] {this.customEndpointHostSpec.getUrl(), this.waitOnCachedInfoDurationMs})); long waitForEndpointInfoTimeoutNano = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(this.waitOnCachedInfoDurationMs); @@ -243,13 +249,13 @@ protected void waitForCustomEndpointInfo(CustomEndpointMonitor monitor) throws S throw new SQLException( Messages.get( "CustomEndpointPlugin.interruptedThread", - new Object[]{ this.customEndpointHostSpec.getHost() })); + new Object[] {this.customEndpointHostSpec.getUrl()})); } if (!hasCustomEndpointInfo) { throw new SQLException( Messages.get("CustomEndpointPlugin.timedOutWaitingForCustomEndpointInfo", - new Object[]{this.waitOnCachedInfoDurationMs, this.customEndpointHostSpec.getHost()})); + new Object[] {this.waitOnCachedInfoDurationMs, this.customEndpointHostSpec.getUrl()})); } } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java index 6687b9395..bd1f40052 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java @@ -32,6 +32,6 @@ public ConnectionPlugin getInstance(final PluginService pluginService, final Pro throw new RuntimeException(Messages.get("CustomEndpointPluginFactory.awsSdkNotInClasspath")); } - return new CustomEndpointPlugin(pluginService, props); + return new CustomEndpointPlugin(pluginService.getServiceContainer(), props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java index fad5d6f5f..815093059 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java @@ -80,7 +80,7 @@ static class ConnectionStatus { /** * Store the monitoring configuration for a connection. * - * @param serviceContainer The service container for the services required by this class. + * @param serviceContainer The service container for the services required by this class.` * @param hostSpec The {@link HostSpec} of the server this {@link MonitorImpl} * instance is monitoring. * @param properties The {@link Properties} containing additional monitoring diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java index 308794078..02bf3e637 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java @@ -280,9 +280,9 @@ public List getReaderHostsByPriority(final List hosts) { hostsByPriority.addAll(downHostList); final int numOfReaders = activeReaders.size() + downHostList.size(); - if (writerHost != null && (numOfReaders == 0 - || this.pluginService.getDialect().getFailoverRestrictions() - .contains(FailoverRestriction.ENABLE_WRITER_IN_TASK_B))) { + final boolean enableWriterInTaskB = + this.pluginService.getDialect().getFailoverRestrictions().contains(FailoverRestriction.ENABLE_WRITER_IN_TASK_B); + if (writerHost != null && (numOfReaders == 0 || enableWriterInTaskB)) { hostsByPriority.add(writerHost); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPlugin.java index 2ec2ef9c5..d5c14d932 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPlugin.java @@ -34,6 +34,7 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.plugin.AbstractConnectionPlugin; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; public class LimitlessConnectionPlugin extends AbstractConnectionPlugin { @@ -82,17 +83,15 @@ public Set getSubscribedMethods() { return subscribedMethods; } - public LimitlessConnectionPlugin(final PluginService pluginService, final @NonNull Properties properties) { - this(pluginService, - properties, - () -> new LimitlessRouterServiceImpl(pluginService.getServiceContainer())); + public LimitlessConnectionPlugin(final ServiceContainer serviceContainer, final @NonNull Properties properties) { + this(serviceContainer, properties, () -> new LimitlessRouterServiceImpl(serviceContainer)); } public LimitlessConnectionPlugin( - final PluginService pluginService, + final @NonNull ServiceContainer serviceContainer, final @NonNull Properties properties, final @NonNull Supplier limitlessRouterServiceSupplier) { - this.pluginService = pluginService; + this.pluginService = serviceContainer.getPluginService(); this.properties = properties; this.limitlessRouterServiceSupplier = limitlessRouterServiceSupplier; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginFactory.java index 27542f9ed..e54a67f57 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginFactory.java @@ -24,6 +24,6 @@ public class LimitlessConnectionPluginFactory implements ConnectionPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - return new LimitlessConnectionPlugin(pluginService, props); + return new LimitlessConnectionPlugin(pluginService.getServiceContainer(), props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ShouldDisposeFunc.java b/wrapper/src/main/java/software/amazon/jdbc/util/ShouldDisposeFunc.java index 3cdaa035c..cb45c0ac1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ShouldDisposeFunc.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ShouldDisposeFunc.java @@ -17,8 +17,7 @@ package software.amazon.jdbc.util; /** - * An optional function defining the conditions under which an expired entry should be cleaned up - * at cleanup time. + * An optional function defining the conditions under which an expired entry should be cleaned up at cleanup time. * * @param the type of object being analyzed for disposal */ diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java index caa5926c4..b985d4307 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java @@ -27,7 +27,7 @@ public interface ConnectionService { * specific tasks such as monitoring a host's availability, checking the topology information for a cluster, etc. * * @param hostSpec the hostSpec containing the host information for the auxiliary connection. - * @param props the properties for the auxiliary connection. + * @param props the properties for the auxiliary connection. * @return a new connection to the given host using the given props. */ Connection createAuxiliaryConnection(HostSpec hostSpec, Properties props) throws SQLException; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index aac505dca..0f2b2f873 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -50,8 +50,8 @@ public ConnectionServiceImpl( public Connection createAuxiliaryConnection(HostSpec hostSpec, Properties props) throws SQLException { final Properties auxiliaryProps = PropertyUtils.copyProperties(props); final String databaseName = PropertyDefinition.DATABASE.getString(auxiliaryProps) != null - ? PropertyDefinition.DATABASE.getString(auxiliaryProps) - : ""; + ? PropertyDefinition.DATABASE.getString(auxiliaryProps) + : ""; final String connString = this.targetDriverProtocol + hostSpec.getUrl() + databaseName; PropertyDefinition.IS_AUXILIARY_CONNECTION.set(auxiliaryProps, "true"); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java index 0a9b236e2..a47bd0842 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/DataAccessEvent.java @@ -20,8 +20,8 @@ import org.checkerframework.checker.nullness.qual.NonNull; /** - * A class defining an occurrence of data access. The class specifies the class of the data that was accessed and the - * key for the data. + * A class defining a data access event. The class specifies the class of the data that was accessed and the key for the + * data. */ public class DataAccessEvent implements Event { protected @NonNull Class dataClass; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java index 335af6ca9..322ca2054 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java @@ -71,7 +71,7 @@ private void sendMessages() { public void subscribe(EventSubscriber subscriber, Set> eventClasses) { for (Class eventClass : eventClasses) { // ConcurrentHashMap.newKeySet() is the recommended way to get a concurrent set. - subscribers.compute(eventClass, (k, v) -> v == null ? ConcurrentHashMap.newKeySet() : v).add(subscriber); + subscribers.computeIfAbsent(eventClass, (k) -> ConcurrentHashMap.newKeySet()).add(subscriber); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 96f6f9d67..911ba3480 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -29,12 +29,11 @@ public interface MonitorService { * * @param monitorClass the class of the monitor, eg `CustomEndpointMonitorImpl.class`. * @param expirationTimeoutNanos how long a monitor should be stored without use before being considered expired, in - * nanoseconds. If the monitor is expired and shouldDisposeFunc returns `true`, the - * monitor will be stopped. + * nanoseconds. Expired monitors may be removed and stopped. * @param heartbeatTimeoutNanos a duration in nanoseconds defining the maximum amount of time that a monitor should * take between updating its last-updated timestamp. If a monitor has not updated its * last-updated timestamp within this duration it will be considered stuck. - * @param errorResponses a Set defining actions to take if the monitor is in an error state. + * @param errorResponses a {@link Set} defining actions to take if the monitor is stuck or in an error state. * @param producedDataClass the class of data produced by the monitor. */ void registerMonitorTypeIfAbsent( @@ -46,7 +45,7 @@ void registerMonitorTypeIfAbsent( /** * Creates and starts the given monitor if it does not already exist and stores it under the given monitor type and - * key. If the monitor already exists, its time-to-live duration will be renewed, even if it was already expired. + * key. If the monitor already exists, its expiration time will be renewed, even if it was already expired. * * @param monitorClass the class of the monitor, eg `CustomEndpointMonitorImpl.class`. * @param key the key for the monitor, eg @@ -57,7 +56,7 @@ void registerMonitorTypeIfAbsent( T runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier); /** - * Get the monitor stored at the given key. + * Gets the monitor stored at the given key. * * @param monitorClass the expected class of the monitor. * @param key the key for the monitor. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index feab1d630..2f8abdaa2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -61,15 +61,15 @@ public class MonitorServiceImpl implements MonitorService, EventSubscriber { static { Map, Supplier> suppliers = new HashMap<>(); - Set resetErrorResponse = + Set recreateOnError = new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)); MonitorSettings defaultSettings = new MonitorSettings( - TimeUnit.MINUTES.toNanos(5), TimeUnit.MINUTES.toNanos(1), resetErrorResponse); + TimeUnit.MINUTES.toNanos(5), TimeUnit.MINUTES.toNanos(1), recreateOnError); suppliers.put( CustomEndpointMonitorImpl.class, () -> new CacheContainer(defaultSettings, AllowedAndBlockedHosts.class)); MonitorSettings topologySettings = - new MonitorSettings(TimeUnit.MINUTES.toNanos(5), TimeUnit.MINUTES.toNanos(3), resetErrorResponse); + new MonitorSettings(TimeUnit.MINUTES.toNanos(5), TimeUnit.MINUTES.toNanos(3), recreateOnError); suppliers.put(ClusterTopologyMonitorImpl.class, () -> new CacheContainer(topologySettings, Topology.class)); defaultSuppliers = Collections.unmodifiableMap(suppliers); } @@ -141,7 +141,7 @@ protected void checkMonitors() { // Monitor has been inactive for longer than the inactive timeout and is considered stuck. LOGGER.fine( Messages.get("MonitorServiceImpl.monitorStuck", - new Object[] {removedItem.getMonitor(), TimeUnit.NANOSECONDS.toSeconds(inactiveTimeoutNanos)})); + new Object[] {removedItem.getMonitor(), TimeUnit.NANOSECONDS.toMillis(inactiveTimeoutNanos)})); handleMonitorError(container, key, removedItem); continue; } @@ -165,8 +165,8 @@ protected void handleMonitorError( Set errorResponses = cacheContainer.getSettings().getErrorResponses(); if (errorResponses.contains(MonitorErrorResponse.RECREATE)) { - LOGGER.fine(Messages.get("MonitorServiceImpl.restartingMonitor", new Object[] {monitor})); cacheContainer.getCache().computeIfAbsent(key, k -> { + LOGGER.fine(Messages.get("MonitorServiceImpl.recreatingMonitor", new Object[] {monitor})); MonitorItem newMonitorItem = new MonitorItem(errorMonitorItem.getMonitorSupplier()); newMonitorItem.getMonitor().start(); return newMonitorItem; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java index e97dafede..00d5c9e3e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java @@ -35,7 +35,7 @@ public class MonitorSettings { * @param inactiveTimeoutNanos a duration in nanoseconds defining the maximum amount of time that a monitor should * take between updating its last-updated timestamp. If a monitor has not updated its * last-updated timestamp within this duration it will be considered stuck. - * @param errorResponses a Set defining actions to take if the monitor is in an error state. + * @param errorResponses a {@link Set} defining actions to take if the monitor is in an error state. */ public MonitorSettings( long expirationTimeoutNanos, long inactiveTimeoutNanos, @NonNull Set errorResponses) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java index 9d57f233e..a0f2474a7 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorState.java @@ -17,7 +17,7 @@ package software.amazon.jdbc.util.monitoring; /** - * Represents the different states of a monitor. + * Represents the state of a monitor. */ public enum MonitorState { /** diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index 7eba6ed76..f14e0c947 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -26,6 +26,7 @@ import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.util.ItemDisposalFunc; +import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ShouldDisposeFunc; /** @@ -57,6 +58,7 @@ public ExpirationCache() { * @param timeToLiveNanos the duration that the item should sit in the cache before being considered expired, in * nanoseconds. * @param shouldDisposeFunc a function defining the conditions under which an expired entry should be cleaned up. + * If null is passed, the entry will always be cleaned up if it is expired. * @param itemDisposalFunc a function defining how to dispose of an item when it is removed. If null is passed, * the item will be removed without performing any additional operations. */ @@ -76,8 +78,8 @@ public ExpirationCache( * * @param key the key at which the value should be stored. * @param value the value to store. - * @return the previous value stored at the given key, or null if there was no previous value. If the previous value - * is expired it will be disposed and returned. + * @return the previous value stored at the given key, or null if there was no previous value. If there was a previous + * value it will also be disposed. */ public @Nullable V put( final K key, @@ -148,8 +150,8 @@ public ExpirationCache( /** * Retrieves the value stored at the given key. * - * @param key the key from which to retrieve the value. - * @return the value stored at the given key, or null if there is no existing value. + * @param key the key for the value. + * @return the value stored at the given key, or null if there is no existing value or the existing value is expired. */ public @Nullable V get(final K key) { final CacheItem cacheItem = cache.get(key); @@ -166,6 +168,12 @@ public ExpirationCache( return cacheItem.item; } + /** + * Indicates whether a non-expired value is stored at the given key. + * + * @param key the key for the value. + * @return true if there is a non-expired value stored at the given key, otherwise returns false. + */ public boolean exists(final K key) { final CacheItem cacheItem = cache.get(key); return cacheItem != null && !cacheItem.shouldCleanup(); @@ -203,7 +211,7 @@ public void removeExpiredEntries() { try { removeIfExpired(key); } catch (Exception ex) { - // ignore + LOGGER.fine(Messages.get("ExpirationCache.exceptionWhileRemovingEntry", new Object[] {key, value, ex})); } }); } @@ -294,7 +302,7 @@ protected CacheItem(final V item, final long expirationTimeNanos) { * Determines if a cache item should be cleaned up. An item should be cleaned up if it has past its expiration time * and the {@link ShouldDisposeFunc} (if defined) indicates that it should be cleaned up. * - * @return true if the cache item should be cleaned up at cleanup time. Otherwise, returns false. + * @return true if the cache item should be cleaned up. Otherwise, returns false. */ protected boolean shouldCleanup() { final boolean isExpired = this.expirationTimeNanos != 0 && System.nanoTime() > this.expirationTimeNanos; @@ -326,7 +334,6 @@ public boolean equals(Object obj) { return true; } - // First check null and type (use instanceof for correct type checking) if (obj == null || getClass() != obj.getClass()) { return false; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java index faa60b7c3..5173ee6c0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java @@ -45,8 +45,8 @@ public class ExternallyManagedCache { /** * Constructs an externally managed cache. * - * @param timeToLiveNanos the duration that the item should sit in the cache before being considered expired, in - * nanoseconds. + * @param timeToLiveNanos the duration that the item should sit in the cache before being considered expired, in + * nanoseconds. */ public ExternallyManagedCache(long timeToLiveNanos) { this.timeToLiveNanos = timeToLiveNanos; @@ -72,7 +72,7 @@ public ExternallyManagedCache(long timeToLiveNanos) { * Get the value stored at the given key. If the value is expired, null will be returned. * * @param key the key for the value. - * @return the value stored at the given key, or null if the value is expired. + * @return the value stored at the given key, or null if the key does not exist or the value is expired. */ public @Nullable V get(@NonNull K key) { CacheItem cacheItem = this.cache.get(key); @@ -124,7 +124,7 @@ public void extendExpiration(K key) { if (cacheItem != null) { cacheItem.extendExpiration(); } else { - LOGGER.finest(Messages.get("ExternallyManagedCache.extendExpirationOnNonExistingKey", new Object[]{key})); + LOGGER.finest(Messages.get("ExternallyManagedCache.extendExpirationOnNonExistingKey", new Object[] {key})); } } @@ -229,7 +229,7 @@ protected class CacheItem { * Constructs a CacheItem. * * @param item the item value. - * @param expirationTimeNanos the amount of time before a CacheItem should be marked as expired. + * @param expirationTimeNanos the time at which the CacheItem should be considered expired. */ protected CacheItem(@NonNull final V item, final long expirationTimeNanos) { this.item = item; @@ -267,7 +267,6 @@ public boolean equals(Object obj) { return true; } - // First check null and type (use instanceof for correct type checking) if (obj == null || getClass() != obj.getClass()) { return false; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java index 147938b20..d280b41f6 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java @@ -27,15 +27,15 @@ public interface StorageService { * items to the service, so that the service knows when and how to dispose of the item. Expected item classes will be * added automatically during driver initialization, but this method can be called to add new classes of items. * - * @param itemClass the class of the item that will be stored, eg `CustomEndpointInfo.class`. - * @param isRenewableExpiration controls whether the item's expiration should be renewed if the item is fetched, - * regardless of whether it is already expired or not. - * @param timeToLiveNanos how long an item should be stored before being considered expired, in nanoseconds. - * @param shouldDisposeFunc a function defining whether an item should be disposed if expired. If null is passed, - * the item will always be disposed if expired. - * @param itemDisposalFunc a function defining how to dispose of an item when it is removed. If null is - * passed, the item will be removed without performing any additional operations. - * @param the type of item that will be stored under the item class. + * @param itemClass the class of the item that will be stored, eg `CustomEndpointInfo.class`. + * @param isRenewableExpiration controls whether the item's expiration should be renewed if the item is fetched, + * regardless of whether it is already expired or not. + * @param timeToLiveNanos how long an item should be stored before being considered expired, in nanoseconds. + * @param shouldDisposeFunc a function defining whether an item should be disposed if expired. If null is passed, + * the item will always be disposed if expired. + * @param itemDisposalFunc a function defining how to dispose of an item when it is removed. If null is + * passed, the item will be removed without performing any additional operations. + * @param the type of item that will be stored under the item class. */ void registerItemClassIfAbsent( Class itemClass, @@ -47,18 +47,18 @@ void registerItemClassIfAbsent( /** * Stores an item in the storage service under the given item class. * - * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com". - * @param item the item to store. - * @param the type of the item being retrieved. + * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". + * @param item the item to store. + * @param the type of the item being retrieved. */ void set(Object key, V item); /** * Gets an item stored in the storage service. * - * @param itemClass the expected class of the item being retrieved, eg `CustomEndpointInfo.class`. - * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com". - * @param the type of the item being retrieved. + * @param itemClass the expected class of the item being retrieved, eg `CustomEndpointInfo.class`. + * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". + * @param the type of the item being retrieved. * @return the item stored at the given key for the given item class. */ @Nullable V get(Class itemClass, Object key); @@ -66,8 +66,8 @@ void registerItemClassIfAbsent( /** * Indicates whether an item exists under the given item class and key. * - * @param itemClass the class of the item. - * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com". + * @param itemClass the class of the item. + * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". * @return true if the item exists under the given item class and key, otherwise returns false. */ boolean exists(Class itemClass, Object key); @@ -75,8 +75,8 @@ void registerItemClassIfAbsent( /** * Removes an item stored under the given item class. * - * @param itemClass the class of the item. - * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com". + * @param itemClass the class of the item. + * @param key the key for the item, eg "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". */ void remove(Class itemClass, Object key); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 6b36ef0df..23087e4ec 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -106,7 +106,7 @@ public void registerItemClassIfAbsent( @Nullable ItemDisposalFunc itemDisposalFunc) { caches.computeIfAbsent( itemClass, - category -> new ExpirationCache<>( + k -> new ExpirationCache<>( isRenewableExpiration, timeToLiveNanos, shouldDisposeFunc, @@ -157,7 +157,7 @@ public void set(Object key, V value) { LOGGER.fine( Messages.get( "StorageServiceImpl.itemClassMismatch", - new Object[]{key, itemClass, value, value.getClass()})); + new Object[] {key, itemClass, value, value.getClass()})); return null; } @@ -174,11 +174,9 @@ public boolean exists(Class itemClass, Object key) { @Override public void remove(Class itemClass, Object key) { final ExpirationCache cache = caches.get(itemClass); - if (cache == null) { - return; + if (cache != null) { + cache.remove(key); } - - cache.remove(key); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/Topology.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/Topology.java index 36ced73e4..bdeec0792 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/Topology.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/Topology.java @@ -17,16 +17,17 @@ package software.amazon.jdbc.util.storage; import java.util.List; +import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.HostSpec; public class Topology { - private final List hosts; + private final @NonNull List hosts; - public Topology(List hosts) { + public Topology(@NonNull List hosts) { this.hosts = hosts; } - public List getHosts() { + public @NonNull List getHosts() { return hosts; } } diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index a20e35ea3..6b087e7c0 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -91,8 +91,6 @@ ClusterAwareWriterFailoverHandler.taskAEncounteredException=[TaskA] encountered ClusterAwareWriterFailoverHandler.standaloneNode=[TaskB] Host {0} is not yet connected to a cluster. The cluster is still being reconfigured. ClusterAwareWriterFailoverHandler.alreadyWriter=Current reader connection is actually a new writer connection. -ConnectionPluginChainBuilder.removedNonAuxiliaryPlugins=The requested connection is an auxiliary connection, so the following non-auxiliary plugin factories were removed: ''{0}''. The following auxiliary plugin factories will be used: ''{1}''. - # Connection String Host List Provider ConnectionStringHostListProvider.parsedListEmpty=Can''t parse connection string: ''{0}''. ConnectionStringHostListProvider.unsupportedIdentifyConnection=ConnectionStringHostListProvider does not support identifyConnection. @@ -172,7 +170,9 @@ DataSource.failedToSetProperty=Failed to set property ''{0}'' on target datasour # Execution Time Connection Plugin ExecutionTimeConnectionPlugin.executionTime=Executed {0} in {1} nanos. -ExternallyManagedCache.extendExpirationOnNonExistingKey=A request was made to extend the expiration of the entry at key {0}, but the key does not exist. +ExpirationCache.exceptionWhileRemovingEntry=An exception occurred while removing entry with key ''{0}'' and value ''{1}'': ''{2}''. + +ExternallyManagedCache.extendExpirationOnNonExistingKey=A request was made to extend the expiration of the entry at key ''{0}'', but the key does not exist. # Failover Connection Plugin Failover.transactionResolutionUnknownError=Transaction resolution unknown. Please re-configure session state if required and try restarting the transaction. @@ -299,13 +299,13 @@ MonitorImpl.stopMonitoringThread=Stop monitoring thread for {0}. MonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should not be empty. # monitoring.MonitorServiceImpl -MonitorServiceImpl.monitorClassMismatch=The monitor stored at ''{0}'' did not have the expected type. The expected type was ''{1}'', but the monitor ''{2}'' had a type of ''{3}''. . +MonitorServiceImpl.monitorClassMismatch=The monitor stored at ''{0}'' did not have the expected type. The expected type was ''{1}'', but the monitor ''{2}'' had a type of ''{3}''. MonitorServiceImpl.monitorErrorForMissingMonitor=Monitor ''{0}'' reported an error to the monitor service, but the monitor service could not find the monitor under its registered monitors. Reported error: ''{1}''. MonitorServiceImpl.monitorErrorForUnregisteredType=Monitor ''{0}'' reported an error to the monitor service, but the monitor type has not been registered. Reported error: ''{1}''. -MonitorServiceImpl.monitorStuck=Monitor ''{0}'' has not been updated within the inactive timeout of {1} seconds. +MonitorServiceImpl.monitorStuck=Monitor ''{0}'' has not been updated within the inactive timeout of {1} milliseconds. MonitorServiceImpl.monitorTypeNotRegistered=The given monitor class ''{0}'' is not registered. Please register the monitor class before running monitors of that class with the monitor service. MonitorServiceImpl.removedExpiredMonitor=Removed expired monitor: ''{0}''. -MonitorServiceImpl.restartingMonitor=Restarting monitor: ''{0}''. +MonitorServiceImpl.recreatingMonitor=Recreating monitor: ''{0}''. MonitorServiceImpl.stopAndRemoveMissingMonitorType=The monitor service received a request to stop a monitor with type ''{0}'' and key ''{1}'', but the monitor service does not have any monitors registered under the given type. Please ensure monitors are registered under the correct type. MonitorServiceImpl.stopAndRemoveMonitorsMissingType=The monitor service received a request to stop all monitors with type ''{0}'', but the monitor service does not have any monitors registered under the given type. Please ensure monitors are registered under the correct type. MonitorServiceImpl.unexpectedMonitorClass=Monitor type mismatch - the monitor ''{0}'' was unexpectedly found under the ''{1}'' monitor class category. Please verify that monitors are submitted under their concrete class. @@ -368,8 +368,8 @@ SAMLCredentialsProviderFactory.getSamlAssertionFailed=Failed to get SAML Asserti SamlAuthPlugin.javaStsSdkNotInClasspath=Required dependency 'AWS Java SDK for AWS Secret Token Service' is not on the classpath. SamlAuthPlugin.unhandledException=Unhandled exception: ''{0}'' -StorageServiceImpl.unexpectedValueMismatch=Attempted to store value ''{0}'' under item class ''{1}'' but there was an unexpected mismatch between the passed in value type and the expected value type. Item bin for item class ''{1}'': ''{2}''. -StorageServiceImpl.itemClassNotRegistered=The given item category ''{0}'' is not registered. Please register the item category before storing items under the category. +StorageServiceImpl.unexpectedValueMismatch=Attempted to store value ''{0}'' under item class ''{1}'' but there was an unexpected mismatch between the passed in value type and the expected value type. The cache for item class ''{1}'' is ''{2}''. +StorageServiceImpl.itemClassNotRegistered=The given item class ''{0}'' is not registered. Please register the item class before storing items of that class. StorageServiceImpl.itemClassMismatch=The item stored at ''{0}'' did not have the expected type. The expected type was ''{1}'', but the stored item ''{2}'' had a type of ''{3}''. Returning null. StorageServiceImpl.removeExpiredItems=Removing expired items from the storage service... diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java index 599ca5b3b..d61734102 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java @@ -49,12 +49,14 @@ import software.amazon.jdbc.HostSpecBuilder; import software.amazon.jdbc.hostavailability.HostAvailabilityStrategy; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class CustomEndpointMonitorImplTest { + @Mock private ServiceContainer mockServiceContainer; @Mock private MonitorService mockMonitorService; @Mock private StorageService mockStorageService; @Mock private BiFunction mockRdsClientFunc; @@ -94,6 +96,10 @@ public void init() throws SQLException { twoEndpointList = Arrays.asList(mockClusterEndpoint1, mockClusterEndpoint2); oneEndpointList = Collections.singletonList(mockClusterEndpoint1); + when(mockServiceContainer.getStorageService()).thenReturn(mockStorageService); + when(mockServiceContainer.getMonitorService()).thenReturn(mockMonitorService); + when(mockServiceContainer.getTelemetryFactory()).thenReturn(mockTelemetryFactory); + when(mockTelemetryFactory.createCounter(any(String.class))).thenReturn(mockTelemetryCounter); when(mockTelemetryFactory.createCounter(any(String.class))).thenReturn(mockTelemetryCounter); when(mockRdsClientFunc.apply(any(HostSpec.class), any(Region.class))).thenReturn(mockRdsClient); when(mockRdsClient.describeDBClusterEndpoints(any(Consumer.class))).thenReturn(mockDescribeResponse); @@ -115,9 +121,7 @@ void cleanUp() throws Exception { @Test public void testRun() throws InterruptedException { CustomEndpointMonitorImpl monitor = new CustomEndpointMonitorImpl( - mockMonitorService, - mockStorageService, - mockTelemetryFactory, + mockServiceContainer, host, endpointId, Region.US_EAST_1, @@ -128,11 +132,11 @@ public void testRun() throws InterruptedException { // Wait for 2 run cycles. The first will return an unexpected number of endpoints in the API response, the second // will return the expected number of endpoints (one). TimeUnit.MILLISECONDS.sleep(100); - assertEquals(expectedInfo, CustomEndpointMonitorImpl.customEndpointInfoCache.get(host.getHost())); + assertEquals(expectedInfo, CustomEndpointMonitorImpl.customEndpointInfoCache.get(host.getUrl())); monitor.stop(); ArgumentCaptor captor = ArgumentCaptor.forClass(AllowedAndBlockedHosts.class); - verify(mockStorageService).set(eq(host.getHost()), captor.capture()); + verify(mockStorageService).set(eq(host.getUrl()), captor.capture()); assertEquals(staticMembersSet, captor.getValue().getAllowedHostIds()); assertNull(captor.getValue().getBlockedHostIds()); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java index 9595687cf..0cce3e375 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java @@ -45,6 +45,7 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.hostavailability.HostAvailabilityStrategy; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -59,6 +60,7 @@ public class CustomEndpointPluginTest { private final HostSpec writerClusterHost = hostSpecBuilder.host(writerClusterUrl).build(); private final HostSpec host = hostSpecBuilder.host(customEndpointUrl).build(); + @Mock private ServiceContainer mockServiceContainer; @Mock private PluginService mockPluginService; @Mock private BiFunction mockRdsClientFunc; @Mock private TelemetryFactory mockTelemetryFactory; @@ -72,7 +74,8 @@ public class CustomEndpointPluginTest { public void init() throws SQLException { closeable = MockitoAnnotations.openMocks(this); - when(mockPluginService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); + when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); + when(mockServiceContainer.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockTelemetryFactory.createCounter(any(String.class))).thenReturn(mockTelemetryCounter); when(mockMonitor.hasCustomEndpointInfo()).thenReturn(true); } @@ -84,7 +87,7 @@ void cleanUp() throws Exception { } private CustomEndpointPlugin getSpyPlugin() { - CustomEndpointPlugin plugin = new CustomEndpointPlugin(mockPluginService, props, mockRdsClientFunc); + CustomEndpointPlugin plugin = new CustomEndpointPlugin(mockServiceContainer, props, mockRdsClientFunc); CustomEndpointPlugin spyPlugin = spy(plugin); doReturn(mockMonitor).when(spyPlugin).createMonitorIfAbsent(any(Properties.class)); return spyPlugin; diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java index c0452273d..2fff232b4 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java @@ -84,7 +84,6 @@ void setUp() { closeable = MockitoAnnotations.openMocks(this); when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); when(mockServiceContainer.getConnectionService()).thenReturn(mockConnectionService); - when(mockPluginService.getServiceContainer()).thenReturn(mockServiceContainer); writer.addAlias("writer-host"); newWriterHost.addAlias("new-writer-host"); readerA.addAlias("reader-a-host"); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginTest.java index 411233100..aeaa5a1ad 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginTest.java @@ -45,6 +45,7 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.dialect.PgDialect; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; +import software.amazon.jdbc.util.ServiceContainer; public class LimitlessConnectionPluginTest { @@ -58,21 +59,20 @@ public class LimitlessConnectionPluginTest { private static final Dialect supportedDialect = new AuroraPgDialect(); @Mock JdbcCallable mockConnectFuncLambda; @Mock private Connection mockConnection; + @Mock private ServiceContainer mockServiceContainer; @Mock private PluginService mockPluginService; @Mock private HostListProvider mockHostListProvider; @Mock private LimitlessRouterService mockLimitlessRouterService; private static Properties props; - private static LimitlessConnectionPlugin plugin; - private AutoCloseable closeable; @BeforeEach public void init() throws SQLException { closeable = MockitoAnnotations.openMocks(this); props = new Properties(); - plugin = new LimitlessConnectionPlugin(mockPluginService, props, () -> mockLimitlessRouterService); + when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); when(mockPluginService.getHostListProvider()).thenReturn(mockHostListProvider); when(mockPluginService.getDialect()).thenReturn(supportedDialect); when(mockHostListProvider.getClusterId()).thenReturn(CLUSTER_ID); @@ -94,6 +94,8 @@ public Void answer(InvocationOnMock invocation) { } }).when(mockLimitlessRouterService).establishConnection(any()); + final LimitlessConnectionPlugin plugin = + new LimitlessConnectionPlugin(mockServiceContainer, props, () -> mockLimitlessRouterService); final Connection expectedConnection = mockConnection; final Connection actualConnection = plugin.connect(DRIVER_PROTOCOL, INPUT_HOST_SPEC, props, true, mockConnectFuncLambda); @@ -116,6 +118,8 @@ public Void answer(InvocationOnMock invocation) { } }).when(mockLimitlessRouterService).establishConnection(any()); + final LimitlessConnectionPlugin plugin = + new LimitlessConnectionPlugin(mockServiceContainer, props, () -> mockLimitlessRouterService); assertThrows( SQLException.class, () -> plugin.connect(DRIVER_PROTOCOL, INPUT_HOST_SPEC, props, true, mockConnectFuncLambda)); @@ -132,6 +136,8 @@ void testConnectGivenUnsupportedDialect() throws SQLException { final Dialect unsupportedDialect = new PgDialect(); when(mockPluginService.getDialect()).thenReturn(unsupportedDialect, unsupportedDialect); + final LimitlessConnectionPlugin plugin = + new LimitlessConnectionPlugin(mockServiceContainer, props, () -> mockLimitlessRouterService); assertThrows( UnsupportedOperationException.class, () -> plugin.connect(DRIVER_PROTOCOL, INPUT_HOST_SPEC, props, true, mockConnectFuncLambda)); @@ -148,6 +154,8 @@ void testConnectGivenSupportedDialectAfterRefresh() throws SQLException { final Dialect unsupportedDialect = new PgDialect(); when(mockPluginService.getDialect()).thenReturn(unsupportedDialect, supportedDialect); + final LimitlessConnectionPlugin plugin = + new LimitlessConnectionPlugin(mockServiceContainer, props, () -> mockLimitlessRouterService); final Connection expectedConnection = mockConnection; final Connection actualConnection = plugin.connect(DRIVER_PROTOCOL, INPUT_HOST_SPEC, props, true, mockConnectFuncLambda); From 28bdffcb5039a97cb04ac7223527f53b1eb437f0 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 25 Apr 2025 10:36:40 -0700 Subject: [PATCH 076/149] cleanup --- .../main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java index 815093059..fad5d6f5f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java @@ -80,7 +80,7 @@ static class ConnectionStatus { /** * Store the monitoring configuration for a connection. * - * @param serviceContainer The service container for the services required by this class.` + * @param serviceContainer The service container for the services required by this class. * @param hostSpec The {@link HostSpec} of the server this {@link MonitorImpl} * instance is monitoring. * @param properties The {@link Properties} containing additional monitoring From 834e44df03fb7b44eb5204b166d5970e76efc4c4 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 25 Apr 2025 12:28:34 -0700 Subject: [PATCH 077/149] Move old cache classes into util.storage package --- .../software/amazon/jdbc/C3P0PooledConnectionProvider.java | 2 +- .../software/amazon/jdbc/HikariPooledConnectionProvider.java | 2 +- .../main/java/software/amazon/jdbc/HikariPoolsHolder.java | 2 +- .../software/amazon/jdbc/LeastConnectionsHostSelector.java | 2 +- .../software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java | 2 +- .../amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java | 2 +- .../plugin/limitless/LimitlessRouterMonitorInitializer.java | 5 +---- .../jdbc/plugin/limitless/LimitlessRouterServiceImpl.java | 2 +- .../fastestresponse/HostResponseTimeServiceImpl.java | 2 +- .../software/amazon/jdbc/util/storage/ExpirationCache.java | 2 -- .../amazon/jdbc/util/{ => storage}/ItemDisposalFunc.java | 2 +- .../amazon/jdbc/util/{ => storage}/ShouldDisposeFunc.java | 2 +- .../jdbc/util/{ => storage}/SlidingExpirationCache.java | 2 +- .../SlidingExpirationCacheWithCleanupThread.java | 2 +- .../software/amazon/jdbc/util/storage/StorageService.java | 2 -- .../amazon/jdbc/util/storage/StorageServiceImpl.java | 2 -- .../amazon/jdbc/HikariPooledConnectionProviderTest.java | 2 +- .../jdbc/util/{ => storage}/SlidingExpirationCacheTest.java | 2 +- 18 files changed, 15 insertions(+), 24 deletions(-) rename wrapper/src/main/java/software/amazon/jdbc/util/{ => storage}/ItemDisposalFunc.java (95%) rename wrapper/src/main/java/software/amazon/jdbc/util/{ => storage}/ShouldDisposeFunc.java (95%) rename wrapper/src/main/java/software/amazon/jdbc/util/{ => storage}/SlidingExpirationCache.java (99%) rename wrapper/src/main/java/software/amazon/jdbc/util/{ => storage}/SlidingExpirationCacheWithCleanupThread.java (98%) rename wrapper/src/test/java/software/amazon/jdbc/util/{ => storage}/SlidingExpirationCacheTest.java (99%) diff --git a/wrapper/src/main/java/software/amazon/jdbc/C3P0PooledConnectionProvider.java b/wrapper/src/main/java/software/amazon/jdbc/C3P0PooledConnectionProvider.java index 466e61e64..5694f5bd5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/C3P0PooledConnectionProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/C3P0PooledConnectionProvider.java @@ -35,7 +35,7 @@ import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; -import software.amazon.jdbc.util.SlidingExpirationCache; +import software.amazon.jdbc.util.storage.SlidingExpirationCache; import software.amazon.jdbc.wrapper.HighestWeightHostSelector; public class C3P0PooledConnectionProvider implements PooledConnectionProvider, CanReleaseResources { diff --git a/wrapper/src/main/java/software/amazon/jdbc/HikariPooledConnectionProvider.java b/wrapper/src/main/java/software/amazon/jdbc/HikariPooledConnectionProvider.java index dd6f6e741..8638fbb17 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/HikariPooledConnectionProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/HikariPooledConnectionProvider.java @@ -43,7 +43,7 @@ import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; -import software.amazon.jdbc.util.SlidingExpirationCache; +import software.amazon.jdbc.util.storage.SlidingExpirationCache; import software.amazon.jdbc.wrapper.HighestWeightHostSelector; public class HikariPooledConnectionProvider implements PooledConnectionProvider, diff --git a/wrapper/src/main/java/software/amazon/jdbc/HikariPoolsHolder.java b/wrapper/src/main/java/software/amazon/jdbc/HikariPoolsHolder.java index 7bec68a49..7eb2389e3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/HikariPoolsHolder.java +++ b/wrapper/src/main/java/software/amazon/jdbc/HikariPoolsHolder.java @@ -17,7 +17,7 @@ package software.amazon.jdbc; import software.amazon.jdbc.util.Pair; -import software.amazon.jdbc.util.SlidingExpirationCache; +import software.amazon.jdbc.util.storage.SlidingExpirationCache; public class HikariPoolsHolder { static SlidingExpirationCache databasePools = diff --git a/wrapper/src/main/java/software/amazon/jdbc/LeastConnectionsHostSelector.java b/wrapper/src/main/java/software/amazon/jdbc/LeastConnectionsHostSelector.java index 2ada66281..88b90624c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/LeastConnectionsHostSelector.java +++ b/wrapper/src/main/java/software/amazon/jdbc/LeastConnectionsHostSelector.java @@ -27,7 +27,7 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Pair; -import software.amazon.jdbc.util.SlidingExpirationCache; +import software.amazon.jdbc.util.storage.SlidingExpirationCache; public class LeastConnectionsHostSelector implements HostSelector { public static final String STRATEGY_LEAST_CONNECTIONS = "leastConnections"; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java index 06879c6b2..9399eb398 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java @@ -29,7 +29,7 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; +import software.amazon.jdbc.util.storage.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java index 5246fa30a..0e493a961 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java @@ -31,7 +31,7 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; +import software.amazon.jdbc.util.storage.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.telemetry.TelemetryContext; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitorInitializer.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitorInitializer.java index d036f2d42..03680ffa1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitorInitializer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitorInitializer.java @@ -18,11 +18,8 @@ import java.util.List; import java.util.Properties; -import java.util.concurrent.atomic.AtomicReference; -import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; +import software.amazon.jdbc.util.storage.SlidingExpirationCacheWithCleanupThread; @FunctionalInterface public interface LimitlessRouterMonitorInitializer { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java index 2fb24432d..c7f2c4141 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java @@ -33,7 +33,7 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; +import software.amazon.jdbc.util.storage.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.wrapper.HighestWeightHostSelector; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/HostResponseTimeServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/HostResponseTimeServiceImpl.java index b77dd8ba0..acfc3ac73 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/HostResponseTimeServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/HostResponseTimeServiceImpl.java @@ -27,7 +27,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.SlidingExpirationCacheWithCleanupThread; +import software.amazon.jdbc.util.storage.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryGauge; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index f14e0c947..ccd86f2e2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -25,9 +25,7 @@ import java.util.function.Function; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; -import software.amazon.jdbc.util.ItemDisposalFunc; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ShouldDisposeFunc; /** * A cache that can be used to store values that expire after a configured period of time. Entries are disposed when diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ItemDisposalFunc.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemDisposalFunc.java similarity index 95% rename from wrapper/src/main/java/software/amazon/jdbc/util/ItemDisposalFunc.java rename to wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemDisposalFunc.java index f0ad7ac39..7bf7b3f4f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ItemDisposalFunc.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ItemDisposalFunc.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package software.amazon.jdbc.util; +package software.amazon.jdbc.util.storage; /** * An optional function defining extra cleanup steps to take when a cache item is cleaned up. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ShouldDisposeFunc.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ShouldDisposeFunc.java similarity index 95% rename from wrapper/src/main/java/software/amazon/jdbc/util/ShouldDisposeFunc.java rename to wrapper/src/main/java/software/amazon/jdbc/util/storage/ShouldDisposeFunc.java index cb45c0ac1..eb9683f84 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ShouldDisposeFunc.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ShouldDisposeFunc.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package software.amazon.jdbc.util; +package software.amazon.jdbc.util.storage; /** * An optional function defining the conditions under which an expired entry should be cleaned up at cleanup time. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/SlidingExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCache.java similarity index 99% rename from wrapper/src/main/java/software/amazon/jdbc/util/SlidingExpirationCache.java rename to wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCache.java index 7ba3650c3..ea3870f2e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/SlidingExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCache.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package software.amazon.jdbc.util; +package software.amazon.jdbc.util.storage; import java.util.ArrayList; import java.util.HashMap; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/SlidingExpirationCacheWithCleanupThread.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCacheWithCleanupThread.java similarity index 98% rename from wrapper/src/main/java/software/amazon/jdbc/util/SlidingExpirationCacheWithCleanupThread.java rename to wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCacheWithCleanupThread.java index db505bc83..8ae7a2e43 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/SlidingExpirationCacheWithCleanupThread.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCacheWithCleanupThread.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package software.amazon.jdbc.util; +package software.amazon.jdbc.util.storage; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java index d280b41f6..b97f7871e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java @@ -18,8 +18,6 @@ import java.util.Map; import org.checkerframework.checker.nullness.qual.Nullable; -import software.amazon.jdbc.util.ItemDisposalFunc; -import software.amazon.jdbc.util.ShouldDisposeFunc; public interface StorageService { /** diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 23087e4ec..737b922e4 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -29,9 +29,7 @@ import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.AllowedAndBlockedHosts; -import software.amazon.jdbc.util.ItemDisposalFunc; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ShouldDisposeFunc; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.events.DataAccessEvent; import software.amazon.jdbc.util.events.EventPublisher; diff --git a/wrapper/src/test/java/software/amazon/jdbc/HikariPooledConnectionProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/HikariPooledConnectionProviderTest.java index 1b7e4a431..6e5844ccf 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/HikariPooledConnectionProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/HikariPooledConnectionProviderTest.java @@ -50,7 +50,7 @@ import software.amazon.jdbc.targetdriverdialect.ConnectInfo; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.Pair; -import software.amazon.jdbc.util.SlidingExpirationCache; +import software.amazon.jdbc.util.storage.SlidingExpirationCache; class HikariPooledConnectionProviderTest { @Mock Connection mockConnection; diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/SlidingExpirationCacheTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/storage/SlidingExpirationCacheTest.java similarity index 99% rename from wrapper/src/test/java/software/amazon/jdbc/util/SlidingExpirationCacheTest.java rename to wrapper/src/test/java/software/amazon/jdbc/util/storage/SlidingExpirationCacheTest.java index f9f265212..26b105c23 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/util/SlidingExpirationCacheTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/util/storage/SlidingExpirationCacheTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package software.amazon.jdbc.util; +package software.amazon.jdbc.util.storage; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; From 0deeeb6549f04f9a13e71df82cd414ec53e1c82a Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 25 Apr 2025 13:03:18 -0700 Subject: [PATCH 078/149] cleanup --- .../amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java | 2 +- .../amazon/jdbc/plugin/customendpoint/MemberListType.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java index 81e4f4eeb..37a127c42 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java @@ -169,7 +169,7 @@ public Connection connect( this.customEndpointHostSpec = hostSpec; LOGGER.finest( Messages.get( - "CustomEndpointPlugin.connectionRequestToCustomEndpoint", new Object[] {hostSpec.getHost()})); + "CustomEndpointPlugin.connectionRequestToCustomEndpoint", new Object[] {hostSpec.getUrl()})); this.customEndpointId = this.rdsUtils.getRdsClusterId(customEndpointHostSpec.getHost()); if (StringUtils.isNullOrEmpty(customEndpointId)) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/MemberListType.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/MemberListType.java index 298c28069..8a30bbecf 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/MemberListType.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/MemberListType.java @@ -17,7 +17,7 @@ package software.amazon.jdbc.plugin.customendpoint; /** - * Enum representing the member list type of a custom endpoint. This information can be used together with a member list + * Enum representing the member list type of custom endpoint. This information can be used together with a member list * to determine which instances are included or excluded from a custom endpoint. */ public enum MemberListType { From 117bac587e7413081ee9324326368251e02f390c Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 25 Apr 2025 13:23:12 -0700 Subject: [PATCH 079/149] Fix bug where the allowed hosts were fetched using the host instead of the url --- .../src/main/java/software/amazon/jdbc/PluginServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index 89dba6b86..5821d1eaf 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -414,7 +414,7 @@ public List getAllHosts() { @Override public List getHosts() { AllowedAndBlockedHosts hostPermissions = this.storageService.get( - AllowedAndBlockedHosts.class, this.initialConnectionHostSpec.getHost()); + AllowedAndBlockedHosts.class, this.initialConnectionHostSpec.getUrl()); if (hostPermissions == null) { return this.allHosts; } From 5782d7a1ecb752c9421da56024b008e52779819e Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 25 Apr 2025 13:30:45 -0700 Subject: [PATCH 080/149] Fix checkstyle --- .../amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java | 2 +- .../jdbc/plugin/limitless/LimitlessRouterServiceImpl.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java index 0e493a961..99f3d411a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java @@ -31,9 +31,9 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.storage.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.connection.ConnectionService; +import software.amazon.jdbc.util.storage.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryTraceLevel; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java index c7f2c4141..4d6e380df 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java @@ -33,8 +33,8 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.storage.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.storage.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.wrapper.HighestWeightHostSelector; public class LimitlessRouterServiceImpl implements LimitlessRouterService { From 2ac66d481e2b10bf5d3653e7db160480c4b6f011 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 30 Apr 2025 09:13:31 -0700 Subject: [PATCH 081/149] Move CacheMap, create CacheItem --- .../amazon/jdbc/PluginServiceImpl.java | 2 +- .../amazon/jdbc/RoundRobinHostSelector.java | 2 +- .../amazon/jdbc/dialect/DialectManager.java | 2 +- .../hostlistprovider/RdsHostListProvider.java | 2 +- .../monitoring/ClusterTopologyMonitor.java | 2 +- .../CustomEndpointMonitorImpl.java | 2 +- .../FastestResponseStrategyPlugin.java | 2 +- .../amazon/jdbc/util/storage/CacheItem.java | 82 ++++++++++++++++++ .../jdbc/util/{ => storage}/CacheMap.java | 48 +---------- .../util/storage/ExternallyManagedCache.java | 83 +++---------------- 10 files changed, 100 insertions(+), 127 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java rename wrapper/src/main/java/software/amazon/jdbc/util/{ => storage}/CacheMap.java (73%) diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index 5821d1eaf..db6ac0f6f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -50,7 +50,7 @@ import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.states.SessionStateServiceImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.CacheMap; +import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; diff --git a/wrapper/src/main/java/software/amazon/jdbc/RoundRobinHostSelector.java b/wrapper/src/main/java/software/amazon/jdbc/RoundRobinHostSelector.java index b458677d6..c4538110f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/RoundRobinHostSelector.java +++ b/wrapper/src/main/java/software/amazon/jdbc/RoundRobinHostSelector.java @@ -30,7 +30,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.hostavailability.HostAvailability; -import software.amazon.jdbc.util.CacheMap; +import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.StringUtils; diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectManager.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectManager.java index bdc7a5134..f2747ae2b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectManager.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectManager.java @@ -30,7 +30,7 @@ import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PluginService; import software.amazon.jdbc.PropertyDefinition; -import software.amazon.jdbc.util.CacheMap; +import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index e1a5d515d..bc56eeee4 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -49,7 +49,7 @@ import software.amazon.jdbc.HostSpecBuilder; import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.hostavailability.HostAvailability; -import software.amazon.jdbc.util.CacheMap; +import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitor.java index 75ccb5ba9..73ea62399 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitor.java @@ -24,7 +24,7 @@ import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.util.monitoring.Monitor; -public interface ClusterTopologyMonitor extends Monitor, Runnable { +public interface ClusterTopologyMonitor extends Monitor { boolean canDispose(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index cfb97788c..d86ec56c5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -32,7 +32,7 @@ import software.amazon.awssdk.services.rds.model.Filter; import software.amazon.jdbc.AllowedAndBlockedHosts; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.util.CacheMap; +import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.monitoring.AbstractMonitor; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java index 0f9972b51..0a163f940 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java @@ -39,7 +39,7 @@ import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.RandomHostSelector; import software.amazon.jdbc.plugin.AbstractConnectionPlugin; -import software.amazon.jdbc.util.CacheMap; +import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.ServiceContainer; public class FastestResponseStrategyPlugin extends AbstractConnectionPlugin { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java new file mode 100644 index 000000000..ef7481cdc --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java @@ -0,0 +1,82 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.storage; + +import org.checkerframework.checker.nullness.qual.NonNull; + +/** + * A container class that holds a cache value together with the time at which the value should be considered expired. + */ +public class CacheItem { + protected final @NonNull V item; + protected long expirationTimeNanos; + + /** + * Constructs a CacheItem. + * + * @param item the item value. + * @param expirationTimeNanos the time at which the CacheItem should be considered expired. + */ + protected CacheItem(final @NonNull V item, final long expirationTimeNanos) { + this.item = item; + this.expirationTimeNanos = expirationTimeNanos; + } + + /** + * Indicates whether this item is expired. + * + * @return true if this item is expired, otherwise returns false. + */ + protected boolean isExpired() { + return System.currentTimeMillis() > expirationTimeNanos; + } + + /** + * Renews a cache item's expiration time. + */ + protected void extendExpiration(long timeToLiveNanos) { + this.expirationTimeNanos = System.nanoTime() + timeToLiveNanos; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + item.hashCode(); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final CacheItem other = (CacheItem) obj; + return item.equals(other.item); + } + + @Override + public String toString() { + return "CacheItem [item=" + item + ", expirationTimeNanos=" + expirationTimeNanos + "]"; + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CacheMap.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheMap.java similarity index 73% rename from wrapper/src/main/java/software/amazon/jdbc/util/CacheMap.java rename to wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheMap.java index 7d2e2dbbb..2bad26df2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CacheMap.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheMap.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package software.amazon.jdbc.util; +package software.amazon.jdbc.util.storage; import java.util.HashMap; import java.util.Map; @@ -93,50 +93,4 @@ protected void cleanUp() { }); } } - - static class CacheItem { - final V item; - final long expirationTime; - - public CacheItem(final V item, final long expirationTime) { - this.item = item; - this.expirationTime = expirationTime; - } - - boolean isExpired() { - return System.nanoTime() > expirationTime; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((item == null) ? 0 : item.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final CacheItem other = (CacheItem) obj; - if (item == null) { - return other.item == null; - } else { - return item.equals(other.item); - } - } - - @Override - public String toString() { - return "CacheItem [item=" + item + ", expirationTime=" + expirationTime + "]"; - } - } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java index 5173ee6c0..fb2fbe53d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExternallyManagedCache.java @@ -39,7 +39,7 @@ */ public class ExternallyManagedCache { private static final Logger LOGGER = Logger.getLogger(ExpirationCache.class.getName()); - protected final Map cache = new ConcurrentHashMap<>(); + protected final Map> cache = new ConcurrentHashMap<>(); protected final long timeToLiveNanos; /** @@ -60,7 +60,7 @@ public ExternallyManagedCache(long timeToLiveNanos) { * @return the previous value stored at the key, or null if there was no value stored at the key. */ public @Nullable V put(@NonNull K key, @NonNull V value) { - CacheItem cacheItem = this.cache.put(key, new CacheItem(value, System.nanoTime() + timeToLiveNanos)); + CacheItem cacheItem = this.cache.put(key, new CacheItem<>(value, System.nanoTime() + timeToLiveNanos)); if (cacheItem == null) { return null; } @@ -75,7 +75,7 @@ public ExternallyManagedCache(long timeToLiveNanos) { * @return the value stored at the given key, or null if the key does not exist or the value is expired. */ public @Nullable V get(@NonNull K key) { - CacheItem cacheItem = this.cache.get(key); + CacheItem cacheItem = this.cache.get(key); if (cacheItem == null) { return null; } @@ -97,17 +97,17 @@ public ExternallyManagedCache(long timeToLiveNanos) { * is null. */ public @NonNull V computeIfAbsent(K key, Function mappingFunction) { - final CacheItem cacheItem = cache.compute( + final CacheItem cacheItem = cache.compute( key, (k, valueItem) -> { if (valueItem == null) { // The key is absent; compute and store the new value. - return new CacheItem( + return new CacheItem<>( mappingFunction.apply(k), System.nanoTime() + this.timeToLiveNanos); } - valueItem.extendExpiration(); + valueItem.extendExpiration(this.timeToLiveNanos); return valueItem; }); @@ -120,9 +120,9 @@ public ExternallyManagedCache(long timeToLiveNanos) { * @param key the key for the value whose expiration should be extended. */ public void extendExpiration(K key) { - final CacheItem cacheItem = cache.get(key); + final CacheItem cacheItem = cache.get(key); if (cacheItem != null) { - cacheItem.extendExpiration(); + cacheItem.extendExpiration(this.timeToLiveNanos); } else { LOGGER.finest(Messages.get("ExternallyManagedCache.extendExpirationOnNonExistingKey", new Object[] {key})); } @@ -135,7 +135,7 @@ public void extendExpiration(K key) { * @return the value that was removed, or null if the key did not exist. */ public @Nullable V remove(K key) { - CacheItem cacheItem = cache.remove(key); + CacheItem cacheItem = cache.remove(key); if (cacheItem == null) { return null; } @@ -211,73 +211,10 @@ public void extendExpiration(K key) { */ public Map getEntries() { final Map entries = new HashMap<>(); - for (final Map.Entry entry : this.cache.entrySet()) { + for (final Map.Entry> entry : this.cache.entrySet()) { entries.put(entry.getKey(), entry.getValue().item); } return entries; } - - /** - * A container class that holds a cache value together with the time at which the value should be considered expired. - */ - protected class CacheItem { - private final @NonNull V item; - private long expirationTimeNanos; - - /** - * Constructs a CacheItem. - * - * @param item the item value. - * @param expirationTimeNanos the time at which the CacheItem should be considered expired. - */ - protected CacheItem(@NonNull final V item, final long expirationTimeNanos) { - this.item = item; - this.expirationTimeNanos = expirationTimeNanos; - } - - /** - * Indicates whether this item is expired. - * - * @return true if this item is expired, otherwise returns false. - */ - protected boolean isExpired() { - return System.nanoTime() >= expirationTimeNanos; - } - - /** - * Renews a cache item's expiration time. - */ - protected void extendExpiration() { - this.expirationTimeNanos = System.nanoTime() + timeToLiveNanos; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + item.hashCode(); - return result; - } - - @Override - @SuppressWarnings("unchecked") - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (obj == null || getClass() != obj.getClass()) { - return false; - } - - CacheItem other = (CacheItem) obj; - return this.item.equals(other.item) && this.expirationTimeNanos == other.expirationTimeNanos; - } - - @Override - public String toString() { - return "CacheItem [item=" + item + ", expirationTime=" + expirationTimeNanos + "]"; - } - } } From db51f3036434d033e08e81aac58c8b556f0ee68f Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 30 Apr 2025 09:30:38 -0700 Subject: [PATCH 082/149] Create DisposableCacheItem --- .../util/storage/DisposableCacheItem.java | 64 +++++++++++++ .../jdbc/util/storage/ExpirationCache.java | 92 +++---------------- .../util/storage/SlidingExpirationCache.java | 75 --------------- 3 files changed, 76 insertions(+), 155 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/storage/DisposableCacheItem.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/DisposableCacheItem.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/DisposableCacheItem.java new file mode 100644 index 000000000..de6ada278 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/DisposableCacheItem.java @@ -0,0 +1,64 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.storage; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; + +public class DisposableCacheItem extends CacheItem { + protected @Nullable final ShouldDisposeFunc shouldDisposeFunc; + + /** + * Constructs a CacheItem. + * + * @param item the item value. + * @param expirationTimeNanos the time at which the CacheItem should be considered expired. + */ + protected DisposableCacheItem(@NotNull V item, long expirationTimeNanos) { + super(item, expirationTimeNanos); + this.shouldDisposeFunc = null; + } + + /** + * Constructs a CacheItem. + * + * @param item the item value. + * @param expirationTimeNanos the time at which the CacheItem should be considered expired. + */ + protected DisposableCacheItem( + @NotNull V item, + long expirationTimeNanos, + @Nullable ShouldDisposeFunc shouldDisposeFunc) { + super(item, expirationTimeNanos); + this.shouldDisposeFunc = shouldDisposeFunc; + } + + /** + * Determines if a cache item should be cleaned up. An item should be cleaned up if it has past its expiration time + * and the {@link ShouldDisposeFunc} (if defined) indicates that it should be cleaned up. + * + * @return true if the cache item should be cleaned up. Otherwise, returns false. + */ + protected boolean shouldCleanup() { + final boolean isExpired = this.expirationTimeNanos != 0 && System.nanoTime() > this.expirationTimeNanos; + if (shouldDisposeFunc != null) { + return isExpired && shouldDisposeFunc.shouldDispose(this.item); + } + return isExpired; + } + +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index ccd86f2e2..dc77efe96 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -37,7 +37,7 @@ public class ExpirationCache { private static final Logger LOGGER = Logger.getLogger(ExpirationCache.class.getName()); protected static final long DEFAULT_TIME_TO_LIVE_NANOS = TimeUnit.MINUTES.toNanos(5); - protected final Map cache = new ConcurrentHashMap<>(); + protected final Map> cache = new ConcurrentHashMap<>(); protected final boolean isRenewableExpiration; protected final long timeToLiveNanos; protected final ShouldDisposeFunc shouldDisposeFunc; @@ -82,8 +82,8 @@ public ExpirationCache( public @Nullable V put( final K key, final V value) { - final CacheItem cacheItem = - cache.put(key, new CacheItem(value, System.nanoTime() + this.timeToLiveNanos)); + final CacheItem cacheItem = + cache.put(key, new DisposableCacheItem<>(value, System.nanoTime() + this.timeToLiveNanos)); if (cacheItem == null) { return null; } @@ -112,12 +112,12 @@ public ExpirationCache( // to be final. This allows us to dispose of the item after it has been removed and the cache has been unlocked, // which is important because the disposal function may be long-running. final List toDisposeList = new ArrayList<>(1); - final CacheItem cacheItem = cache.compute( + final CacheItem cacheItem = cache.compute( key, (k, valueItem) -> { if (valueItem == null) { // The key is absent; compute and store the new value. - return new CacheItem( + return new DisposableCacheItem<>( mappingFunction.apply(k), System.nanoTime() + this.timeToLiveNanos); } @@ -125,14 +125,14 @@ public ExpirationCache( if (valueItem.shouldCleanup() && !this.isRenewableExpiration) { // The existing value is expired and non-renewable. Mark it for disposal and store the new value. toDisposeList.add(valueItem.item); - return new CacheItem( + return new DisposableCacheItem<>( mappingFunction.apply(k), System.nanoTime() + this.timeToLiveNanos); } // The existing value is non-expired or renewable. Keep the existing value. if (this.isRenewableExpiration) { - valueItem.extendExpiration(); + valueItem.extendExpiration(this.timeToLiveNanos); } return valueItem; @@ -152,13 +152,13 @@ public ExpirationCache( * @return the value stored at the given key, or null if there is no existing value or the existing value is expired. */ public @Nullable V get(final K key) { - final CacheItem cacheItem = cache.get(key); + final DisposableCacheItem cacheItem = cache.get(key); if (cacheItem == null) { return null; } if (this.isRenewableExpiration) { - cacheItem.extendExpiration(); + cacheItem.extendExpiration(this.timeToLiveNanos); } else if (cacheItem.shouldCleanup()) { return null; } @@ -173,7 +173,7 @@ public ExpirationCache( * @return true if there is a non-expired value stored at the given key, otherwise returns false. */ public boolean exists(final K key) { - final CacheItem cacheItem = cache.get(key); + final DisposableCacheItem cacheItem = cache.get(key); return cacheItem != null && !cacheItem.shouldCleanup(); } @@ -189,7 +189,7 @@ public boolean exists(final K key) { } protected @Nullable V removeAndDispose(K key) { - final CacheItem cacheItem = cache.remove(key); + final DisposableCacheItem cacheItem = cache.remove(key); if (cacheItem == null) { return null; } @@ -262,7 +262,7 @@ public void clear() { */ public Map getEntries() { final Map entries = new HashMap<>(); - for (final Map.Entry entry : this.cache.entrySet()) { + for (final Map.Entry> entry : this.cache.entrySet()) { entries.put(entry.getKey(), entry.getValue().item); } @@ -277,72 +277,4 @@ public Map getEntries() { public int size() { return this.cache.size(); } - - /** - * A container class that holds a cache value together with the time at which the value should be considered expired. - */ - protected class CacheItem { - private final V item; - private long expirationTimeNanos; - - /** - * Constructs a CacheItem. - * - * @param item the item value. - * @param expirationTimeNanos the time at which a CacheItem should be considered expired. - */ - protected CacheItem(final V item, final long expirationTimeNanos) { - this.item = item; - this.expirationTimeNanos = expirationTimeNanos; - } - - /** - * Determines if a cache item should be cleaned up. An item should be cleaned up if it has past its expiration time - * and the {@link ShouldDisposeFunc} (if defined) indicates that it should be cleaned up. - * - * @return true if the cache item should be cleaned up. Otherwise, returns false. - */ - protected boolean shouldCleanup() { - final boolean isExpired = this.expirationTimeNanos != 0 && System.nanoTime() > this.expirationTimeNanos; - if (shouldDisposeFunc != null) { - return isExpired && shouldDisposeFunc.shouldDispose(this.item); - } - return isExpired; - } - - /** - * Renews a cache item's expiration time. - */ - protected void extendExpiration() { - this.expirationTimeNanos = System.nanoTime() + timeToLiveNanos; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((item == null) ? 0 : item.hashCode()); - return result; - } - - @Override - @SuppressWarnings("unchecked") - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (obj == null || getClass() != obj.getClass()) { - return false; - } - - CacheItem other = (CacheItem) obj; - return this.item.equals(other.item) && this.expirationTimeNanos == other.expirationTimeNanos; - } - - @Override - public String toString() { - return "CacheItem [item=" + item + ", expirationTime=" + expirationTimeNanos + "]"; - } - } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCache.java index ea3870f2e..2e4f5a06d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCache.java @@ -218,79 +218,4 @@ public void setCleanupIntervalNanos(long cleanupIntervalNanos) { Map getCache() { return cache; } - - class CacheItem { - private final V item; - private long expirationTimeNano; - - /** - * CacheItem constructor. - * - * @param item the item value - * @param expirationTimeNano the amount of time before a CacheItem should be marked as expired. - */ - public CacheItem(final V item, final long expirationTimeNano) { - this.item = item; - this.expirationTimeNano = expirationTimeNano; - } - - /** - * Determines if a cache item should be cleaned up. An item should be cleaned up if it has past - * its expiration time and {@link ShouldDisposeFunc} (if defined) indicates that it should be - * cleaned up. - * - * @return true if the cache item should be cleaned up at cleanup time. Otherwise, returns - * false. - */ - boolean shouldCleanup() { - final ShouldDisposeFunc tempShouldDisposeFunc = shouldDisposeFunc.get(); - if (tempShouldDisposeFunc != null) { - return System.nanoTime() > expirationTimeNano && tempShouldDisposeFunc.shouldDispose(this.item); - } - return System.nanoTime() > expirationTimeNano; - } - - /** - * Renew a cache item's expiration time and return the value. - * - * @param itemExpirationNano the new expiration duration for the item - * @return the item value - */ - public CacheItem withExtendExpiration(final long itemExpirationNano) { - this.expirationTimeNano = System.nanoTime() + itemExpirationNano; - return this; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((item == null) ? 0 : item.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final CacheItem other = (CacheItem) obj; - if (item == null) { - return other.item == null; - } else { - return item.equals(other.item); - } - } - - @Override - public String toString() { - return "CacheItem [item=" + item + ", expirationTime=" + expirationTimeNano + "]"; - } - } } From 47f9f44dd40b2d02f1eb56449f0fa9ae13189a7b Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 30 Apr 2025 15:54:17 -0700 Subject: [PATCH 083/149] PR suggestions --- .../ClusterTopologyMonitorImpl.java | 18 ++++++++++------- .../MonitoringRdsHostListProvider.java | 9 +++++---- .../CustomEndpointMonitorImpl.java | 20 ++++++++++++------- .../customendpoint/CustomEndpointPlugin.java | 8 +++++--- .../util/events/PeriodicEventPublisher.java | 14 ++++++++----- .../CustomEndpointMonitorImplTest.java | 14 +++++-------- 6 files changed, 48 insertions(+), 35 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 8d7b55f36..063c24f8f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -52,12 +52,12 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SynchronousExecutor; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.AbstractMonitor; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.Topology; @@ -112,23 +112,27 @@ public class ClusterTopologyMonitorImpl extends AbstractMonitor implements Clust protected final AtomicReference> nodeThreadsLatestTopology = new AtomicReference<>(null); public ClusterTopologyMonitorImpl( - final ServiceContainer serviceContainer, final String clusterId, + final StorageService storageService, + final MonitorService monitorService, + final ConnectionService connectionService, final HostSpec initialHostSpec, final Properties properties, + final PluginService pluginService, + final HostListProviderService hostListProviderService, final HostSpec clusterInstanceTemplate, final long refreshRateNano, final long highRefreshRateNano, final String topologyQuery, final String writerTopologyQuery, final String nodeIdQuery) { - super(serviceContainer.getMonitorService()); + super(monitorService); this.clusterId = clusterId; - this.storageService = serviceContainer.getStorageService(); - this.connectionService = serviceContainer.getConnectionService(); - this.pluginService = serviceContainer.getPluginService(); - this.hostListProviderService = serviceContainer.getHostListProviderService(); + this.storageService = storageService; + this.connectionService = connectionService; + this.pluginService = pluginService; + this.hostListProviderService = hostListProviderService; this.initialHostSpec = initialHostSpec; this.clusterInstanceTemplate = clusterInstanceTemplate; this.properties = properties; diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index 26346521d..88be14ea7 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -45,9 +45,6 @@ public class MonitoringRdsHostListProvider extends RdsHostListProvider "100", "Cluster topology high refresh rate in millis."); - protected static final long CACHE_CLEANUP_NANO = TimeUnit.MINUTES.toNanos(1); - protected static final long MONITOR_EXPIRATION_NANO = TimeUnit.MINUTES.toNanos(15); - static { PropertyDefinition.registerPluginProperties(MonitoringRdsHostListProvider.class); } @@ -89,10 +86,14 @@ protected ClusterTopologyMonitor initMonitor() { ClusterTopologyMonitorImpl.class, this.clusterId, () -> new ClusterTopologyMonitorImpl( - this.serviceContainer, this.clusterId, + this.serviceContainer.getStorageService(), + this.monitorService, + this.serviceContainer.getConnectionService(), this.initialHostSpec, this.properties, + this.pluginService, + this.serviceContainer.getHostListProviderService(), this.clusterInstanceTemplate, this.refreshRateNano, this.highRefreshRateNano, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index d86ec56c5..96d5c4e47 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -32,12 +32,13 @@ import software.amazon.awssdk.services.rds.model.Filter; import software.amazon.jdbc.AllowedAndBlockedHosts; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.monitoring.AbstractMonitor; +import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; +import software.amazon.jdbc.util.telemetry.TelemetryFactory; /** * The default custom endpoint monitor implementation. This class uses a background thread to monitor a given custom @@ -64,7 +65,10 @@ public class CustomEndpointMonitorImpl extends AbstractMonitor implements Custom /** * Constructs a CustomEndpointMonitorImpl instance for the host specified by {@code customEndpointHostSpec}. * - * @param serviceContainer The service container for the services required by this class. + * @param monitorService The monitorService used to submit this monitor. + * @param storageService The storage service used to store the set of allowed/blocked hosts according to the + * custom endpoint info. + * @param telemetryFactory The telemetry factory * @param customEndpointHostSpec The host information for the custom endpoint to be monitored. * @param endpointIdentifier An endpoint identifier. * @param region The region of the custom endpoint to be monitored. @@ -74,21 +78,23 @@ public class CustomEndpointMonitorImpl extends AbstractMonitor implements Custom * information. */ public CustomEndpointMonitorImpl( - ServiceContainer serviceContainer, + MonitorService monitorService, + StorageService storageService, + TelemetryFactory telemetryFactory, HostSpec customEndpointHostSpec, String endpointIdentifier, Region region, long refreshRateNano, BiFunction rdsClientFunc) { - super(serviceContainer.getMonitorService()); - this.storageService = serviceContainer.getStorageService(); + super(monitorService); + this.storageService = storageService; this.customEndpointHostSpec = customEndpointHostSpec; this.endpointIdentifier = endpointIdentifier; this.region = region; this.refreshRateNano = refreshRateNano; this.rdsClient = rdsClientFunc.apply(customEndpointHostSpec, this.region); - this.infoChangedCounter = serviceContainer.getTelemetryFactory().createCounter(TELEMETRY_ENDPOINT_INFO_CHANGED); + this.infoChangedCounter = telemetryFactory.createCounter(TELEMETRY_ENDPOINT_INFO_CHANGED); } /** diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java index 37a127c42..1cda9a9d4 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java @@ -204,11 +204,13 @@ public Connection connect( * @return {@link CustomEndpointMonitor} */ protected CustomEndpointMonitor createMonitorIfAbsent(Properties props) { - return this.monitorService.runIfAbsent( + return this.serviceContainer.getMonitorService().runIfAbsent( CustomEndpointMonitorImpl.class, - this.customEndpointHostSpec.getUrl(), + this.customEndpointHostSpec.getHost(), () -> new CustomEndpointMonitorImpl( - this.serviceContainer, + this.serviceContainer.getMonitorService(), + this.serviceContainer.getStorageService(), + this.serviceContainer.getTelemetryFactory(), this.customEndpointHostSpec, this.customEndpointId, this.region, diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java index 322ca2054..eb14a78ef 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java @@ -16,8 +16,10 @@ package software.amazon.jdbc.util.events; +import java.util.Collections; import java.util.Map; import java.util.Set; +import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -30,7 +32,7 @@ */ public class PeriodicEventPublisher implements EventPublisher { protected static final long DEFAULT_MESSAGE_INTERVAL_NANOS = TimeUnit.SECONDS.toNanos(30); - protected final Map, Set> subscribers = new ConcurrentHashMap<>(); + protected final Map, Set> subscribersMap = new ConcurrentHashMap<>(); // ConcurrentHashMap.newKeySet() is the recommended way to get a concurrent set. A set is used to prevent duplicate // event messages from being sent in the same message batch. // TODO: should duplicate events be allowed? Data access events may happen frequently so duplicates could result in @@ -61,7 +63,7 @@ public PeriodicEventPublisher(long messageIntervalNanos) { private void sendMessages() { for (Event event : eventMessages) { - for (EventSubscriber subscriber : subscribers.get(event.getClass())) { + for (EventSubscriber subscriber : subscribersMap.get(event.getClass())) { subscriber.processEvent(event); } } @@ -70,15 +72,17 @@ private void sendMessages() { @Override public void subscribe(EventSubscriber subscriber, Set> eventClasses) { for (Class eventClass : eventClasses) { - // ConcurrentHashMap.newKeySet() is the recommended way to get a concurrent set. - subscribers.computeIfAbsent(eventClass, (k) -> ConcurrentHashMap.newKeySet()).add(subscriber); + // The subscriber collection is a weakly referenced set so that we avoid garbage collection issues. + // TODO: do subscribers need to implement equals/hashcode? + subscribersMap.computeIfAbsent( + eventClass, (k) -> Collections.newSetFromMap(new WeakHashMap<>())).add(subscriber); } } @Override public void unsubscribe(EventSubscriber subscriber, Set> eventClasses) { for (Class eventClass : eventClasses) { - subscribers.computeIfPresent(eventClass, (k, v) -> { + subscribersMap.computeIfPresent(eventClass, (k, v) -> { v.remove(subscriber); return v.isEmpty() ? null : v; }); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java index d61734102..599ca5b3b 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java @@ -49,14 +49,12 @@ import software.amazon.jdbc.HostSpecBuilder; import software.amazon.jdbc.hostavailability.HostAvailabilityStrategy; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class CustomEndpointMonitorImplTest { - @Mock private ServiceContainer mockServiceContainer; @Mock private MonitorService mockMonitorService; @Mock private StorageService mockStorageService; @Mock private BiFunction mockRdsClientFunc; @@ -96,10 +94,6 @@ public void init() throws SQLException { twoEndpointList = Arrays.asList(mockClusterEndpoint1, mockClusterEndpoint2); oneEndpointList = Collections.singletonList(mockClusterEndpoint1); - when(mockServiceContainer.getStorageService()).thenReturn(mockStorageService); - when(mockServiceContainer.getMonitorService()).thenReturn(mockMonitorService); - when(mockServiceContainer.getTelemetryFactory()).thenReturn(mockTelemetryFactory); - when(mockTelemetryFactory.createCounter(any(String.class))).thenReturn(mockTelemetryCounter); when(mockTelemetryFactory.createCounter(any(String.class))).thenReturn(mockTelemetryCounter); when(mockRdsClientFunc.apply(any(HostSpec.class), any(Region.class))).thenReturn(mockRdsClient); when(mockRdsClient.describeDBClusterEndpoints(any(Consumer.class))).thenReturn(mockDescribeResponse); @@ -121,7 +115,9 @@ void cleanUp() throws Exception { @Test public void testRun() throws InterruptedException { CustomEndpointMonitorImpl monitor = new CustomEndpointMonitorImpl( - mockServiceContainer, + mockMonitorService, + mockStorageService, + mockTelemetryFactory, host, endpointId, Region.US_EAST_1, @@ -132,11 +128,11 @@ public void testRun() throws InterruptedException { // Wait for 2 run cycles. The first will return an unexpected number of endpoints in the API response, the second // will return the expected number of endpoints (one). TimeUnit.MILLISECONDS.sleep(100); - assertEquals(expectedInfo, CustomEndpointMonitorImpl.customEndpointInfoCache.get(host.getUrl())); + assertEquals(expectedInfo, CustomEndpointMonitorImpl.customEndpointInfoCache.get(host.getHost())); monitor.stop(); ArgumentCaptor captor = ArgumentCaptor.forClass(AllowedAndBlockedHosts.class); - verify(mockStorageService).set(eq(host.getUrl()), captor.capture()); + verify(mockStorageService).set(eq(host.getHost()), captor.capture()); assertEquals(staticMembersSet, captor.getValue().getAllowedHostIds()); assertNull(captor.getValue().getBlockedHostIds()); From fadcefcfacc91e8d2a1e5b6de6187092633fd693 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 1 May 2025 12:18:48 -0700 Subject: [PATCH 084/149] Fix CacheItem, build errors --- .../MonitoringRdsMultiAzHostListProvider.java | 9 ++- .../MultiAzClusterTopologyMonitorImpl.java | 29 +++++++-- .../amazon/jdbc/util/storage/CacheItem.java | 31 +++++++++ .../util/storage/DisposableCacheItem.java | 64 ------------------- .../jdbc/util/storage/ExpirationCache.java | 23 ++++--- .../util/storage/SlidingExpirationCache.java | 34 ++++++---- 6 files changed, 98 insertions(+), 92 deletions(-) delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/storage/DisposableCacheItem.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java index 877dfd42d..025321808 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java @@ -53,12 +53,17 @@ protected ClusterTopologyMonitor initMonitor() { return monitorService.runIfAbsent(MultiAzClusterTopologyMonitorImpl.class, this.clusterId, () -> new MultiAzClusterTopologyMonitorImpl( - this.serviceContainer, this.clusterId, + this.storageService, + this.monitorService, + this.serviceContainer.getConnectionService(), this.initialHostSpec, this.properties, + this.pluginService, + this.hostListProviderService, this.clusterInstanceTemplate, - this.refreshRateNano, this.highRefreshRateNano, + this.refreshRateNano, + this.highRefreshRateNano, this.topologyQuery, this.writerTopologyQuery, this.nodeIdQuery, diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java index 67210a405..348ce3ed5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java @@ -24,9 +24,13 @@ import java.time.Instant; import java.util.Properties; import java.util.logging.Logger; +import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.connection.ConnectionService; +import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.storage.StorageService; public class MultiAzClusterTopologyMonitorImpl extends ClusterTopologyMonitorImpl { @@ -36,10 +40,14 @@ public class MultiAzClusterTopologyMonitorImpl extends ClusterTopologyMonitorImp protected final String fetchWriterNodeColumnName; public MultiAzClusterTopologyMonitorImpl( - final ServiceContainer serviceContainer, final String clusterId, + final StorageService storageService, + final MonitorService monitorService, + final ConnectionService connectionService, final HostSpec initialHostSpec, final Properties properties, + final PluginService pluginService, + final HostListProviderService hostListProviderService, final HostSpec clusterInstanceTemplate, final long refreshRateNano, final long highRefreshRateNano, @@ -48,8 +56,21 @@ public MultiAzClusterTopologyMonitorImpl( final String nodeIdQuery, final String fetchWriterNodeQuery, final String fetchWriterNodeColumnName) { - super(serviceContainer, clusterId, initialHostSpec, properties, clusterInstanceTemplate, refreshRateNano, - highRefreshRateNano, topologyQuery, writerTopologyQuery, nodeIdQuery); + super( + clusterId, + storageService, + monitorService, + connectionService, + initialHostSpec, + properties, + pluginService, + hostListProviderService, + clusterInstanceTemplate, + refreshRateNano, + highRefreshRateNano, + topologyQuery, + writerTopologyQuery, + nodeIdQuery); this.fetchWriterNodeQuery = fetchWriterNodeQuery; this.fetchWriterNodeColumnName = fetchWriterNodeColumnName; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java index ef7481cdc..656dc635a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java @@ -17,6 +17,7 @@ package software.amazon.jdbc.util.storage; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; /** * A container class that holds a cache value together with the time at which the value should be considered expired. @@ -24,6 +25,7 @@ public class CacheItem { protected final @NonNull V item; protected long expirationTimeNanos; + protected final @Nullable ShouldDisposeFunc shouldDisposeFunc; /** * Constructs a CacheItem. @@ -34,6 +36,21 @@ public class CacheItem { protected CacheItem(final @NonNull V item, final long expirationTimeNanos) { this.item = item; this.expirationTimeNanos = expirationTimeNanos; + this.shouldDisposeFunc = null; + } + + /** + * Constructs a CacheItem. + * + * @param item the item value. + * @param expirationTimeNanos the time at which the CacheItem should be considered expired. + * @param shouldDisposeFunc a function defining whether an expired item should be disposed. If null, items will + * always be disposed when expired. + */ + protected CacheItem(final @NonNull V item, final long expirationTimeNanos, final ShouldDisposeFunc shouldDisposeFunc) { + this.item = item; + this.expirationTimeNanos = expirationTimeNanos; + this.shouldDisposeFunc = shouldDisposeFunc; } /** @@ -52,6 +69,20 @@ protected void extendExpiration(long timeToLiveNanos) { this.expirationTimeNanos = System.nanoTime() + timeToLiveNanos; } + /** + * Determines if a cache item should be cleaned up. An item should be cleaned up if it has past its expiration time + * and the {@link ShouldDisposeFunc} (if defined) indicates that it should be cleaned up. + * + * @return true if the cache item should be cleaned up. Otherwise, returns false. + */ + protected boolean shouldCleanup() { + final boolean isExpired = this.expirationTimeNanos != 0 && System.nanoTime() > this.expirationTimeNanos; + if (shouldDisposeFunc != null) { + return isExpired && shouldDisposeFunc.shouldDispose(this.item); + } + return isExpired; + } + @Override public int hashCode() { final int prime = 31; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/DisposableCacheItem.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/DisposableCacheItem.java deleted file mode 100644 index de6ada278..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/DisposableCacheItem.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.util.storage; - -import org.checkerframework.checker.nullness.qual.Nullable; -import org.jetbrains.annotations.NotNull; - -public class DisposableCacheItem extends CacheItem { - protected @Nullable final ShouldDisposeFunc shouldDisposeFunc; - - /** - * Constructs a CacheItem. - * - * @param item the item value. - * @param expirationTimeNanos the time at which the CacheItem should be considered expired. - */ - protected DisposableCacheItem(@NotNull V item, long expirationTimeNanos) { - super(item, expirationTimeNanos); - this.shouldDisposeFunc = null; - } - - /** - * Constructs a CacheItem. - * - * @param item the item value. - * @param expirationTimeNanos the time at which the CacheItem should be considered expired. - */ - protected DisposableCacheItem( - @NotNull V item, - long expirationTimeNanos, - @Nullable ShouldDisposeFunc shouldDisposeFunc) { - super(item, expirationTimeNanos); - this.shouldDisposeFunc = shouldDisposeFunc; - } - - /** - * Determines if a cache item should be cleaned up. An item should be cleaned up if it has past its expiration time - * and the {@link ShouldDisposeFunc} (if defined) indicates that it should be cleaned up. - * - * @return true if the cache item should be cleaned up. Otherwise, returns false. - */ - protected boolean shouldCleanup() { - final boolean isExpired = this.expirationTimeNanos != 0 && System.nanoTime() > this.expirationTimeNanos; - if (shouldDisposeFunc != null) { - return isExpired && shouldDisposeFunc.shouldDispose(this.item); - } - return isExpired; - } - -} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index dc77efe96..2331bd2d8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -37,7 +37,7 @@ public class ExpirationCache { private static final Logger LOGGER = Logger.getLogger(ExpirationCache.class.getName()); protected static final long DEFAULT_TIME_TO_LIVE_NANOS = TimeUnit.MINUTES.toNanos(5); - protected final Map> cache = new ConcurrentHashMap<>(); + protected final Map> cache = new ConcurrentHashMap<>(); protected final boolean isRenewableExpiration; protected final long timeToLiveNanos; protected final ShouldDisposeFunc shouldDisposeFunc; @@ -83,7 +83,8 @@ public ExpirationCache( final K key, final V value) { final CacheItem cacheItem = - cache.put(key, new DisposableCacheItem<>(value, System.nanoTime() + this.timeToLiveNanos)); + cache.put(key, new CacheItem<>( + value, System.nanoTime() + this.timeToLiveNanos, this.shouldDisposeFunc)); if (cacheItem == null) { return null; } @@ -117,17 +118,19 @@ public ExpirationCache( (k, valueItem) -> { if (valueItem == null) { // The key is absent; compute and store the new value. - return new DisposableCacheItem<>( + return new CacheItem<>( mappingFunction.apply(k), - System.nanoTime() + this.timeToLiveNanos); + System.nanoTime() + this.timeToLiveNanos, + this.shouldDisposeFunc); } if (valueItem.shouldCleanup() && !this.isRenewableExpiration) { // The existing value is expired and non-renewable. Mark it for disposal and store the new value. toDisposeList.add(valueItem.item); - return new DisposableCacheItem<>( + return new CacheItem<>( mappingFunction.apply(k), - System.nanoTime() + this.timeToLiveNanos); + System.nanoTime() + this.timeToLiveNanos, + this.shouldDisposeFunc); } // The existing value is non-expired or renewable. Keep the existing value. @@ -152,7 +155,7 @@ public ExpirationCache( * @return the value stored at the given key, or null if there is no existing value or the existing value is expired. */ public @Nullable V get(final K key) { - final DisposableCacheItem cacheItem = cache.get(key); + final CacheItem cacheItem = cache.get(key); if (cacheItem == null) { return null; } @@ -173,7 +176,7 @@ public ExpirationCache( * @return true if there is a non-expired value stored at the given key, otherwise returns false. */ public boolean exists(final K key) { - final DisposableCacheItem cacheItem = cache.get(key); + final CacheItem cacheItem = cache.get(key); return cacheItem != null && !cacheItem.shouldCleanup(); } @@ -189,7 +192,7 @@ public boolean exists(final K key) { } protected @Nullable V removeAndDispose(K key) { - final DisposableCacheItem cacheItem = cache.remove(key); + final CacheItem cacheItem = cache.remove(key); if (cacheItem == null) { return null; } @@ -262,7 +265,7 @@ public void clear() { */ public Map getEntries() { final Map entries = new HashMap<>(); - for (final Map.Entry> entry : this.cache.entrySet()) { + for (final Map.Entry> entry : this.cache.entrySet()) { entries.put(entry.getKey(), entry.getValue().item); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCache.java index 2e4f5a06d..604b603f2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCache.java @@ -28,7 +28,7 @@ public class SlidingExpirationCache { - protected final Map cache = new ConcurrentHashMap<>(); + protected final Map> cache = new ConcurrentHashMap<>(); protected long cleanupIntervalNanos = TimeUnit.MINUTES.toNanos(10); protected final AtomicLong cleanupTimeNanos = new AtomicLong(System.nanoTime() + cleanupIntervalNanos); protected final AtomicReference> shouldDisposeFunc = new AtomicReference<>(null); @@ -92,12 +92,14 @@ public V computeIfAbsent( final long itemExpirationNano) { cleanUp(); - final CacheItem cacheItem = cache.computeIfAbsent( + final CacheItem cacheItem = cache.computeIfAbsent( key, - k -> new CacheItem( + k -> new CacheItem<>( mappingFunction.apply(k), - System.nanoTime() + itemExpirationNano)); - return cacheItem.withExtendExpiration(itemExpirationNano).item; + System.nanoTime() + itemExpirationNano, + this.shouldDisposeFunc.get())); + cacheItem.extendExpiration(itemExpirationNano); + return cacheItem.item; } public V put( @@ -105,17 +107,25 @@ public V put( final V value, final long itemExpirationNano) { cleanUp(); - final CacheItem cacheItem = cache.put(key, new CacheItem(value, System.nanoTime() + itemExpirationNano)); + final CacheItem cacheItem = cache.put( + key, new CacheItem<>(value, System.nanoTime() + itemExpirationNano)); if (cacheItem == null) { return null; } - return cacheItem.withExtendExpiration(itemExpirationNano).item; + + cacheItem.extendExpiration(itemExpirationNano); + return cacheItem.item; } public V get(final K key, final long itemExpirationNano) { cleanUp(); - final CacheItem cacheItem = cache.get(key); - return cacheItem == null ? null : cacheItem.withExtendExpiration(itemExpirationNano).item; + final CacheItem cacheItem = cache.get(key); + if (cacheItem == null) { + return null; + } + + cacheItem.extendExpiration(itemExpirationNano); + return cacheItem.item; } /** @@ -130,7 +140,7 @@ public void remove(final K key) { } protected void removeAndDispose(K key) { - final CacheItem cacheItem = cache.remove(key); + final CacheItem cacheItem = cache.remove(key); if (cacheItem != null && itemDisposalFunc != null) { itemDisposalFunc.dispose(cacheItem.item); } @@ -178,7 +188,7 @@ public void clear() { */ public Map getEntries() { final Map entries = new HashMap<>(); - for (final Map.Entry entry : this.cache.entrySet()) { + for (final Map.Entry> entry : this.cache.entrySet()) { entries.put(entry.getKey(), entry.getValue().item); } return entries; @@ -215,7 +225,7 @@ public void setCleanupIntervalNanos(long cleanupIntervalNanos) { } // For testing purposes only - Map getCache() { + Map> getCache() { return cache; } } From dfc07d9ef512186823cbdabff4a41f3717d7e725 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 1 May 2025 14:11:11 -0700 Subject: [PATCH 085/149] Remove IS_AUXILIARY_CONNECTION property --- .../amazon/jdbc/ConnectionPluginChainBuilder.java | 9 --------- .../java/software/amazon/jdbc/PropertyDefinition.java | 5 ----- .../amazon/jdbc/plugin/DefaultConnectionPlugin.java | 10 ++-------- .../jdbc/util/connection/ConnectionServiceImpl.java | 1 - 4 files changed, 2 insertions(+), 23 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java index 41e5a302a..6b41835f8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java @@ -28,7 +28,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.plugin.AuroraConnectionTrackerPluginFactory; import software.amazon.jdbc.plugin.AuroraInitialConnectionStrategyPluginFactory; -import software.amazon.jdbc.plugin.AuxiliaryPluginFactory; import software.amazon.jdbc.plugin.AwsSecretsManagerConnectionPluginFactory; import software.amazon.jdbc.plugin.ConnectTimeConnectionPluginFactory; import software.amazon.jdbc.plugin.DataCacheConnectionPluginFactory; @@ -164,14 +163,6 @@ public List getPlugins( } if (!pluginFactories.isEmpty()) { - if (PropertyDefinition.IS_AUXILIARY_CONNECTION.getBoolean(props)) { - // The requested connection is an auxiliary connection. Auxiliary connections should only use auxiliary plugins - // because non-auxiliary plugins can interfere with their intended purpose. For example, auxiliary EFM - // connections should not contain the failover plugin because they should always monitor the same instance. - pluginFactories = pluginFactories.stream() - .filter(AuxiliaryPluginFactory.class::isAssignableFrom) - .collect(Collectors.toList()); - } if (PropertyDefinition.AUTO_SORT_PLUGIN_ORDER.getBoolean(props)) { pluginFactories = this.sortPluginFactories(pluginFactories); diff --git a/wrapper/src/main/java/software/amazon/jdbc/PropertyDefinition.java b/wrapper/src/main/java/software/amazon/jdbc/PropertyDefinition.java index e3a7ca208..68a3568d9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PropertyDefinition.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PropertyDefinition.java @@ -53,11 +53,6 @@ public class PropertyDefinition { new AwsWrapperProperty( "wrapperPlugins", null, "Comma separated list of connection plugin codes"); - public static final AwsWrapperProperty IS_AUXILIARY_CONNECTION = - new AwsWrapperProperty( - "isAuxiliaryConnection", "false", "Represents whether a connection is a " - + "driver-internal auxiliary connection or not. Auxiliary connections only contain auxiliary plugins."); - public static final AwsWrapperProperty AUTO_SORT_PLUGIN_ORDER = new AwsWrapperProperty( "autoSortWrapperPluginOrder", diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java index 08446ce80..2d82d865f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/DefaultConnectionPlugin.java @@ -42,7 +42,6 @@ import software.amazon.jdbc.OldConnectionSuggestedAction; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.SqlMethodAnalyzer; @@ -171,13 +170,8 @@ public Connection connect( final boolean isInitialConnection, final JdbcCallable connectFunc) throws SQLException { - ConnectionProvider connProvider; - if (PropertyDefinition.IS_AUXILIARY_CONNECTION.getBoolean(props)) { - // Auxiliary connections should connect directly instead of using custom connection providers. - connProvider = this.defaultConnProvider; - } else { - connProvider = this.connProviderManager.getConnectionProvider(driverProtocol, hostSpec, props); - } + + ConnectionProvider connProvider = this.connProviderManager.getConnectionProvider(driverProtocol, hostSpec, props); // It's guaranteed that this plugin is always the last in plugin chain so connectFunc can be // ignored. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index 0f2b2f873..d486f8eb1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -53,7 +53,6 @@ public Connection createAuxiliaryConnection(HostSpec hostSpec, Properties props) ? PropertyDefinition.DATABASE.getString(auxiliaryProps) : ""; final String connString = this.targetDriverProtocol + hostSpec.getUrl() + databaseName; - PropertyDefinition.IS_AUXILIARY_CONNECTION.set(auxiliaryProps, "true"); // The auxiliary connection should have its own separate service container since the original container holds // services that should not be shared between connections (for example, PluginService). From f3d6ee9a25e5005197f355b3e0e60cefb42c7cf0 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 1 May 2025 14:40:00 -0700 Subject: [PATCH 086/149] Remove deprecated annotation for forceConnect --- .../src/main/java/software/amazon/jdbc/ConnectionPlugin.java | 3 --- .../java/software/amazon/jdbc/ConnectionPluginManager.java | 4 ---- wrapper/src/main/java/software/amazon/jdbc/PluginService.java | 3 --- 3 files changed, 10 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java index 9506eb7b1..b0636c5f2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java @@ -94,10 +94,7 @@ Connection connect( * @return a {@link Connection} to the requested host * @throws SQLException if there was an error establishing a {@link Connection} to the requested * host - * - * @deprecated Use {@link ConnectionService#createAuxiliaryConnection(HostSpec, Properties)} instead. */ - @Deprecated Connection forceConnect( final String driverProtocol, final HostSpec hostSpec, diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java index f7fe36c2b..dbef6942c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java @@ -53,7 +53,6 @@ import software.amazon.jdbc.util.SqlMethodAnalyzer; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.WrapperUtils; -import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryTraceLevel; @@ -421,10 +420,7 @@ public Connection connect( * @return a {@link Connection} to the requested host * @throws SQLException if there was an error establishing a {@link Connection} to the requested * host - * - * @deprecated Use {@link ConnectionService#createAuxiliaryConnection(HostSpec, Properties)} instead. */ - @Deprecated public Connection forceConnect( final String driverProtocol, final HostSpec hostSpec, diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java index f2f2239a3..b9a8edee2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java @@ -221,12 +221,9 @@ Connection connect(HostSpec hostSpec, Properties props, final @Nullable Connecti * @return a {@link Connection} to the requested host * @throws SQLException if there was an error establishing a {@link Connection} to the requested * host - * @deprecated Use {@link ConnectionService#createAuxiliaryConnection(HostSpec, Properties)} instead. */ - @Deprecated Connection forceConnect(HostSpec hostSpec, Properties props) throws SQLException; - @Deprecated Connection forceConnect( HostSpec hostSpec, Properties props, final @Nullable ConnectionPlugin pluginToSkip) throws SQLException; From a12725d6579712654590a85aa3292773120ce093 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 1 May 2025 15:53:36 -0700 Subject: [PATCH 087/149] wip --- .../amazon/jdbc/ConnectionPluginManager.java | 2 +- .../amazon/jdbc/MonitorPluginService.java | 792 ++++++++++++++++++ .../connection/ConnectionServiceImpl.java | 62 +- ..._advanced_jdbc_wrapper_messages.properties | 2 + 4 files changed, 824 insertions(+), 34 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java index dbef6942c..7f45a384b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java @@ -118,7 +118,7 @@ public class ConnectionPluginManager implements CanReleaseResources, Wrapper { public ConnectionPluginManager( final @NonNull ConnectionProvider defaultConnProvider, final @Nullable ConnectionProvider effectiveConnProvider, - final @NonNull ConnectionWrapper connectionWrapper, + final @Nullable ConnectionWrapper connectionWrapper, final @NonNull TelemetryFactory telemetryFactory) { this.defaultConnProvider = defaultConnProvider; this.effectiveConnProvider = effectiveConnProvider; diff --git a/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java new file mode 100644 index 000000000..ee7592cce --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java @@ -0,0 +1,792 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import software.amazon.jdbc.cleanup.CanReleaseResources; +import software.amazon.jdbc.dialect.Dialect; +import software.amazon.jdbc.dialect.DialectManager; +import software.amazon.jdbc.dialect.DialectProvider; +import software.amazon.jdbc.exceptions.ExceptionHandler; +import software.amazon.jdbc.exceptions.ExceptionManager; +import software.amazon.jdbc.hostavailability.HostAvailability; +import software.amazon.jdbc.hostavailability.HostAvailabilityStrategyFactory; +import software.amazon.jdbc.hostlistprovider.StaticHostListProvider; +import software.amazon.jdbc.profile.ConfigurationProfile; +import software.amazon.jdbc.states.SessionStateService; +import software.amazon.jdbc.states.SessionStateServiceImpl; +import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.storage.CacheMap; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.telemetry.TelemetryFactory; +import sun.reflect.generics.reflectiveObjects.NotImplementedException; + +public class MonitorPluginService implements PluginService, CanReleaseResources, + HostListProviderService, PluginManagerService { + + private static final Logger LOGGER = Logger.getLogger(PluginServiceImpl.class.getName()); + protected static final long DEFAULT_HOST_AVAILABILITY_CACHE_EXPIRE_NANO = TimeUnit.MINUTES.toNanos(5); + + protected static final CacheMap hostAvailabilityExpiringCache = new CacheMap<>(); + protected final ServiceContainer serviceContainer; + protected final StorageService storageService; + protected final ConnectionPluginManager pluginManager; + private final Properties props; + private final String driverProtocol; + protected volatile HostListProvider hostListProvider; + protected List allHosts = new ArrayList<>(); + protected Connection currentConnection; + protected HostSpec currentHostSpec; + protected HostSpec initialConnectionHostSpec; + private boolean isInTransaction; + private final ExceptionManager exceptionManager; + protected final @Nullable ExceptionHandler exceptionHandler; + protected final DialectProvider dialectProvider; + protected Dialect dbDialect; + protected TargetDriverDialect targetDriverDialect; + protected @Nullable final ConfigurationProfile configurationProfile; + protected final ConnectionProviderManager connectionProviderManager; + + protected final SessionStateService sessionStateService; + + protected final ReentrantLock connectionSwitchLock = new ReentrantLock(); + + public MonitorPluginService( + @NonNull final ServiceContainer serviceContainer, + @NonNull final Properties props, + @NonNull final String targetDriverProtocol, + @NonNull final TargetDriverDialect targetDriverDialect, + @NonNull final Dialect dbDialect) { + this( + serviceContainer, + new ExceptionManager(), + props, + targetDriverProtocol, + null, + targetDriverDialect, + dbDialect, + null, + null); + } + + public MonitorPluginService( + @NonNull final ServiceContainer serviceContainer, + @NonNull final Properties props, + @NonNull final String targetDriverProtocol, + @NonNull final TargetDriverDialect targetDriverDialect, + @NonNull final Dialect dbDialect, + @Nullable final ConfigurationProfile configurationProfile) { + this( + serviceContainer, + new ExceptionManager(), + props, + targetDriverProtocol, + null, + targetDriverDialect, + dbDialect, + configurationProfile, + null); + } + + public MonitorPluginService( + @NonNull final ServiceContainer serviceContainer, + @NonNull final ExceptionManager exceptionManager, + @NonNull final Properties props, + @NonNull final String targetDriverProtocol, + @Nullable final DialectProvider dialectProvider, + @NonNull final TargetDriverDialect targetDriverDialect, + @NonNull final Dialect dbDialect, + @Nullable final ConfigurationProfile configurationProfile, + @Nullable final SessionStateService sessionStateService) { + this.serviceContainer = serviceContainer; + this.storageService = serviceContainer.getStorageService(); + this.pluginManager = serviceContainer.getConnectionPluginManager(); + this.props = props; + this.driverProtocol = targetDriverProtocol; + this.configurationProfile = configurationProfile; + this.exceptionManager = exceptionManager; + this.dialectProvider = dialectProvider != null ? dialectProvider : new DialectManager(this); + this.targetDriverDialect = targetDriverDialect; + this.connectionProviderManager = new ConnectionProviderManager( + this.pluginManager.getDefaultConnProvider(), + this.pluginManager.getEffectiveConnProvider()); + + this.sessionStateService = sessionStateService != null + ? sessionStateService + : new SessionStateServiceImpl(this, this.props); + + this.exceptionHandler = this.configurationProfile != null && this.configurationProfile.getExceptionHandler() != null + ? this.configurationProfile.getExceptionHandler() + : null; + + this.dbDialect = dbDialect; + } + + @Override + public Connection getCurrentConnection() { + return this.currentConnection; + } + + @Override + public HostSpec getCurrentHostSpec() { + if (this.currentHostSpec == null) { + this.currentHostSpec = this.initialConnectionHostSpec; + + if (this.currentHostSpec == null) { + if (this.getAllHosts().isEmpty()) { + throw new RuntimeException(Messages.get("PluginServiceImpl.hostListEmpty")); + } + + this.currentHostSpec = this.getWriter(this.getAllHosts()); + final List allowedHosts = this.getHosts(); + if (!Utils.containsUrl(allowedHosts, this.currentHostSpec.getUrl())) { + throw new RuntimeException( + Messages.get("PluginServiceImpl.currentHostNotAllowed", + new Object[] { + currentHostSpec == null ? "" : currentHostSpec.getUrl(), + Utils.logTopology(allowedHosts, "")}) + ); + } + + if (this.currentHostSpec == null) { + this.currentHostSpec = this.getHosts().get(0); + } + } + if (this.currentHostSpec == null) { + throw new RuntimeException("Current host is undefined."); + } + LOGGER.finest(() -> "Set current host to " + this.currentHostSpec); + } + return this.currentHostSpec; + } + + public void setInitialConnectionHostSpec(final @NonNull HostSpec initialConnectionHostSpec) { + this.initialConnectionHostSpec = initialConnectionHostSpec; + } + + @Override + public HostSpec getInitialConnectionHostSpec() { + return this.initialConnectionHostSpec; + } + + @Override + public ServiceContainer getServiceContainer() { + return this.serviceContainer; + } + + @Override + @Deprecated + public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHosts) { + this.storageService.set(this.initialConnectionHostSpec.getHost(), allowedAndBlockedHosts); + } + + @Override + public boolean acceptsStrategy(HostRole role, String strategy) throws SQLException { + return this.pluginManager.acceptsStrategy(role, strategy); + } + + @Override + public HostSpec getHostSpecByStrategy(HostRole role, String strategy) throws SQLException { + return this.pluginManager.getHostSpecByStrategy(role, strategy); + } + + @Override + public HostSpec getHostSpecByStrategy(List hosts, HostRole role, String strategy) throws SQLException { + return this.pluginManager.getHostSpecByStrategy(hosts, role, strategy); + } + + @Override + public HostRole getHostRole(Connection conn) throws SQLException { + return this.hostListProvider.getHostRole(conn); + } + + private HostSpec getWriter(final @NonNull List hosts) { + for (final HostSpec hostSpec : hosts) { + if (hostSpec.getRole() == HostRole.WRITER) { + return hostSpec; + } + } + return null; + } + + @Override + @Deprecated + public ConnectionProvider getConnectionProvider() { + return this.pluginManager.defaultConnProvider; + } + + public boolean isPooledConnectionProvider(HostSpec host, Properties props) { + final ConnectionProvider connectionProvider = + this.connectionProviderManager.getConnectionProvider(this.driverProtocol, host, props); + return (connectionProvider instanceof PooledConnectionProvider); + } + + @Override + public String getDriverProtocol() { + return this.driverProtocol; + } + + @Override + public void setCurrentConnection( + final @NonNull Connection connection, final @NonNull HostSpec hostSpec) throws SQLException { + + setCurrentConnection(connection, hostSpec, null); + } + + @Override + public EnumSet setCurrentConnection( + final @NonNull Connection connection, + final @NonNull HostSpec hostSpec, + @Nullable final ConnectionPlugin skipNotificationForThisPlugin) + throws SQLException { + + connectionSwitchLock.lock(); + try { + + if (this.currentConnection == null) { + // setting up an initial connection + + this.currentConnection = connection; + this.currentHostSpec = hostSpec; + this.sessionStateService.reset(); + + final EnumSet changes = EnumSet.of(NodeChangeOptions.INITIAL_CONNECTION); + this.pluginManager.notifyConnectionChanged(changes, skipNotificationForThisPlugin); + + return changes; + + } else { + // update an existing connection + + final EnumSet changes = compare(this.currentConnection, this.currentHostSpec, + connection, hostSpec); + + if (!changes.isEmpty()) { + + final Connection oldConnection = this.currentConnection; + final boolean isInTransaction = this.isInTransaction; + this.sessionStateService.begin(); + + try { + this.currentConnection = connection; + this.currentHostSpec = hostSpec; + + this.sessionStateService.applyCurrentSessionState(connection); + this.setInTransaction(false); + + if (isInTransaction && PropertyDefinition.ROLLBACK_ON_SWITCH.getBoolean(this.props)) { + try { + oldConnection.rollback(); + } catch (final SQLException e) { + // Ignore any exception + } + } + + final EnumSet pluginOpinions = this.pluginManager.notifyConnectionChanged( + changes, skipNotificationForThisPlugin); + + final boolean shouldCloseConnection = + changes.contains(NodeChangeOptions.CONNECTION_OBJECT_CHANGED) + && !oldConnection.isClosed() + && !pluginOpinions.contains(OldConnectionSuggestedAction.PRESERVE); + + if (shouldCloseConnection) { + try { + this.sessionStateService.applyPristineSessionState(oldConnection); + } catch (final SQLException e) { + // Ignore any exception + } + + try { + oldConnection.close(); + } catch (final SQLException e) { + // Ignore any exception + } + } + } finally { + this.sessionStateService.complete(); + } + } + return changes; + } + } finally { + connectionSwitchLock.unlock(); + } + } + + protected EnumSet compare( + final @NonNull Connection connA, + final @NonNull HostSpec hostSpecA, + final @NonNull Connection connB, + final @NonNull HostSpec hostSpecB) { + + final EnumSet changes = EnumSet.noneOf(NodeChangeOptions.class); + + if (connA != connB) { + changes.add(NodeChangeOptions.CONNECTION_OBJECT_CHANGED); + } + + changes.addAll(compare(hostSpecA, hostSpecB)); + return changes; + } + + protected EnumSet compare( + final @NonNull HostSpec hostSpecA, + final @NonNull HostSpec hostSpecB) { + + final EnumSet changes = EnumSet.noneOf(NodeChangeOptions.class); + + if (!hostSpecA.getHost().equals(hostSpecB.getHost()) + || hostSpecA.getPort() != hostSpecB.getPort()) { + changes.add(NodeChangeOptions.HOSTNAME); + } + + if (hostSpecA.getRole() != hostSpecB.getRole()) { + if (hostSpecB.getRole() == HostRole.WRITER) { + changes.add(NodeChangeOptions.PROMOTED_TO_WRITER); + } else if (hostSpecB.getRole() == HostRole.READER) { + changes.add(NodeChangeOptions.PROMOTED_TO_READER); + } + } + + if (hostSpecA.getAvailability() != hostSpecB.getAvailability()) { + if (hostSpecB.getAvailability() == HostAvailability.AVAILABLE) { + changes.add(NodeChangeOptions.WENT_UP); + } else if (hostSpecB.getAvailability() == HostAvailability.NOT_AVAILABLE) { + changes.add(NodeChangeOptions.WENT_DOWN); + } + } + + if (!changes.isEmpty()) { + changes.add(NodeChangeOptions.NODE_CHANGED); + } + + return changes; + } + + @Override + public List getAllHosts() { + return this.allHosts; + } + + @Override + public List getHosts() { + AllowedAndBlockedHosts hostPermissions = this.storageService.get( + AllowedAndBlockedHosts.class, this.initialConnectionHostSpec.getUrl()); + if (hostPermissions == null) { + return this.allHosts; + } + + List hosts = this.allHosts; + Set allowedHostIds = hostPermissions.getAllowedHostIds(); + Set blockedHostIds = hostPermissions.getBlockedHostIds(); + + if (!Utils.isNullOrEmpty(allowedHostIds)) { + hosts = hosts.stream() + .filter((hostSpec -> allowedHostIds.contains(hostSpec.getHostId()))) + .collect(Collectors.toList()); + } + + if (!Utils.isNullOrEmpty(blockedHostIds)) { + hosts = hosts.stream() + .filter((hostSpec -> !blockedHostIds.contains(hostSpec.getHostId()))) + .collect(Collectors.toList()); + } + + return hosts; + } + + @Override + public void setAvailability(final @NonNull Set hostAliases, final @NonNull HostAvailability availability) { + + if (hostAliases.isEmpty()) { + return; + } + + final List hostsToChange = this.getAllHosts().stream() + .filter((host) -> hostAliases.contains(host.asAlias()) + || host.getAliases().stream().anyMatch(hostAliases::contains)) + .distinct() + .collect(Collectors.toList()); + + if (hostsToChange.isEmpty()) { + LOGGER.finest(() -> Messages.get("PluginServiceImpl.hostsChangelistEmpty")); + return; + } + + final Map> changes = new HashMap<>(); + for (final HostSpec host : hostsToChange) { + final HostAvailability currentAvailability = host.getAvailability(); + host.setAvailability(availability); + hostAvailabilityExpiringCache.put(host.getUrl(), availability, + DEFAULT_HOST_AVAILABILITY_CACHE_EXPIRE_NANO); + if (currentAvailability != availability) { + final EnumSet hostChanges; + if (availability == HostAvailability.AVAILABLE) { + hostChanges = EnumSet.of(NodeChangeOptions.WENT_UP, NodeChangeOptions.NODE_CHANGED); + } else { + hostChanges = EnumSet.of(NodeChangeOptions.WENT_DOWN, NodeChangeOptions.NODE_CHANGED); + } + changes.put(host.getUrl(), hostChanges); + } + } + + if (!changes.isEmpty()) { + this.pluginManager.notifyNodeListChanged(changes); + } + } + + @Override + public boolean isInTransaction() { + return this.isInTransaction; + } + + @Override + public void setInTransaction(final boolean inTransaction) { + this.isInTransaction = inTransaction; + } + + @Override + public HostListProvider getHostListProvider() { + return this.hostListProvider; + } + + @Override + public void refreshHostList() throws SQLException { + final List updatedHostList = this.getHostListProvider().refresh(); + if (!Objects.equals(updatedHostList, this.allHosts)) { + updateHostAvailability(updatedHostList); + setNodeList(this.allHosts, updatedHostList); + } + } + + @Override + public void refreshHostList(final Connection connection) throws SQLException { + final List updatedHostList = this.getHostListProvider().refresh(connection); + if (!Objects.equals(updatedHostList, this.allHosts)) { + updateHostAvailability(updatedHostList); + setNodeList(this.allHosts, updatedHostList); + } + } + + @Override + public void forceRefreshHostList() throws SQLException { + final List updatedHostList = this.getHostListProvider().forceRefresh(); + if (updatedHostList != null) { + updateHostAvailability(updatedHostList); + setNodeList(this.allHosts, updatedHostList); + } + } + + @Override + public void forceRefreshHostList(final Connection connection) throws SQLException { + final List updatedHostList = this.getHostListProvider().forceRefresh(connection); + if (updatedHostList != null) { + updateHostAvailability(updatedHostList); + setNodeList(this.allHosts, updatedHostList); + } + } + + @Override + public boolean forceRefreshHostList(final boolean shouldVerifyWriter, final long timeoutMs) + throws SQLException { + + final HostListProvider hostListProvider = this.getHostListProvider(); + if (!(hostListProvider instanceof BlockingHostListProvider)) { + throw new UnsupportedOperationException( + Messages.get("PluginServiceImpl.requiredBlockingHostListProvider", + new Object[] {hostListProvider.getClass().getName()})); + } + + try { + final List updatedHostList = + ((BlockingHostListProvider) hostListProvider).forceRefresh(shouldVerifyWriter, timeoutMs); + if (updatedHostList != null) { + updateHostAvailability(updatedHostList); + setNodeList(this.allHosts, updatedHostList); + return true; + } + } catch (TimeoutException ex) { + // do nothing. + LOGGER.finest(Messages.get("PluginServiceImpl.forceRefreshTimeout", new Object[] {timeoutMs})); + } + return false; + } + + void setNodeList(@Nullable final List oldHosts, + @Nullable final List newHosts) { + + final Map oldHostMap = oldHosts == null + ? new HashMap<>() + : oldHosts.stream().collect(Collectors.toMap(HostSpec::getUrl, (value) -> value)); + + final Map newHostMap = newHosts == null + ? new HashMap<>() + : newHosts.stream().collect(Collectors.toMap(HostSpec::getUrl, (value) -> value)); + + final Map> changes = new HashMap<>(); + + for (final Map.Entry entry : oldHostMap.entrySet()) { + final HostSpec correspondingNewHost = newHostMap.get(entry.getKey()); + if (correspondingNewHost == null) { + // host deleted + changes.put(entry.getKey(), EnumSet.of(NodeChangeOptions.NODE_DELETED)); + } else { + // host maybe changed + final EnumSet hostChanges = compare(entry.getValue(), correspondingNewHost); + if (!hostChanges.isEmpty()) { + changes.put(entry.getKey(), hostChanges); + } + } + } + + for (final Map.Entry entry : newHostMap.entrySet()) { + if (!oldHostMap.containsKey(entry.getKey())) { + // host added + changes.put(entry.getKey(), EnumSet.of(NodeChangeOptions.NODE_ADDED)); + } + } + + if (!changes.isEmpty()) { + this.allHosts = newHosts != null ? newHosts : new ArrayList<>(); + this.pluginManager.notifyNodeListChanged(changes); + } + } + + @Override + public boolean isStaticHostListProvider() { + return this.getHostListProvider() instanceof StaticHostListProvider; + } + + @Override + public void setHostListProvider(final HostListProvider hostListProvider) { + this.hostListProvider = hostListProvider; + } + + @Override + public Connection connect(final HostSpec hostSpec, final Properties props) throws SQLException { + return this.connect(hostSpec, props, null); + } + + @Override + public Connection connect( + final HostSpec hostSpec, + final Properties props, + final @Nullable ConnectionPlugin pluginToSkip) + throws SQLException { + return this.pluginManager.connect( + this.driverProtocol, hostSpec, props, this.currentConnection == null, pluginToSkip); + } + + @Override + public Connection forceConnect( + final HostSpec hostSpec, + final Properties props) + throws SQLException { + return this.forceConnect(hostSpec, props, null); + } + + @Override + public Connection forceConnect( + final HostSpec hostSpec, + final Properties props, + final @Nullable ConnectionPlugin pluginToSkip) + throws SQLException { + return this.pluginManager.forceConnect( + this.driverProtocol, hostSpec, props, this.currentConnection == null, pluginToSkip); + } + + private void updateHostAvailability(final List hosts) { + for (final HostSpec host : hosts) { + final HostAvailability availability = hostAvailabilityExpiringCache.get(host.getUrl()); + if (availability != null) { + host.setAvailability(availability); + } + } + } + + @Override + public void releaseResources() { + LOGGER.fine(() -> Messages.get("PluginServiceImpl.releaseResources")); + + try { + if (this.currentConnection != null && !this.currentConnection.isClosed()) { + this.currentConnection.close(); + } + } catch (final SQLException e) { + // Ignore an exception + } + + if (this.hostListProvider != null && this.hostListProvider instanceof CanReleaseResources) { + final CanReleaseResources canReleaseResourcesObject = (CanReleaseResources) this.hostListProvider; + canReleaseResourcesObject.releaseResources(); + } + } + + @Override + public boolean isNetworkException(Throwable throwable) { + return this.isNetworkException(throwable, this.targetDriverDialect); + } + + @Override + public boolean isNetworkException(final Throwable throwable, @Nullable TargetDriverDialect targetDriverDialect) { + if (this.exceptionHandler != null) { + return this.exceptionHandler.isNetworkException(throwable, targetDriverDialect); + } + return this.exceptionManager.isNetworkException(this.dbDialect, throwable, targetDriverDialect); + } + + @Override + public boolean isNetworkException(final String sqlState) { + if (this.exceptionHandler != null) { + return this.exceptionHandler.isNetworkException(sqlState); + } + return this.exceptionManager.isNetworkException(this.dbDialect, sqlState); + } + + @Override + public boolean isLoginException(Throwable throwable) { + return this.isLoginException(throwable, this.targetDriverDialect); + } + + @Override + public boolean isLoginException(final Throwable throwable, @Nullable TargetDriverDialect targetDriverDialect) { + if (this.exceptionHandler != null) { + return this.exceptionHandler.isLoginException(throwable, targetDriverDialect); + } + return this.exceptionManager.isLoginException(this.dbDialect, throwable, targetDriverDialect); + } + + @Override + public boolean isLoginException(final String sqlState) { + if (this.exceptionHandler != null) { + return this.exceptionHandler.isLoginException(sqlState); + } + return this.exceptionManager.isLoginException(this.dbDialect, sqlState); + } + + @Override + public Dialect getDialect() { + return this.dbDialect; + } + + @Override + public TargetDriverDialect getTargetDriverDialect() { + return this.targetDriverDialect; + } + + public void updateDialect(final @NonNull Connection connection) throws SQLException { + LOGGER.warning( + Messages.get( + "MonitorPluginService.methodNotIntendedForUse", new Object[] {"MonitorPluginService.updateDialect"})); + throw new NotImplementedException(); + } + + @Override + public HostSpec identifyConnection(Connection connection) throws SQLException { + return this.getHostListProvider().identifyConnection(connection); + } + + @Override + public void fillAliases(Connection connection, HostSpec hostSpec) throws SQLException { + if (hostSpec == null) { + return; + } + + if (!hostSpec.getAliases().isEmpty()) { + LOGGER.finest(() -> Messages.get("PluginServiceImpl.nonEmptyAliases", new Object[] {hostSpec.getAliases()})); + return; + } + + hostSpec.addAlias(hostSpec.asAlias()); + + // Add the host name and port, this host name is usually the internal IP address. + try (final Statement stmt = connection.createStatement()) { + try (final ResultSet rs = stmt.executeQuery(this.getDialect().getHostAliasQuery())) { + while (rs.next()) { + hostSpec.addAlias(rs.getString(1)); + } + } + } catch (final SQLException sqlException) { + // log and ignore + LOGGER.finest(() -> Messages.get("PluginServiceImpl.failedToRetrieveHostPort")); + } + + // Add the instance endpoint if the current connection is associated with a topology aware database cluster. + final HostSpec host = this.identifyConnection(connection); + if (host != null) { + hostSpec.addAlias(host.asAliases().toArray(new String[] {})); + } + } + + @Override + public HostSpecBuilder getHostSpecBuilder() { + return new HostSpecBuilder(new HostAvailabilityStrategyFactory().create(this.props)); + } + + @Override + public Properties getProperties() { + return this.props; + } + + public TelemetryFactory getTelemetryFactory() { + return this.pluginManager.getTelemetryFactory(); + } + + public String getTargetName() { + return this.pluginManager.getDefaultConnProvider().getTargetName(); + } + + @Override + public @NonNull SessionStateService getSessionStateService() { + return this.sessionStateService; + } + + public T getPlugin(final Class pluginClazz) { + for (ConnectionPlugin p : this.pluginManager.plugins) { + if (pluginClazz.isAssignableFrom(p.getClass())) { + return pluginClazz.cast(p); + } + } + return null; + } + + public static void clearCache() { + hostAvailabilityExpiringCache.clear(); + } +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index d486f8eb1..0091ce057 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -19,57 +19,53 @@ import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; +import software.amazon.jdbc.ConnectionPluginManager; import software.amazon.jdbc.ConnectionProvider; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.PropertyDefinition; +import software.amazon.jdbc.MonitorPluginService; +import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.ServiceContainerImpl; -import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; -import software.amazon.jdbc.wrapper.ConnectionWrapper; public class ConnectionServiceImpl implements ConnectionService { - protected ServiceContainer serviceContainer; - protected ConnectionProvider connectionProvider; - protected TargetDriverDialect driverDialect; - protected String targetDriverProtocol; + protected final ServiceContainer serviceContainer; + protected final ConnectionProvider connectionProvider; + protected final TargetDriverDialect driverDialect; + protected final String targetDriverProtocol; + protected final ConnectionPluginManager pluginManager; + protected final MonitorPluginService monitorPluginService; public ConnectionServiceImpl( ServiceContainer serviceContainer, ConnectionProvider connectionProvider, TargetDriverDialect driverDialect, - String targetDriverProtocol) { + Dialect dbDialect, + String targetDriverProtocol, + Properties props) throws SQLException { this.serviceContainer = serviceContainer; this.connectionProvider = connectionProvider; this.driverDialect = driverDialect; this.targetDriverProtocol = targetDriverProtocol; + this.pluginManager = new ConnectionPluginManager( + this.connectionProvider, + null, + null, + // TODO: is it okay to share the telemetry factory? + serviceContainer.getTelemetryFactory()); + this.monitorPluginService = new MonitorPluginService( + this.serviceContainer, + props, + this.targetDriverProtocol, + this.driverDialect, + dbDialect); + + this.pluginManager.init(this.monitorPluginService, props, this.monitorPluginService, null); } @Override + // TODO: do we need the props parameter or should we just use the props passed to the constructor? Not clear if they + // will differ. public Connection createAuxiliaryConnection(HostSpec hostSpec, Properties props) throws SQLException { - final Properties auxiliaryProps = PropertyUtils.copyProperties(props); - final String databaseName = PropertyDefinition.DATABASE.getString(auxiliaryProps) != null - ? PropertyDefinition.DATABASE.getString(auxiliaryProps) - : ""; - final String connString = this.targetDriverProtocol + hostSpec.getUrl() + databaseName; - - // The auxiliary connection should have its own separate service container since the original container holds - // services that should not be shared between connections (for example, PluginService). - ServiceContainerImpl auxiliaryServiceContainer = new ServiceContainerImpl( - this.serviceContainer.getStorageService(), - this.serviceContainer.getMonitorService(), - new DefaultTelemetryFactory(auxiliaryProps)); - - // TODO: does the auxiliary connection need its own ConnectionProvider/TargetDriverDialect, or is it okay to share? - return new ConnectionWrapper( - auxiliaryServiceContainer, - auxiliaryProps, - connString, - this.connectionProvider, - null, - this.driverDialect, - null - ); + return this.pluginManager.forceConnect(this.targetDriverProtocol, hostSpec, props, false, null); } } diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 6b087e7c0..0c3eda6a0 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -295,6 +295,8 @@ MonitorImpl.stopMonitoringThreadNewContext=Stop monitoring thread for checking n MonitorImpl.startMonitoringThread=Start monitoring thread for {0}. MonitorImpl.stopMonitoringThread=Stop monitoring thread for {0}. +MonitorPluginService.methodNotIntendedForUse= + # efm.MonitorServiceImpl MonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should not be empty. From c1140c68ac5f90eda7e7727638a59b041dc18ace Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 5 May 2025 09:35:29 -0700 Subject: [PATCH 088/149] ConnectionServiceImpl changes wip --- .../jdbc/benchmarks/PluginBenchmarks.java | 142 ++++-------------- .../testplugin/TestConnectionWrapper.java | 35 +++-- .../amazon/jdbc/MonitorPluginService.java | 38 +++-- .../amazon/jdbc/plugin/efm/MonitorImpl.java | 6 +- .../amazon/jdbc/plugin/efm2/MonitorImpl.java | 6 +- .../ClusterAwareReaderFailoverHandler.java | 6 +- .../ClusterAwareWriterFailoverHandler.java | 9 +- .../limitless/LimitlessRouterMonitor.java | 11 +- .../NodeResponseTimeMonitor.java | 7 +- .../connection/ConnectionServiceImpl.java | 43 +++--- .../jdbc/wrapper/ConnectionWrapper.java | 32 ++-- .../jdbc/plugin/efm/MonitorImplTest.java | 1 - ...ClusterAwareReaderFailoverHandlerTest.java | 1 - ...ClusterAwareWriterFailoverHandlerTest.java | 1 - 14 files changed, 136 insertions(+), 202 deletions(-) diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java index 09147875a..eb7c13518 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java @@ -95,6 +95,7 @@ public class PluginBenchmarks { @Mock private MonitorService mockMonitorService; @Mock private ConnectionService mockConnectionService; @Mock private PluginService mockPluginService; + @Mock private TargetDriverDialect mockTargetDriverDialect; @Mock private Dialect mockDialect; @Mock private ConnectionPluginManager mockConnectionPluginManager; @Mock private TelemetryFactory mockTelemetryFactory; @@ -163,27 +164,18 @@ public void initAndReleaseBaseLine() { @Benchmark public ConnectionWrapper initAndReleaseWithExecutionTimePlugin() throws SQLException { - try (ConnectionWrapper wrapper = new TestConnectionWrapper( - useExecutionTimePlugin(), - CONNECTION_STRING, - mockConnectionPluginManager, - mockTelemetryFactory, - mockPluginService, - mockHostListProviderService, - mockPluginManagerService, - mockStorageService, - mockMonitorService, - mockConnectionService)) { + try (ConnectionWrapper wrapper = getConnectionWrapper(useExecutionTimePlugin(), CONNECTION_STRING)) { wrapper.releaseResources(); return wrapper; } } - @Benchmark - public ConnectionWrapper initAndReleaseWithAuroraHostListPlugin() throws SQLException { - try (ConnectionWrapper wrapper = new TestConnectionWrapper( - useAuroraHostListPlugin(), - CONNECTION_STRING, + private ConnectionWrapper getConnectionWrapper(Properties props, String connString) throws SQLException { + return new TestConnectionWrapper( + props, + connString, + mockConnectionProvider, + mockTargetDriverDialect, mockConnectionPluginManager, mockTelemetryFactory, mockPluginService, @@ -191,7 +183,12 @@ public ConnectionWrapper initAndReleaseWithAuroraHostListPlugin() throws SQLExce mockPluginManagerService, mockStorageService, mockMonitorService, - mockConnectionService)) { + mockConnectionService); + } + + @Benchmark + public ConnectionWrapper initAndReleaseWithAuroraHostListPlugin() throws SQLException { + try (ConnectionWrapper wrapper = getConnectionWrapper(useAuroraHostListPlugin(), CONNECTION_STRING)) { wrapper.releaseResources(); return wrapper; } @@ -199,17 +196,8 @@ public ConnectionWrapper initAndReleaseWithAuroraHostListPlugin() throws SQLExce @Benchmark public ConnectionWrapper initAndReleaseWithExecutionTimeAndAuroraHostListPlugins() throws SQLException { - try (ConnectionWrapper wrapper = new TestConnectionWrapper( - useExecutionTimeAndAuroraHostListPlugins(), - CONNECTION_STRING, - mockConnectionPluginManager, - mockTelemetryFactory, - mockPluginService, - mockHostListProviderService, - mockPluginManagerService, - mockStorageService, - mockMonitorService, - mockConnectionService)) { + try (ConnectionWrapper wrapper = + getConnectionWrapper(useExecutionTimeAndAuroraHostListPlugins(), CONNECTION_STRING)) { wrapper.releaseResources(); return wrapper; } @@ -217,17 +205,7 @@ public ConnectionWrapper initAndReleaseWithExecutionTimeAndAuroraHostListPlugins @Benchmark public ConnectionWrapper initAndReleaseWithReadWriteSplittingPlugin() throws SQLException { - try (ConnectionWrapper wrapper = new TestConnectionWrapper( - useReadWriteSplittingPlugin(), - CONNECTION_STRING, - mockConnectionPluginManager, - mockTelemetryFactory, - mockPluginService, - mockHostListProviderService, - mockPluginManagerService, - mockStorageService, - mockMonitorService, - mockConnectionService)) { + try (ConnectionWrapper wrapper = getConnectionWrapper(useReadWriteSplittingPlugin(), CONNECTION_STRING)) { wrapper.releaseResources(); return wrapper; } @@ -236,17 +214,8 @@ public ConnectionWrapper initAndReleaseWithReadWriteSplittingPlugin() throws SQL @Benchmark public ConnectionWrapper initAndReleaseWithAuroraHostListAndReadWriteSplittingPlugin() throws SQLException { - try (ConnectionWrapper wrapper = new TestConnectionWrapper( - useAuroraHostListAndReadWriteSplittingPlugin(), - PG_CONNECTION_STRING, - mockConnectionPluginManager, - mockTelemetryFactory, - mockPluginService, - mockHostListProviderService, - mockPluginManagerService, - mockStorageService, - mockMonitorService, - mockConnectionService)) { + try (ConnectionWrapper wrapper = + getConnectionWrapper(useAuroraHostListAndReadWriteSplittingPlugin(), PG_CONNECTION_STRING)) { wrapper.releaseResources(); return wrapper; } @@ -257,17 +226,7 @@ public ConnectionWrapper initAndReleaseWithReadWriteSplittingPlugin_internalConn HikariPooledConnectionProvider provider = new HikariPooledConnectionProvider((hostSpec, props) -> new HikariConfig()); Driver.setCustomConnectionProvider(provider); - try (ConnectionWrapper wrapper = new TestConnectionWrapper( - useReadWriteSplittingPlugin(), - CONNECTION_STRING, - mockConnectionPluginManager, - mockTelemetryFactory, - mockPluginService, - mockHostListProviderService, - mockPluginManagerService, - mockStorageService, - mockMonitorService, - mockConnectionService)) { + try (ConnectionWrapper wrapper = getConnectionWrapper(useReadWriteSplittingPlugin(), CONNECTION_STRING)) { wrapper.releaseResources(); ConnectionProviderManager.releaseResources(); Driver.resetCustomConnectionProvider(); @@ -281,17 +240,8 @@ public ConnectionWrapper initAndReleaseWithAuroraHostListAndReadWriteSplittingPl HikariPooledConnectionProvider provider = new HikariPooledConnectionProvider((hostSpec, props) -> new HikariConfig()); Driver.setCustomConnectionProvider(provider); - try (ConnectionWrapper wrapper = new TestConnectionWrapper( - useAuroraHostListAndReadWriteSplittingPlugin(), - PG_CONNECTION_STRING, - mockConnectionPluginManager, - mockTelemetryFactory, - mockPluginService, - mockHostListProviderService, - mockPluginManagerService, - mockStorageService, - mockMonitorService, - mockConnectionService)) { + try (ConnectionWrapper wrapper = getConnectionWrapper( + useAuroraHostListAndReadWriteSplittingPlugin(), PG_CONNECTION_STRING)) { wrapper.releaseResources(); ConnectionProviderManager.releaseResources(); Driver.resetCustomConnectionProvider(); @@ -301,17 +251,7 @@ public ConnectionWrapper initAndReleaseWithAuroraHostListAndReadWriteSplittingPl @Benchmark public Statement executeStatementBaseline() throws SQLException { - try (ConnectionWrapper wrapper = new TestConnectionWrapper( - useExecutionTimePlugin(), - CONNECTION_STRING, - mockConnectionPluginManager, - mockTelemetryFactory, - mockPluginService, - mockHostListProviderService, - mockPluginManagerService, - mockStorageService, - mockMonitorService, - mockConnectionService); + try (ConnectionWrapper wrapper = getConnectionWrapper(useExecutionTimePlugin(), CONNECTION_STRING); Statement statement = wrapper.createStatement()) { return statement; } @@ -320,17 +260,7 @@ public Statement executeStatementBaseline() throws SQLException { @Benchmark public ResultSet executeStatementWithExecutionTimePlugin() throws SQLException { try ( - ConnectionWrapper wrapper = new TestConnectionWrapper( - useExecutionTimePlugin(), - CONNECTION_STRING, - mockConnectionPluginManager, - mockTelemetryFactory, - mockPluginService, - mockHostListProviderService, - mockPluginManagerService, - mockStorageService, - mockMonitorService, - mockConnectionService); + ConnectionWrapper wrapper = getConnectionWrapper(useExecutionTimePlugin(), CONNECTION_STRING); Statement statement = wrapper.createStatement(); ResultSet resultSet = statement.executeQuery("some sql")) { return resultSet; @@ -340,17 +270,7 @@ public ResultSet executeStatementWithExecutionTimePlugin() throws SQLException { @Benchmark public ResultSet executeStatementWithTelemetryDisabled() throws SQLException { try ( - ConnectionWrapper wrapper = new TestConnectionWrapper( - disabledTelemetry(), - CONNECTION_STRING, - mockConnectionPluginManager, - mockTelemetryFactory, - mockPluginService, - mockHostListProviderService, - mockPluginManagerService, - mockStorageService, - mockMonitorService, - mockConnectionService); + ConnectionWrapper wrapper = getConnectionWrapper(disabledTelemetry(), CONNECTION_STRING); Statement statement = wrapper.createStatement(); ResultSet resultSet = statement.executeQuery("some sql")) { return resultSet; @@ -360,17 +280,7 @@ public ResultSet executeStatementWithTelemetryDisabled() throws SQLException { @Benchmark public ResultSet executeStatementWithTelemetry() throws SQLException { try ( - ConnectionWrapper wrapper = new TestConnectionWrapper( - useTelemetry(), - CONNECTION_STRING, - mockConnectionPluginManager, - mockTelemetryFactory, - mockPluginService, - mockHostListProviderService, - mockPluginManagerService, - mockStorageService, - mockMonitorService, - mockConnectionService); + ConnectionWrapper wrapper = getConnectionWrapper(useTelemetry(), CONNECTION_STRING); Statement statement = wrapper.createStatement(); ResultSet resultSet = statement.executeQuery("some sql")) { return resultSet; diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java index 218a45a69..d0ec5c063 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java @@ -20,9 +20,11 @@ import java.util.Properties; import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.ConnectionPluginManager; +import software.amazon.jdbc.ConnectionProvider; import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; @@ -33,18 +35,29 @@ public class TestConnectionWrapper extends ConnectionWrapper { public TestConnectionWrapper( - @NonNull Properties props, - @NonNull String url, - @NonNull ConnectionPluginManager connectionPluginManager, + @NonNull final Properties props, + @NonNull final String url, + @NonNull final ConnectionProvider defaultConnectionProvider, + @NonNull final TargetDriverDialect driverDialect, + @NonNull final ConnectionPluginManager connectionPluginManager, @NonNull final TelemetryFactory telemetryFactory, - @NonNull PluginService pluginService, - @NonNull HostListProviderService hostListProviderService, - @NonNull PluginManagerService pluginManagerService, - @NonNull StorageService storageService, - @NonNull MonitorService monitorService, - @NonNull ConnectionService connectionService) + @NonNull final PluginService pluginService, + @NonNull final HostListProviderService hostListProviderService, + @NonNull final PluginManagerService pluginManagerService, + @NonNull final StorageService storageService, + @NonNull final MonitorService monitorService, + @NonNull final ConnectionService connectionService) throws SQLException { - super(props, url, connectionPluginManager, telemetryFactory, pluginService, hostListProviderService, - pluginManagerService, storageService, monitorService, connectionService); + super( + props, + url, + defaultConnectionProvider, + driverDialect, + connectionPluginManager, + telemetryFactory, + pluginService, + hostListProviderService, + pluginManagerService, + storageService, monitorService, connectionService); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java index ee7592cce..e9f95f234 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java @@ -39,6 +39,7 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.dialect.DialectManager; import software.amazon.jdbc.dialect.DialectProvider; +import software.amazon.jdbc.dialect.HostListProviderSupplier; import software.amazon.jdbc.exceptions.ExceptionHandler; import software.amazon.jdbc.exceptions.ExceptionManager; import software.amazon.jdbc.hostavailability.HostAvailability; @@ -54,7 +55,6 @@ import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -import sun.reflect.generics.reflectiveObjects.NotImplementedException; public class MonitorPluginService implements PluginService, CanReleaseResources, HostListProviderService, PluginManagerService { @@ -67,6 +67,7 @@ public class MonitorPluginService implements PluginService, CanReleaseResources, protected final StorageService storageService; protected final ConnectionPluginManager pluginManager; private final Properties props; + private final String originalUrl; private final String driverProtocol; protected volatile HostListProvider hostListProvider; protected List allHosts = new ArrayList<>(); @@ -89,17 +90,17 @@ public class MonitorPluginService implements PluginService, CanReleaseResources, public MonitorPluginService( @NonNull final ServiceContainer serviceContainer, @NonNull final Properties props, + @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, - @NonNull final TargetDriverDialect targetDriverDialect, - @NonNull final Dialect dbDialect) { + @NonNull final TargetDriverDialect targetDriverDialect) throws SQLException { this( serviceContainer, new ExceptionManager(), props, + originalUrl, targetDriverProtocol, null, targetDriverDialect, - dbDialect, null, null); } @@ -107,18 +108,18 @@ public MonitorPluginService( public MonitorPluginService( @NonNull final ServiceContainer serviceContainer, @NonNull final Properties props, + @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, @NonNull final TargetDriverDialect targetDriverDialect, - @NonNull final Dialect dbDialect, - @Nullable final ConfigurationProfile configurationProfile) { + @Nullable final ConfigurationProfile configurationProfile) throws SQLException { this( serviceContainer, new ExceptionManager(), props, + originalUrl, targetDriverProtocol, null, targetDriverDialect, - dbDialect, configurationProfile, null); } @@ -127,16 +128,17 @@ public MonitorPluginService( @NonNull final ServiceContainer serviceContainer, @NonNull final ExceptionManager exceptionManager, @NonNull final Properties props, + @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, @Nullable final DialectProvider dialectProvider, @NonNull final TargetDriverDialect targetDriverDialect, - @NonNull final Dialect dbDialect, @Nullable final ConfigurationProfile configurationProfile, - @Nullable final SessionStateService sessionStateService) { + @Nullable final SessionStateService sessionStateService) throws SQLException { this.serviceContainer = serviceContainer; this.storageService = serviceContainer.getStorageService(); this.pluginManager = serviceContainer.getConnectionPluginManager(); this.props = props; + this.originalUrl = originalUrl; this.driverProtocol = targetDriverProtocol; this.configurationProfile = configurationProfile; this.exceptionManager = exceptionManager; @@ -154,7 +156,7 @@ public MonitorPluginService( ? this.configurationProfile.getExceptionHandler() : null; - this.dbDialect = dbDialect; + this.dialectProvider.getDialect(this.driverProtocol, this.originalUrl, this.props); } @Override @@ -710,11 +712,19 @@ public TargetDriverDialect getTargetDriverDialect() { return this.targetDriverDialect; } + @Override public void updateDialect(final @NonNull Connection connection) throws SQLException { - LOGGER.warning( - Messages.get( - "MonitorPluginService.methodNotIntendedForUse", new Object[] {"MonitorPluginService.updateDialect"})); - throw new NotImplementedException(); + final Dialect originalDialect = this.dbDialect; + this.dbDialect = this.dialectProvider.getDialect( + this.originalUrl, + this.initialConnectionHostSpec, + connection); + if (originalDialect == this.dbDialect) { + return; + } + + final HostListProviderSupplier supplier = this.dbDialect.getHostListProvider(); + this.setHostListProvider(supplier.getProvider(this.props, this.originalUrl, this.serviceContainer)); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java index fad5d6f5f..bc0dfdc0c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java @@ -31,7 +31,6 @@ import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.StringUtils; -import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -62,7 +61,6 @@ static class ConnectionStatus { final Queue activeContexts = new ConcurrentLinkedQueue<>(); private final Queue newContexts = new ConcurrentLinkedQueue<>(); - private final ConnectionService connectionService; private final PluginService pluginService; private final TelemetryFactory telemetryFactory; private final Properties properties; @@ -98,7 +96,6 @@ public MonitorImpl( final long monitorDisposalTimeMillis, @NonNull final MonitorThreadContainer threadContainer) { this.pluginService = serviceContainer.getPluginService(); - this.connectionService = serviceContainer.getConnectionService(); this.telemetryFactory = serviceContainer.getTelemetryFactory(); this.hostSpec = hostSpec; this.properties = properties; @@ -336,7 +333,8 @@ ConnectionStatus checkConnectionStatus(final long shortestFailureDetectionInterv LOGGER.finest(() -> "Opening a monitoring connection to " + this.hostSpec.getUrl()); startNano = this.getCurrentTimeNano(); - this.monitoringConn = this.connectionService.createAuxiliaryConnection(this.hostSpec, monitoringConnProperties); + // TODO: replace with ConnectionService#createAuxiliaryConnection + this.monitoringConn = this.pluginService.forceConnect(this.hostSpec, monitoringConnProperties); LOGGER.finest(() -> "Opened monitoring connection: " + this.monitoringConn); return new ConnectionStatus(true, this.getCurrentTimeNano() - startNano); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java index 4fde46b12..95792eb52 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java @@ -41,7 +41,6 @@ import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.StringUtils; -import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -64,7 +63,6 @@ public class MonitorImpl implements Monitor { private final Map>> newContexts = new ConcurrentHashMap<>(); private final PluginService pluginService; - private final ConnectionService connectionService; private final TelemetryFactory telemetryFactory; private final Properties properties; private final HostSpec hostSpec; @@ -112,7 +110,6 @@ public MonitorImpl( final TelemetryCounter abortedConnectionsCounter) { this.pluginService = serviceContainer.getPluginService(); - this.connectionService = serviceContainer.getConnectionService(); this.telemetryFactory = serviceContainer.getTelemetryFactory(); this.hostSpec = hostSpec; this.properties = properties; @@ -363,7 +360,8 @@ boolean checkConnectionStatus() { }); LOGGER.finest(() -> "Opening a monitoring connection to " + this.hostSpec.getUrl()); - this.monitoringConn = this.connectionService.createAuxiliaryConnection(this.hostSpec, monitoringConnProperties); + // TODO: replace with ConnectionService#createAuxiliaryConnection + this.monitoringConn = this.pluginService.forceConnect(this.hostSpec, monitoringConnProperties); LOGGER.finest(() -> "Opened monitoring connection: " + this.monitoringConn); return true; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java index 02bf3e637..944d2f38a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java @@ -40,7 +40,6 @@ import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; -import software.amazon.jdbc.util.connection.ConnectionService; /** * An implementation of ReaderFailoverHandler. @@ -65,7 +64,6 @@ public class ClusterAwareReaderFailoverHandler implements ReaderFailoverHandler protected int timeoutMs; protected boolean isStrictReaderRequired; protected final PluginService pluginService; - protected final ConnectionService connectionService; /** * ClusterAwareReaderFailoverHandler constructor. @@ -101,7 +99,6 @@ public ClusterAwareReaderFailoverHandler( final int timeoutMs, final boolean isStrictReaderRequired) { this.pluginService = serviceContainer.getPluginService(); - this.connectionService = serviceContainer.getConnectionService(); this.initialConnectionProps = initialConnectionProps; this.maxFailoverTimeoutMs = maxFailoverTimeoutMs; this.timeoutMs = timeoutMs; @@ -393,7 +390,8 @@ public ReaderFailoverResult call() { final Properties copy = new Properties(); copy.putAll(initialConnectionProps); - final Connection conn = connectionService.createAuxiliaryConnection(this.newHost, copy); + // TODO: replace with ConnectionService#createAuxiliaryConnection + final Connection conn = pluginService.forceConnect(this.newHost, copy); pluginService.setAvailability(this.newHost.asAliases(), HostAvailability.AVAILABLE); if (this.isStrictReaderRequired) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java index da030a5b3..6d1ad3374 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java @@ -39,7 +39,6 @@ import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; -import software.amazon.jdbc.util.connection.ConnectionService; /** * An implementation of WriterFailoverHandler. @@ -58,7 +57,6 @@ public class ClusterAwareWriterFailoverHandler implements WriterFailoverHandler protected int reconnectWriterIntervalMs = 5000; // 5 sec protected Properties initialConnectionProps; protected PluginService pluginService; - protected ConnectionService connectionService; protected ReaderFailoverHandler readerFailoverHandler; private static final WriterFailoverResult DEFAULT_RESULT = new WriterFailoverResult(false, false, null, null, "None"); @@ -68,7 +66,6 @@ public ClusterAwareWriterFailoverHandler( final ReaderFailoverHandler readerFailoverHandler, final Properties initialConnectionProps) { this.pluginService = serviceContainer.getPluginService(); - this.connectionService = serviceContainer.getConnectionService(); this.readerFailoverHandler = readerFailoverHandler; this.initialConnectionProps = initialConnectionProps; } @@ -261,7 +258,8 @@ public WriterFailoverResult call() { conn.close(); } - conn = connectionService.createAuxiliaryConnection(this.originalWriterHost, initialConnectionProps); + // TODO: replace with ConnectionService#createAuxiliaryConnection + conn = pluginService.forceConnect(this.originalWriterHost, initialConnectionProps); pluginService.forceRefreshHostList(conn); latestTopology = pluginService.getAllHosts(); @@ -468,7 +466,8 @@ private boolean connectToWriter(final HostSpec writerCandidate) { new Object[] {writerCandidate.getUrl()})); try { // connect to the new writer - this.currentConnection = connectionService.createAuxiliaryConnection(writerCandidate, initialConnectionProps); + // TODO: replace with ConnectionService#createAuxiliaryConnection + this.currentConnection = pluginService.forceConnect(writerCandidate, initialConnectionProps); pluginService.setAvailability(writerCandidate.asAliases(), HostAvailability.AVAILABLE); return true; } catch (final SQLException exception) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java index 99f3d411a..51361e052 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java @@ -28,11 +28,11 @@ import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.HostSpec; +import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; -import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.storage.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -49,7 +49,7 @@ public class LimitlessRouterMonitor implements AutoCloseable, Runnable { protected final SlidingExpirationCacheWithCleanupThread> limitlessRouterCache; protected final String limitlessRouterCacheKey; protected final @NonNull Properties props; - protected final @NonNull ConnectionService connectionService; + protected final PluginService pluginService; protected final LimitlessQueryHelper queryHelper; protected final TelemetryFactory telemetryFactory; protected Connection monitoringConn = null; @@ -69,7 +69,6 @@ public LimitlessRouterMonitor( final @NonNull String limitlessRouterCacheKey, final @NonNull Properties props, final int intervalMs) { - this.connectionService = serviceContainer.getConnectionService(); this.hostSpec = hostSpec; this.limitlessRouterCache = limitlessRouterCache; this.limitlessRouterCacheKey = limitlessRouterCacheKey; @@ -86,8 +85,9 @@ public LimitlessRouterMonitor( this.props.setProperty(LimitlessConnectionPlugin.WAIT_FOR_ROUTER_INFO.name, "false"); this.intervalMs = intervalMs; + this.pluginService = serviceContainer.getPluginService(); this.telemetryFactory = serviceContainer.getTelemetryFactory(); - this.queryHelper = new LimitlessQueryHelper(serviceContainer.getPluginService()); + this.queryHelper = new LimitlessQueryHelper(this.pluginService); this.threadPool.submit(this); this.threadPool.shutdown(); // No more task are accepted by pool. } @@ -197,7 +197,8 @@ private void openConnection() throws SQLException { LOGGER.finest(() -> Messages.get( "LimitlessRouterMonitor.openingConnection", new Object[] {this.hostSpec.getUrl()})); - this.monitoringConn = this.connectionService.createAuxiliaryConnection(this.hostSpec, this.props); + // TODO: replace with ConnectionService#createAuxiliaryConnection + this.monitoringConn = this.pluginService.forceConnect(this.hostSpec, this.props); LOGGER.finest(() -> Messages.get( "LimitlessRouterMonitor.openedConnection", new Object[] {this.monitoringConn})); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java index b83b6647b..e09f2145c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java @@ -34,7 +34,6 @@ import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.StringUtils; -import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryGauge; @@ -57,7 +56,6 @@ public class NodeResponseTimeMonitor implements AutoCloseable, Runnable { private final @NonNull Properties props; private final @NonNull PluginService pluginService; - private final @NonNull ConnectionService connectionService; private final TelemetryFactory telemetryFactory; private final TelemetryGauge responseTimeMsGauge; @@ -65,6 +63,7 @@ public class NodeResponseTimeMonitor implements AutoCloseable, Runnable { private Connection monitoringConn = null; + // TODO: remove threadPool and submit monitors to MonitorService instead private final ExecutorService threadPool = Executors.newFixedThreadPool(1, runnableTarget -> { final Thread monitoringThread = new Thread(runnableTarget); monitoringThread.setDaemon(true); @@ -78,7 +77,6 @@ public NodeResponseTimeMonitor( int intervalMs) { this.pluginService = serviceContainer.getPluginService(); - this.connectionService = serviceContainer.getConnectionService(); this.hostSpec = hostSpec; this.props = props; this.intervalMs = intervalMs; @@ -219,7 +217,8 @@ private void openConnection() { LOGGER.finest(() -> Messages.get( "NodeResponseTimeMonitor.openingConnection", new Object[] {this.hostSpec.getUrl()})); - this.monitoringConn = this.connectionService.createAuxiliaryConnection(this.hostSpec, monitoringConnProperties); + // TODO: replace with ConnectionService#createAuxiliaryConnection + this.monitoringConn = this.pluginService.forceConnect(this.hostSpec, monitoringConnProperties); LOGGER.finest(() -> Messages.get( "NodeResponseTimeMonitor.openedConnection", new Object[] {this.monitoringConn})); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index 0091ce057..d4a4609f0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -23,49 +23,52 @@ import software.amazon.jdbc.ConnectionProvider; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.MonitorPluginService; -import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.ServiceContainerImpl; +import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class ConnectionServiceImpl implements ConnectionService { - protected final ServiceContainer serviceContainer; - protected final ConnectionProvider connectionProvider; - protected final TargetDriverDialect driverDialect; protected final String targetDriverProtocol; protected final ConnectionPluginManager pluginManager; - protected final MonitorPluginService monitorPluginService; public ConnectionServiceImpl( - ServiceContainer serviceContainer, + StorageService storageService, + MonitorService monitorService, + TelemetryFactory telemetryFactory, ConnectionProvider connectionProvider, TargetDriverDialect driverDialect, - Dialect dbDialect, String targetDriverProtocol, + String originalUrl, Properties props) throws SQLException { - this.serviceContainer = serviceContainer; - this.connectionProvider = connectionProvider; - this.driverDialect = driverDialect; this.targetDriverProtocol = targetDriverProtocol; + + ServiceContainer serviceContainer = new ServiceContainerImpl(storageService, monitorService, telemetryFactory); this.pluginManager = new ConnectionPluginManager( - this.connectionProvider, + connectionProvider, null, null, // TODO: is it okay to share the telemetry factory? - serviceContainer.getTelemetryFactory()); - this.monitorPluginService = new MonitorPluginService( - this.serviceContainer, + telemetryFactory); + serviceContainer.setConnectionPluginManager(this.pluginManager); + + MonitorPluginService monitorPluginService = new MonitorPluginService( + serviceContainer, props, + originalUrl, this.targetDriverProtocol, - this.driverDialect, - dbDialect); + driverDialect); + serviceContainer.setHostListProviderService(monitorPluginService); + serviceContainer.setPluginService(monitorPluginService); + serviceContainer.setPluginManagerService(monitorPluginService); - this.pluginManager.init(this.monitorPluginService, props, this.monitorPluginService, null); + this.pluginManager.init(monitorPluginService, props, monitorPluginService, null); } @Override - // TODO: do we need the props parameter or should we just use the props passed to the constructor? Not clear if they - // will differ. public Connection createAuxiliaryConnection(HostSpec hostSpec, Properties props) throws SQLException { - return this.pluginManager.forceConnect(this.targetDriverProtocol, hostSpec, props, false, null); + return this.pluginManager.forceConnect(this.targetDriverProtocol, hostSpec, props, true, null); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index fd380ea06..2137a6432 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -87,7 +87,7 @@ public ConnectionWrapper( @NonNull final String url, @NonNull final ConnectionProvider defaultConnectionProvider, @Nullable final ConnectionProvider effectiveConnectionProvider, - @NonNull final TargetDriverDialect targetDriverDialect, + @NonNull final TargetDriverDialect driverDialect, @Nullable final ConfigurationProfile configurationProfile) throws SQLException { @@ -111,20 +111,13 @@ public ConnectionWrapper( props, url, this.targetDriverProtocol, - targetDriverDialect, + driverDialect, this.configurationProfile); serviceContainer.setHostListProviderService(pluginService); serviceContainer.setPluginService(pluginService); serviceContainer.setPluginManagerService(pluginService); - ConnectionService connectionService = new ConnectionServiceImpl( - serviceContainer, - defaultConnectionProvider, - targetDriverDialect, - this.targetDriverProtocol); - serviceContainer.setConnectionService(connectionService); - - init(props, serviceContainer); + init(props, serviceContainer, defaultConnectionProvider, driverDialect); if (PropertyDefinition.LOG_UNCLOSED_CONNECTIONS.getBoolean(props)) { this.openConnectionStacktrace = new Throwable(Messages.get("ConnectionWrapper.unclosedConnectionInstantiated")); @@ -135,6 +128,8 @@ public ConnectionWrapper( protected ConnectionWrapper( @NonNull final Properties props, @NonNull final String url, + @NonNull final ConnectionProvider defaultConnectionProvider, + @NonNull final TargetDriverDialect driverDialect, @NonNull final ConnectionPluginManager connectionPluginManager, @NonNull final TelemetryFactory telemetryFactory, @NonNull final PluginService pluginService, @@ -160,12 +155,14 @@ protected ConnectionWrapper( pluginManagerService ); - init(props, serviceContainer); + init(props, serviceContainer, defaultConnectionProvider, driverDialect); } protected void init( final Properties props, - final ServiceContainer serviceContainer) throws SQLException { + final ServiceContainer serviceContainer, + final ConnectionProvider defaultConnectionProvider, + final TargetDriverDialect driverDialect) throws SQLException { this.pluginManager = serviceContainer.getConnectionPluginManager(); this.telemetryFactory = serviceContainer.getTelemetryFactory(); this.pluginService = serviceContainer.getPluginService(); @@ -186,6 +183,17 @@ protected void init( this.pluginService.refreshHostList(); + ConnectionService connectionService = new ConnectionServiceImpl( + serviceContainer.getStorageService(), + serviceContainer.getMonitorService(), + serviceContainer.getTelemetryFactory(), + defaultConnectionProvider, + driverDialect, + this.targetDriverProtocol, + this.originalUrl, + props); + serviceContainer.setConnectionService(connectionService); + if (this.pluginService.getCurrentConnection() == null) { final Connection conn = this.pluginManager.connect( diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java index 6ece833d8..c8a5bfea8 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java @@ -94,7 +94,6 @@ void init() throws SQLException { when(booleanProperty.getStringValue()).thenReturn(Boolean.TRUE.toString()); when(longProperty.getValue()).thenReturn(SHORT_INTERVAL_MILLIS); when(serviceContainer.getPluginService()).thenReturn(pluginService); - when(serviceContainer.getConnectionService()).thenReturn(connectionService); when(serviceContainer.getTelemetryFactory()).thenReturn(telemetryFactory); when(connectionService.createAuxiliaryConnection(any(HostSpec.class), any(Properties.class))) .thenReturn(connection); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java index 4d8766e1c..018a033b0 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java @@ -85,7 +85,6 @@ class ClusterAwareReaderFailoverHandlerTest { void setUp() { closeable = MockitoAnnotations.openMocks(this); when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); - when(mockServiceContainer.getConnectionService()).thenReturn(mockConnectionService); } @AfterEach diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java index 2fff232b4..6137d8315 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java @@ -83,7 +83,6 @@ class ClusterAwareWriterFailoverHandlerTest { void setUp() { closeable = MockitoAnnotations.openMocks(this); when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); - when(mockServiceContainer.getConnectionService()).thenReturn(mockConnectionService); writer.addAlias("writer-host"); newWriterHost.addAlias("new-writer-host"); readerA.addAlias("reader-a-host"); From f1b3fce363f3a15c0a7c5524c62761889278b56a Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 6 May 2025 17:14:19 -0700 Subject: [PATCH 089/149] wip --- .../amazon/jdbc/MonitorPluginService.java | 6 +++ .../software/amazon/jdbc/PluginService.java | 3 +- .../amazon/jdbc/PluginServiceImpl.java | 5 +++ .../hostlistprovider/RdsHostListProvider.java | 2 +- .../MonitoringRdsHostListProvider.java | 30 ++++++++++---- .../MonitoringRdsMultiAzHostListProvider.java | 15 +++++-- .../customendpoint/CustomEndpointPlugin.java | 19 ++++++--- .../ClusterAwareReaderFailoverHandler.java | 3 +- .../ClusterAwareWriterFailoverHandler.java | 6 ++- .../util/connection/ConnectionService.java | 3 ++ .../connection/ConnectionServiceImpl.java | 8 ++++ .../util/monitoring/MonitorInitializer.java | 25 ++++++++++++ .../jdbc/util/monitoring/MonitorService.java | 19 +++++++-- .../util/monitoring/MonitorServiceImpl.java | 39 ++++++++++++++++++- .../CustomEndpointPluginTest.java | 2 +- .../jdbc/plugin/efm/ConcurrencyTests.java | 5 +++ 16 files changed, 163 insertions(+), 27 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorInitializer.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java index e9f95f234..041c5166a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java @@ -206,6 +206,12 @@ public HostSpec getInitialConnectionHostSpec() { return this.initialConnectionHostSpec; } + @Override + public String getOriginalUrl() { + return this.originalUrl; + } + + @Override public ServiceContainer getServiceContainer() { return this.serviceContainer; diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java index b9a8edee2..84ae9a0bb 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java @@ -30,7 +30,6 @@ import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; /** @@ -82,6 +81,8 @@ EnumSet setCurrentConnection( HostSpec getInitialConnectionHostSpec(); + String getOriginalUrl(); + ServiceContainer getServiceContainer(); /** diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index db6ac0f6f..8c51b387a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -211,6 +211,11 @@ public HostSpec getInitialConnectionHostSpec() { return this.initialConnectionHostSpec; } + @Override + public String getOriginalUrl() { + return this.originalUrl; + } + @Override public ServiceContainer getServiceContainer() { return this.serviceContainer; diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index bc56eeee4..d5c3b8141 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -283,7 +283,7 @@ protected FetchTopologyResult getTopology(final Connection conn, final boolean f } } - protected void clusterIdChanged(final String oldClusterId) { + protected void clusterIdChanged(final String oldClusterId) throws SQLException { // do nothing } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index 88be14ea7..932699016 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -31,6 +31,7 @@ import software.amazon.jdbc.cleanup.CanReleaseResources; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider; import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.Topology; @@ -81,18 +82,24 @@ protected void init() throws SQLException { super.init(); } - protected ClusterTopologyMonitor initMonitor() { + protected ClusterTopologyMonitor initMonitor() throws SQLException { return monitorService.runIfAbsent( ClusterTopologyMonitorImpl.class, this.clusterId, - () -> new ClusterTopologyMonitorImpl( + this.storageService, + this.pluginService.getTelemetryFactory(), + this.pluginService.getTargetDriverDialect(), + this.pluginService.getDriverProtocol(), + this.originalUrl, + this.properties, + (ConnectionService connectionService, PluginService monitorPluginService) -> new ClusterTopologyMonitorImpl( this.clusterId, - this.serviceContainer.getStorageService(), + this.storageService, this.monitorService, - this.serviceContainer.getConnectionService(), + connectionService, this.initialHostSpec, this.properties, - this.pluginService, + monitorPluginService, this.serviceContainer.getHostListProviderService(), this.clusterInstanceTemplate, this.refreshRateNano, @@ -117,11 +124,20 @@ protected List queryForTopology(final Connection conn) throws SQLExcep } @Override - protected void clusterIdChanged(final String oldClusterId) { + protected void clusterIdChanged(final String oldClusterId) throws SQLException { final ClusterTopologyMonitorImpl existingMonitor = monitorService.get(ClusterTopologyMonitorImpl.class, oldClusterId); if (existingMonitor != null) { - monitorService.runIfAbsent(ClusterTopologyMonitorImpl.class, this.clusterId, () -> existingMonitor); + monitorService.runIfAbsent( + ClusterTopologyMonitorImpl.class, + this.clusterId, + this.storageService, + this.pluginService.getTelemetryFactory(), + this.pluginService.getTargetDriverDialect(), + this.pluginService.getDriverProtocol(), + this.originalUrl, + this.properties, + (connectionService, pluginService) -> existingMonitor); assert monitorService.get(ClusterTopologyMonitorImpl.class, this.clusterId) == existingMonitor; existingMonitor.setClusterId(this.clusterId); monitorService.remove(ClusterTopologyMonitorImpl.class, oldClusterId); diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java index 025321808..d9c4c90b2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java @@ -16,6 +16,7 @@ package software.amazon.jdbc.hostlistprovider.monitoring; +import java.sql.SQLException; import java.util.Properties; import java.util.logging.Logger; import software.amazon.jdbc.util.ServiceContainer; @@ -49,17 +50,23 @@ public MonitoringRdsMultiAzHostListProvider( } @Override - protected ClusterTopologyMonitor initMonitor() { + protected ClusterTopologyMonitor initMonitor() throws SQLException { return monitorService.runIfAbsent(MultiAzClusterTopologyMonitorImpl.class, this.clusterId, - () -> new MultiAzClusterTopologyMonitorImpl( + this.storageService, + this.pluginService.getTelemetryFactory(), + this.pluginService.getTargetDriverDialect(), + this.pluginService.getDriverProtocol(), + this.originalUrl, + this.properties, + (connectionService, pluginService) -> new MultiAzClusterTopologyMonitorImpl( this.clusterId, this.storageService, this.monitorService, - this.serviceContainer.getConnectionService(), + connectionService, this.initialHostSpec, this.properties, - this.pluginService, + pluginService, this.hostListProviderService, this.clusterInstanceTemplate, this.refreshRateNano, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java index 1cda9a9d4..f44ff4f7b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java @@ -30,6 +30,7 @@ import software.amazon.jdbc.AwsWrapperProperty; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.JdbcCallable; +import software.amazon.jdbc.PluginService; import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.authentication.AwsCredentialsManager; import software.amazon.jdbc.plugin.AbstractConnectionPlugin; @@ -90,6 +91,7 @@ public class CustomEndpointPlugin extends AbstractConnectionPlugin { } protected final ServiceContainer serviceContainer; + protected final PluginService pluginService; protected final StorageService storageService; protected final MonitorService monitorService; protected final TelemetryFactory telemetryFactory; @@ -134,6 +136,7 @@ public CustomEndpointPlugin( final Properties props, final BiFunction rdsClientFunc) { this.serviceContainer = serviceContainer; + this.pluginService = serviceContainer.getPluginService(); this.storageService = serviceContainer.getStorageService(); this.monitorService = serviceContainer.getMonitorService(); this.telemetryFactory = serviceContainer.getTelemetryFactory(); @@ -203,13 +206,19 @@ public Connection connect( * @param props The connection properties. * @return {@link CustomEndpointMonitor} */ - protected CustomEndpointMonitor createMonitorIfAbsent(Properties props) { + protected CustomEndpointMonitor createMonitorIfAbsent(Properties props) throws SQLException { return this.serviceContainer.getMonitorService().runIfAbsent( CustomEndpointMonitorImpl.class, - this.customEndpointHostSpec.getHost(), - () -> new CustomEndpointMonitorImpl( - this.serviceContainer.getMonitorService(), - this.serviceContainer.getStorageService(), + this.customEndpointHostSpec.getUrl(), + this.storageService, + this.pluginService.getTelemetryFactory(), + this.pluginService.getTargetDriverDialect(), + this.pluginService.getDriverProtocol(), + this.pluginService.getOriginalUrl(), + this.props, + (connectionService, pluginService) -> new CustomEndpointMonitorImpl( + this.monitorService, + this.storageService, this.serviceContainer.getTelemetryFactory(), this.customEndpointHostSpec, this.customEndpointId, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java index 944d2f38a..256ca4e25 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java @@ -390,7 +390,8 @@ public ReaderFailoverResult call() { final Properties copy = new Properties(); copy.putAll(initialConnectionProps); - // TODO: replace with ConnectionService#createAuxiliaryConnection + // TODO: assess whether multi-threaded access to the plugin service is safe. The same plugin service is used by + // both the ConnectionWrapper and this ConnectionAttemptTask in separate threads. final Connection conn = pluginService.forceConnect(this.newHost, copy); pluginService.setAvailability(this.newHost.asAliases(), HostAvailability.AVAILABLE); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java index 6d1ad3374..72ec31bb7 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java @@ -258,7 +258,8 @@ public WriterFailoverResult call() { conn.close(); } - // TODO: replace with ConnectionService#createAuxiliaryConnection + // TODO: assess whether multi-threaded access to the plugin service is safe. The same plugin service is used + // by both the ConnectionWrapper and this ReconnectToWriterHandler in separate threads. conn = pluginService.forceConnect(this.originalWriterHost, initialConnectionProps); pluginService.forceRefreshHostList(conn); latestTopology = pluginService.getAllHosts(); @@ -466,7 +467,8 @@ private boolean connectToWriter(final HostSpec writerCandidate) { new Object[] {writerCandidate.getUrl()})); try { // connect to the new writer - // TODO: replace with ConnectionService#createAuxiliaryConnection + // TODO: assess whether multi-threaded access to the plugin service is safe. The same plugin service is used + // by both the ConnectionWrapper and this WaitForNewWriterHandler in separate threads. this.currentConnection = pluginService.forceConnect(writerCandidate, initialConnectionProps); pluginService.setAvailability(writerCandidate.asAliases(), HostAvailability.AVAILABLE); return true; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java index b985d4307..7e8c2cee3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java @@ -20,6 +20,7 @@ import java.sql.SQLException; import java.util.Properties; import software.amazon.jdbc.HostSpec; +import software.amazon.jdbc.PluginService; public interface ConnectionService { /** @@ -31,4 +32,6 @@ public interface ConnectionService { * @return a new connection to the given host using the given props. */ Connection createAuxiliaryConnection(HostSpec hostSpec, Properties props) throws SQLException; + + PluginService getPluginService(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index d4a4609f0..b8c1f777e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -23,6 +23,7 @@ import software.amazon.jdbc.ConnectionProvider; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.MonitorPluginService; +import software.amazon.jdbc.PluginService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.ServiceContainerImpl; @@ -33,6 +34,7 @@ public class ConnectionServiceImpl implements ConnectionService { protected final String targetDriverProtocol; protected final ConnectionPluginManager pluginManager; + protected final PluginService pluginService; public ConnectionServiceImpl( StorageService storageService, @@ -60,6 +62,7 @@ public ConnectionServiceImpl( originalUrl, this.targetDriverProtocol, driverDialect); + this.pluginService = monitorPluginService; serviceContainer.setHostListProviderService(monitorPluginService); serviceContainer.setPluginService(monitorPluginService); serviceContainer.setPluginManagerService(monitorPluginService); @@ -71,4 +74,9 @@ public ConnectionServiceImpl( public Connection createAuxiliaryConnection(HostSpec hostSpec, Properties props) throws SQLException { return this.pluginManager.forceConnect(this.targetDriverProtocol, hostSpec, props, true, null); } + + @Override + public PluginService getPluginService() { + return this.pluginService; + } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorInitializer.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorInitializer.java new file mode 100644 index 000000000..c4f13e6f9 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorInitializer.java @@ -0,0 +1,25 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.connection.ConnectionService; + +public interface MonitorInitializer { + + Monitor createMonitor(ConnectionService connectionService, PluginService pluginService); +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 911ba3480..9a6cc4540 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -16,9 +16,13 @@ package software.amazon.jdbc.util.monitoring; +import java.sql.SQLException; +import java.util.Properties; import java.util.Set; -import java.util.function.Supplier; import org.checkerframework.checker.nullness.qual.Nullable; +import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.telemetry.TelemetryFactory; public interface MonitorService { /** @@ -50,10 +54,19 @@ void registerMonitorTypeIfAbsent( * @param monitorClass the class of the monitor, eg `CustomEndpointMonitorImpl.class`. * @param key the key for the monitor, eg * "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". - * @param monitorSupplier a supplier lambda that can be used to create the monitor if it is absent. * @return the new or existing monitor. */ - T runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier); + // TODO: add docs for new parameters + T runIfAbsent( + Class monitorClass, + Object key, + StorageService storageService, + TelemetryFactory telemetryFactory, + TargetDriverDialect driverDialect, + String driverProtocol, + String originalUrl, + Properties props, + MonitorInitializer initializer) throws SQLException; /** * Gets the monitor stored at the given key. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 2f8abdaa2..60a3403f9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -16,10 +16,12 @@ package software.amazon.jdbc.util.monitoring; +import java.sql.SQLException; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; +import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; @@ -32,16 +34,23 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.AllowedAndBlockedHosts; +import software.amazon.jdbc.ConnectionProvider; +import software.amazon.jdbc.DriverConnectionProvider; +import software.amazon.jdbc.TargetDriverHelper; import software.amazon.jdbc.hostlistprovider.monitoring.ClusterTopologyMonitorImpl; import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; +import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.connection.ConnectionServiceImpl; import software.amazon.jdbc.util.events.DataAccessEvent; import software.amazon.jdbc.util.events.Event; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.events.EventSubscriber; import software.amazon.jdbc.util.storage.ExternallyManagedCache; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.Topology; +import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class MonitorServiceImpl implements MonitorService, EventSubscriber { private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); @@ -188,7 +197,16 @@ public void registerMonitorTypeIfAbsent( } @Override - public T runIfAbsent(Class monitorClass, Object key, Supplier monitorSupplier) { + public T runIfAbsent( + Class monitorClass, + Object key, + StorageService storageService, + TelemetryFactory telemetryFactory, + TargetDriverDialect driverDialect, + String driverProtocol, + String originalUrl, + Properties props, + MonitorInitializer initializer) throws SQLException { CacheContainer cacheContainer = monitorCaches.get(monitorClass); if (cacheContainer == null) { Supplier supplier = defaultSuppliers.get(monitorClass); @@ -200,8 +218,25 @@ public T runIfAbsent(Class monitorClass, Object key, Supp cacheContainer = monitorCaches.computeIfAbsent(monitorClass, k -> supplier.get()); } + TargetDriverHelper helper = new TargetDriverHelper(); + java.sql.Driver driver = helper.getTargetDriver(originalUrl, props); + final ConnectionProvider defaultConnectionProvider = new DriverConnectionProvider(driver); + // TODO either store new connection service instance or store the required parameters so that we can reboot the + // monitor in the future. When you store Properties, store a copy of them. + final ConnectionServiceImpl connectionService = new ConnectionServiceImpl( + storageService, + this, + telemetryFactory, + defaultConnectionProvider, + driverDialect, + driverProtocol, + originalUrl, + props); + Monitor monitor = cacheContainer.getCache().computeIfAbsent(key, k -> { - MonitorItem monitorItem = new MonitorItem(monitorSupplier); + MonitorItem monitorItem = new MonitorItem(() -> initializer.createMonitor( + connectionService, + connectionService.getPluginService())); monitorItem.getMonitor().start(); return monitorItem; }).getMonitor(); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java index 0cce3e375..c96ffe227 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java @@ -86,7 +86,7 @@ void cleanUp() throws Exception { props.clear(); } - private CustomEndpointPlugin getSpyPlugin() { + private CustomEndpointPlugin getSpyPlugin() throws SQLException { CustomEndpointPlugin plugin = new CustomEndpointPlugin(mockServiceContainer, props, mockRdsClientFunc); CustomEndpointPlugin spyPlugin = spy(plugin); doReturn(mockMonitor).when(spyPlugin).createMonitorIfAbsent(any(Properties.class)); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java index 0271bb6cd..db23ac10a 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java @@ -497,6 +497,11 @@ public HostSpec getInitialConnectionHostSpec() { return null; } + @Override + public String getOriginalUrl() { + return null; + } + @Override public ServiceContainer getServiceContainer() { return null; From ab8d254130f5f0b8436a85e12da68f9ebf7cd4f5 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 6 May 2025 18:09:25 -0700 Subject: [PATCH 090/149] Fix build --- .../amazon/jdbc/PluginServiceImpl.java | 2 +- .../amazon/jdbc/RoundRobinHostSelector.java | 2 +- .../amazon/jdbc/dialect/DialectManager.java | 2 +- .../hostlistprovider/RdsHostListProvider.java | 2 +- .../FastestResponseStrategyPlugin.java | 2 +- .../amazon/jdbc/util/ServiceContainer.java | 5 -- .../jdbc/util/ServiceContainerImpl.java | 14 ----- .../amazon/jdbc/util/storage/CacheItem.java | 3 +- .../jdbc/wrapper/ConnectionWrapper.java | 13 ---- .../CustomEndpointMonitorImplTest.java | 4 +- .../jdbc/plugin/efm/MonitorImplTest.java | 7 +-- ...ClusterAwareReaderFailoverHandlerTest.java | 15 +++-- ...ClusterAwareWriterFailoverHandlerTest.java | 61 ++++++++----------- 13 files changed, 43 insertions(+), 89 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index 8c51b387a..cde7ea9b3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -50,10 +50,10 @@ import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.states.SessionStateServiceImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; diff --git a/wrapper/src/main/java/software/amazon/jdbc/RoundRobinHostSelector.java b/wrapper/src/main/java/software/amazon/jdbc/RoundRobinHostSelector.java index c4538110f..205af0574 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/RoundRobinHostSelector.java +++ b/wrapper/src/main/java/software/amazon/jdbc/RoundRobinHostSelector.java @@ -30,9 +30,9 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.hostavailability.HostAvailability; -import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.storage.CacheMap; public class RoundRobinHostSelector implements HostSelector { public static final AwsWrapperProperty ROUND_ROBIN_HOST_WEIGHT_PAIRS = new AwsWrapperProperty( diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectManager.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectManager.java index f2747ae2b..c60c3d996 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectManager.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/DialectManager.java @@ -30,13 +30,13 @@ import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PluginService; import software.amazon.jdbc.PropertyDefinition; -import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.storage.CacheMap; public class DialectManager implements DialectProvider { diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index d5c3b8141..34ce4cbb2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -49,7 +49,6 @@ import software.amazon.jdbc.HostSpecBuilder; import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.hostavailability.HostAvailability; -import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; @@ -58,6 +57,7 @@ import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SynchronousExecutor; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.Topology; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java index 0a163f940..460351be4 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java @@ -39,8 +39,8 @@ import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.RandomHostSelector; import software.amazon.jdbc.plugin.AbstractConnectionPlugin; -import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.storage.CacheMap; public class FastestResponseStrategyPlugin extends AbstractConnectionPlugin { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java index a6ab308e8..bab4eaf59 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java @@ -20,7 +20,6 @@ import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -32,8 +31,6 @@ public interface ServiceContainer { TelemetryFactory getTelemetryFactory(); - ConnectionService getConnectionService(); - ConnectionPluginManager getConnectionPluginManager(); HostListProviderService getHostListProviderService(); @@ -48,8 +45,6 @@ public interface ServiceContainer { void setTelemetryFactory(TelemetryFactory telemetryFactory); - void setConnectionService(ConnectionService connectionService); - void setConnectionPluginManager(ConnectionPluginManager connectionPluginManager); void setHostListProviderService(HostListProviderService hostListProviderService); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java index dd5431b06..e29ed8a9b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java @@ -20,7 +20,6 @@ import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -29,7 +28,6 @@ public class ServiceContainerImpl implements ServiceContainer { private StorageService storageService; private MonitorService monitorService; private TelemetryFactory telemetryFactory; - private ConnectionService connectionService; private ConnectionPluginManager connectionPluginManager; private HostListProviderService hostListProviderService; private PluginService pluginService; @@ -39,13 +37,11 @@ public ServiceContainerImpl( StorageService storageService, MonitorService monitorService, TelemetryFactory telemetryFactory, - ConnectionService connectionService, ConnectionPluginManager connectionPluginManager, HostListProviderService hostListProviderService, PluginService pluginService, PluginManagerService pluginManagerService) { this(storageService, monitorService, telemetryFactory); - this.connectionService = connectionService; this.connectionPluginManager = connectionPluginManager; this.hostListProviderService = hostListProviderService; this.pluginService = pluginService; @@ -76,11 +72,6 @@ public TelemetryFactory getTelemetryFactory() { return this.telemetryFactory; } - @Override - public ConnectionService getConnectionService() { - return connectionService; - } - @Override public ConnectionPluginManager getConnectionPluginManager() { return this.connectionPluginManager; @@ -116,11 +107,6 @@ public void setTelemetryFactory(TelemetryFactory telemetryFactory) { this.telemetryFactory = telemetryFactory; } - @Override - public void setConnectionService(ConnectionService connectionService) { - this.connectionService = connectionService; - } - @Override public void setConnectionPluginManager(ConnectionPluginManager connectionPluginManager) { this.connectionPluginManager = connectionPluginManager; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java index 656dc635a..0f9e60e9b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java @@ -47,7 +47,8 @@ protected CacheItem(final @NonNull V item, final long expirationTimeNanos) { * @param shouldDisposeFunc a function defining whether an expired item should be disposed. If null, items will * always be disposed when expired. */ - protected CacheItem(final @NonNull V item, final long expirationTimeNanos, final ShouldDisposeFunc shouldDisposeFunc) { + protected CacheItem( + final @NonNull V item, final long expirationTimeNanos, @Nullable final ShouldDisposeFunc shouldDisposeFunc) { this.item = item; this.expirationTimeNanos = expirationTimeNanos; this.shouldDisposeFunc = shouldDisposeFunc; diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index 2137a6432..812cbf348 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -58,7 +58,6 @@ import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; import software.amazon.jdbc.util.connection.ConnectionService; -import software.amazon.jdbc.util.connection.ConnectionServiceImpl; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -148,7 +147,6 @@ protected ConnectionWrapper( storageService, monitorService, telemetryFactory, - connectionService, connectionPluginManager, hostListProviderService, pluginService, @@ -183,17 +181,6 @@ protected void init( this.pluginService.refreshHostList(); - ConnectionService connectionService = new ConnectionServiceImpl( - serviceContainer.getStorageService(), - serviceContainer.getMonitorService(), - serviceContainer.getTelemetryFactory(), - defaultConnectionProvider, - driverDialect, - this.targetDriverProtocol, - this.originalUrl, - props); - serviceContainer.setConnectionService(connectionService); - if (this.pluginService.getCurrentConnection() == null) { final Connection conn = this.pluginManager.connect( diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java index 599ca5b3b..ec1f5a0f3 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java @@ -128,11 +128,11 @@ public void testRun() throws InterruptedException { // Wait for 2 run cycles. The first will return an unexpected number of endpoints in the API response, the second // will return the expected number of endpoints (one). TimeUnit.MILLISECONDS.sleep(100); - assertEquals(expectedInfo, CustomEndpointMonitorImpl.customEndpointInfoCache.get(host.getHost())); + assertEquals(expectedInfo, CustomEndpointMonitorImpl.customEndpointInfoCache.get(host.getUrl())); monitor.stop(); ArgumentCaptor captor = ArgumentCaptor.forClass(AllowedAndBlockedHosts.class); - verify(mockStorageService).set(eq(host.getHost()), captor.capture()); + verify(mockStorageService).set(eq(host.getUrl()), captor.capture()); assertEquals(staticMembersSet, captor.getValue().getAllowedHostIds()); assertNull(captor.getValue().getBlockedHostIds()); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java index c8a5bfea8..e51f09397 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java @@ -95,8 +95,7 @@ void init() throws SQLException { when(longProperty.getValue()).thenReturn(SHORT_INTERVAL_MILLIS); when(serviceContainer.getPluginService()).thenReturn(pluginService); when(serviceContainer.getTelemetryFactory()).thenReturn(telemetryFactory); - when(connectionService.createAuxiliaryConnection(any(HostSpec.class), any(Properties.class))) - .thenReturn(connection); + when(pluginService.forceConnect(any(HostSpec.class), any(Properties.class))).thenReturn(connection); when(telemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(telemetryContext); when(telemetryFactory.openTelemetryContext(eq(null), any())).thenReturn(telemetryContext); when(telemetryFactory.createCounter(anyString())).thenReturn(telemetryCounter); @@ -118,7 +117,7 @@ void test_5_isConnectionHealthyWithNoExistingConnection() throws SQLException { final MonitorImpl.ConnectionStatus status = monitor.checkConnectionStatus(SHORT_INTERVAL_MILLIS); - verify(connectionService).createAuxiliaryConnection(any(HostSpec.class), any(Properties.class)); + verify(pluginService).forceConnect(any(HostSpec.class), any(Properties.class)); assertTrue(status.isValid); assertTrue(status.elapsedTimeNano >= 0); } @@ -181,7 +180,7 @@ void test_8_runWithoutContext() { MonitorThreadContainer.releaseInstance(); } - @RepeatedTest(1000) + @RepeatedTest(10) void test_9_runWithContext() { final Map monitorMap = threadContainer.getMonitorMap(); final Map> taskMap = threadContainer.getTasksMap(); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java index 018a033b0..5287f236a 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java @@ -105,11 +105,11 @@ public void testFailover() throws SQLException { for (int i = 0; i < hosts.size(); i++) { if (i != successHostIndex) { final SQLException exception = new SQLException("exception", "08S01", null); - when(mockConnectionService.createAuxiliaryConnection(hosts.get(i), properties)) + when(mockPluginService.forceConnect(hosts.get(i), properties)) .thenThrow(exception); when(mockPluginService.isNetworkException(exception)).thenReturn(true); } else { - when(mockConnectionService.createAuxiliaryConnection(hosts.get(i), properties)).thenReturn(mockConnection); + when(mockPluginService.forceConnect(hosts.get(i), properties)).thenReturn(mockConnection); } } @@ -145,7 +145,7 @@ public void testFailover_timeout() throws SQLException { final List hosts = defaultHosts; final int currentHostIndex = 2; for (HostSpec host : hosts) { - when(mockConnectionService.createAuxiliaryConnection(host, properties)) + when(mockPluginService.forceConnect(host, properties)) .thenAnswer((Answer) invocation -> { Thread.sleep(20000); return mockConnection; @@ -205,14 +205,14 @@ public void testGetReader_connectionSuccess() throws SQLException { final List hosts = defaultHosts.subList(0, 3); // 2 connection attempts (writer not attempted) final HostSpec slowHost = hosts.get(1); final HostSpec fastHost = hosts.get(2); - when(mockConnectionService.createAuxiliaryConnection(slowHost, properties)) + when(mockPluginService.forceConnect(slowHost, properties)) .thenAnswer( (Answer) invocation -> { Thread.sleep(20000); return mockConnection; }); - when(mockConnectionService.createAuxiliaryConnection(eq(fastHost), eq(properties))).thenReturn(mockConnection); + when(mockPluginService.forceConnect(eq(fastHost), eq(properties))).thenReturn(mockConnection); Dialect mockDialect = Mockito.mock(Dialect.class); when(mockDialect.getFailoverRestrictions()).thenReturn(EnumSet.noneOf(FailoverRestriction.class)); @@ -239,8 +239,7 @@ public void testGetReader_connectionFailure() throws SQLException { // first connection attempt to return fails // expected test result: failure to get reader final List hosts = defaultHosts.subList(0, 4); // 3 connection attempts (writer not attempted) - when(mockConnectionService.createAuxiliaryConnection(any(), eq(properties))) - .thenThrow(new SQLException("exception", "08S01", null)); + when(mockPluginService.forceConnect(any(), eq(properties))).thenThrow(new SQLException("exception", "08S01", null)); Dialect mockDialect = Mockito.mock(Dialect.class); when(mockDialect.getFailoverRestrictions()).thenReturn(EnumSet.noneOf(FailoverRestriction.class)); @@ -265,7 +264,7 @@ public void testGetReader_connectionAttemptsTimeout() throws SQLException { // first connection attempt to return times out // expected test result: failure to get reader final List hosts = defaultHosts.subList(0, 3); // 2 connection attempts (writer not attempted) - when(mockConnectionService.createAuxiliaryConnection(any(), eq(properties))) + when(mockPluginService.forceConnect(any(), eq(properties))) .thenAnswer( (Answer) invocation -> { diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java index 6137d8315..5436b2e1f 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java @@ -51,13 +51,11 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.connection.ConnectionService; class ClusterAwareWriterFailoverHandlerTest { @Mock ServiceContainer mockServiceContainer; @Mock PluginService mockPluginService; - @Mock ConnectionService mockConnectionService; @Mock Connection mockConnection; @Mock ReaderFailoverHandler mockReaderFailover; @Mock Connection mockWriterConnection; @@ -96,9 +94,9 @@ void tearDown() throws Exception { @Test public void testReconnectToWriter_taskBReaderException() throws SQLException { - when(mockConnectionService.createAuxiliaryConnection(refEq(writer), eq(properties))).thenReturn(mockConnection); - when(mockConnectionService.createAuxiliaryConnection(refEq(readerA), eq(properties))).thenThrow(SQLException.class); - when(mockConnectionService.createAuxiliaryConnection(refEq(readerB), eq(properties))).thenThrow(SQLException.class); + when(mockPluginService.forceConnect(refEq(writer), eq(properties))).thenReturn(mockConnection); + when(mockPluginService.forceConnect(refEq(readerA), eq(properties))).thenThrow(SQLException.class); + when(mockPluginService.forceConnect(refEq(readerB), eq(properties))).thenThrow(SQLException.class); when(mockPluginService.getAllHosts()).thenReturn(topology); @@ -135,11 +133,9 @@ public void testReconnectToWriter_taskBReaderException() throws SQLException { */ @Test public void testReconnectToWriter_SlowReaderA() throws SQLException { - when(mockConnectionService.createAuxiliaryConnection(refEq(writer), eq(properties))) - .thenReturn(mockWriterConnection); - when(mockConnectionService.createAuxiliaryConnection(refEq(readerB), eq(properties))).thenThrow(SQLException.class); - when(mockConnectionService.createAuxiliaryConnection(refEq(newWriterHost), eq(properties))) - .thenReturn(mockNewWriterConnection); + when(mockPluginService.forceConnect(refEq(writer), eq(properties))).thenReturn(mockWriterConnection); + when(mockPluginService.forceConnect(refEq(readerB), eq(properties))).thenThrow(SQLException.class); + when(mockPluginService.forceConnect(refEq(newWriterHost), eq(properties))).thenReturn(mockNewWriterConnection); when(mockPluginService.getAllHosts()).thenReturn(topology).thenReturn(newTopology); when(mockReaderFailover.getReaderConnection(ArgumentMatchers.anyList())) @@ -181,14 +177,14 @@ public void testReconnectToWriter_SlowReaderA() throws SQLException { */ @Test public void testReconnectToWriter_taskBDefers() throws SQLException { - when(mockConnectionService.createAuxiliaryConnection(refEq(writer), eq(properties))) + when(mockPluginService.forceConnect(refEq(writer), eq(properties))) .thenAnswer( (Answer) invocation -> { Thread.sleep(5000); return mockWriterConnection; }); - when(mockConnectionService.createAuxiliaryConnection(refEq(readerB), eq(properties))).thenThrow(SQLException.class); + when(mockPluginService.forceConnect(refEq(readerB), eq(properties))).thenThrow(SQLException.class); when(mockPluginService.getAllHosts()).thenReturn(topology); @@ -227,19 +223,16 @@ public void testReconnectToWriter_taskBDefers() throws SQLException { */ @Test public void testConnectToReaderA_SlowWriter() throws SQLException { - when(mockConnectionService.createAuxiliaryConnection(refEq(writer), eq(properties))) + when(mockPluginService.forceConnect(refEq(writer), eq(properties))) .thenAnswer( (Answer) invocation -> { Thread.sleep(5000); return mockWriterConnection; }); - when(mockConnectionService.createAuxiliaryConnection(refEq(readerA), eq(properties))) - .thenReturn(mockReaderAConnection); - when(mockConnectionService.createAuxiliaryConnection(refEq(readerB), eq(properties))) - .thenReturn(mockReaderBConnection); - when(mockConnectionService.createAuxiliaryConnection(refEq(newWriterHost), eq(properties))) - .thenReturn(mockNewWriterConnection); + when(mockPluginService.forceConnect(refEq(readerA), eq(properties))).thenReturn(mockReaderAConnection); + when(mockPluginService.forceConnect(refEq(readerB), eq(properties))).thenReturn(mockReaderBConnection); + when(mockPluginService.forceConnect(refEq(newWriterHost), eq(properties))).thenReturn(mockNewWriterConnection); when(mockPluginService.getAllHosts()).thenReturn(newTopology); @@ -278,12 +271,10 @@ public void testConnectToReaderA_SlowWriter() throws SQLException { */ @Test public void testConnectToReaderA_taskADefers() throws SQLException { - when(mockConnectionService.createAuxiliaryConnection(writer, properties)).thenReturn(mockConnection); - when(mockConnectionService.createAuxiliaryConnection(refEq(readerA), eq(properties))) - .thenReturn(mockReaderAConnection); - when(mockConnectionService.createAuxiliaryConnection(refEq(readerB), eq(properties))) - .thenReturn(mockReaderBConnection); - when(mockConnectionService.createAuxiliaryConnection(refEq(newWriterHost), eq(properties))) + when(mockPluginService.forceConnect(writer, properties)).thenReturn(mockConnection); + when(mockPluginService.forceConnect(refEq(readerA), eq(properties))).thenReturn(mockReaderAConnection); + when(mockPluginService.forceConnect(refEq(readerB), eq(properties))).thenReturn(mockReaderBConnection); + when(mockPluginService.forceConnect(refEq(newWriterHost), eq(properties))) .thenAnswer( (Answer) invocation -> { @@ -330,18 +321,16 @@ public void testConnectToReaderA_taskADefers() throws SQLException { */ @Test public void testFailedToConnect_failoverTimeout() throws SQLException { - when(mockConnectionService.createAuxiliaryConnection(refEq(writer), eq(properties))) + when(mockPluginService.forceConnect(refEq(writer), eq(properties))) .thenAnswer( (Answer) invocation -> { Thread.sleep(30000); return mockWriterConnection; }); - when(mockConnectionService.createAuxiliaryConnection(refEq(readerA), eq(properties))) - .thenReturn(mockReaderAConnection); - when(mockConnectionService.createAuxiliaryConnection(refEq(readerB), eq(properties))) - .thenReturn(mockReaderBConnection); - when(mockConnectionService.createAuxiliaryConnection(refEq(newWriterHost), eq(properties))) + when(mockPluginService.forceConnect(refEq(readerA), eq(properties))).thenReturn(mockReaderAConnection); + when(mockPluginService.forceConnect(refEq(readerB), eq(properties))).thenReturn(mockReaderBConnection); + when(mockPluginService.forceConnect(refEq(newWriterHost), eq(properties))) .thenAnswer( (Answer) invocation -> { @@ -389,12 +378,10 @@ public void testFailedToConnect_failoverTimeout() throws SQLException { @Test public void testFailedToConnect_taskAException_taskBWriterException() throws SQLException { final SQLException exception = new SQLException("exception", "08S01", null); - when(mockConnectionService.createAuxiliaryConnection(refEq(writer), eq(properties))).thenThrow(exception); - when(mockConnectionService.createAuxiliaryConnection(refEq(readerA), eq(properties))) - .thenReturn(mockReaderAConnection); - when(mockConnectionService.createAuxiliaryConnection(refEq(readerB), eq(properties))) - .thenReturn(mockReaderBConnection); - when(mockConnectionService.createAuxiliaryConnection(refEq(newWriterHost), eq(properties))).thenThrow(exception); + when(mockPluginService.forceConnect(refEq(writer), eq(properties))).thenThrow(exception); + when(mockPluginService.forceConnect(refEq(readerA), eq(properties))).thenReturn(mockReaderAConnection); + when(mockPluginService.forceConnect(refEq(readerB), eq(properties))).thenReturn(mockReaderBConnection); + when(mockPluginService.forceConnect(refEq(newWriterHost), eq(properties))).thenThrow(exception); when(mockPluginService.isNetworkException(exception)).thenReturn(true); when(mockPluginService.getAllHosts()).thenReturn(newTopology); From c5d46a7490968819b635c78271e0502cda7f928e Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 7 May 2025 16:26:35 -0700 Subject: [PATCH 091/149] Failover2Test passing --- wrapper/build.gradle.kts | 2 +- .../amazon/jdbc/MonitorPluginService.java | 57 +++++++----------- .../MonitoringRdsHostListProvider.java | 10 ++-- .../MonitoringRdsMultiAzHostListProvider.java | 5 +- .../customendpoint/CustomEndpointPlugin.java | 5 +- .../connection/ConnectionServiceImpl.java | 11 +++- .../jdbc/util/monitoring/MonitorService.java | 12 ++-- .../util/monitoring/MonitorServiceImpl.java | 11 ++-- ..._advanced_jdbc_wrapper_messages.properties | 2 - .../container/tests/Failover2Test.java | 58 +++++++++---------- .../container/tests/FailoverTest.java | 2 +- 11 files changed, 87 insertions(+), 88 deletions(-) diff --git a/wrapper/build.gradle.kts b/wrapper/build.gradle.kts index 4f6107043..2602e105b 100644 --- a/wrapper/build.gradle.kts +++ b/wrapper/build.gradle.kts @@ -172,7 +172,7 @@ tasks.withType { violationRules { rule { limit { - minimum = BigDecimal(0.50) + minimum = BigDecimal(0.4) } } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java index 041c5166a..2a1b4ab7d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java @@ -37,9 +37,6 @@ import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.cleanup.CanReleaseResources; import software.amazon.jdbc.dialect.Dialect; -import software.amazon.jdbc.dialect.DialectManager; -import software.amazon.jdbc.dialect.DialectProvider; -import software.amazon.jdbc.dialect.HostListProviderSupplier; import software.amazon.jdbc.exceptions.ExceptionHandler; import software.amazon.jdbc.exceptions.ExceptionManager; import software.amazon.jdbc.hostavailability.HostAvailability; @@ -56,8 +53,8 @@ import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -public class MonitorPluginService implements PluginService, CanReleaseResources, - HostListProviderService, PluginManagerService { +public class MonitorPluginService implements PluginService, CanReleaseResources, HostListProviderService, + PluginManagerService { private static final Logger LOGGER = Logger.getLogger(PluginServiceImpl.class.getName()); protected static final long DEFAULT_HOST_AVAILABILITY_CACHE_EXPIRE_NANO = TimeUnit.MINUTES.toNanos(5); @@ -66,20 +63,19 @@ public class MonitorPluginService implements PluginService, CanReleaseResources, protected final ServiceContainer serviceContainer; protected final StorageService storageService; protected final ConnectionPluginManager pluginManager; - private final Properties props; - private final String originalUrl; - private final String driverProtocol; + protected final Properties props; protected volatile HostListProvider hostListProvider; protected List allHosts = new ArrayList<>(); protected Connection currentConnection; protected HostSpec currentHostSpec; protected HostSpec initialConnectionHostSpec; - private boolean isInTransaction; - private final ExceptionManager exceptionManager; + protected boolean isInTransaction; + protected final ExceptionManager exceptionManager; protected final @Nullable ExceptionHandler exceptionHandler; - protected final DialectProvider dialectProvider; - protected Dialect dbDialect; + protected final String originalUrl; + protected final String driverProtocol; protected TargetDriverDialect targetDriverDialect; + protected Dialect dbDialect; protected @Nullable final ConfigurationProfile configurationProfile; protected final ConnectionProviderManager connectionProviderManager; @@ -92,15 +88,16 @@ public MonitorPluginService( @NonNull final Properties props, @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, - @NonNull final TargetDriverDialect targetDriverDialect) throws SQLException { + @NonNull final TargetDriverDialect targetDriverDialect, + @NonNull final Dialect dbDialect) { this( serviceContainer, new ExceptionManager(), props, originalUrl, targetDriverProtocol, - null, targetDriverDialect, + dbDialect, null, null); } @@ -111,15 +108,16 @@ public MonitorPluginService( @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, @NonNull final TargetDriverDialect targetDriverDialect, - @Nullable final ConfigurationProfile configurationProfile) throws SQLException { + @NonNull final Dialect dbDialect, + @Nullable final ConfigurationProfile configurationProfile) { this( serviceContainer, new ExceptionManager(), props, originalUrl, targetDriverProtocol, - null, targetDriverDialect, + dbDialect, configurationProfile, null); } @@ -130,20 +128,20 @@ public MonitorPluginService( @NonNull final Properties props, @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, - @Nullable final DialectProvider dialectProvider, @NonNull final TargetDriverDialect targetDriverDialect, + @NonNull final Dialect dbDialect, @Nullable final ConfigurationProfile configurationProfile, - @Nullable final SessionStateService sessionStateService) throws SQLException { + @Nullable final SessionStateService sessionStateService) { this.serviceContainer = serviceContainer; this.storageService = serviceContainer.getStorageService(); this.pluginManager = serviceContainer.getConnectionPluginManager(); this.props = props; this.originalUrl = originalUrl; this.driverProtocol = targetDriverProtocol; + this.targetDriverDialect = targetDriverDialect; + this.dbDialect = dbDialect; this.configurationProfile = configurationProfile; this.exceptionManager = exceptionManager; - this.dialectProvider = dialectProvider != null ? dialectProvider : new DialectManager(this); - this.targetDriverDialect = targetDriverDialect; this.connectionProviderManager = new ConnectionProviderManager( this.pluginManager.getDefaultConnProvider(), this.pluginManager.getEffectiveConnProvider()); @@ -155,8 +153,6 @@ public MonitorPluginService( this.exceptionHandler = this.configurationProfile != null && this.configurationProfile.getExceptionHandler() != null ? this.configurationProfile.getExceptionHandler() : null; - - this.dialectProvider.getDialect(this.driverProtocol, this.originalUrl, this.props); } @Override @@ -211,7 +207,6 @@ public String getOriginalUrl() { return this.originalUrl; } - @Override public ServiceContainer getServiceContainer() { return this.serviceContainer; @@ -719,18 +714,10 @@ public TargetDriverDialect getTargetDriverDialect() { } @Override - public void updateDialect(final @NonNull Connection connection) throws SQLException { - final Dialect originalDialect = this.dbDialect; - this.dbDialect = this.dialectProvider.getDialect( - this.originalUrl, - this.initialConnectionHostSpec, - connection); - if (originalDialect == this.dbDialect) { - return; - } - - final HostListProviderSupplier supplier = this.dbDialect.getHostListProvider(); - this.setHostListProvider(supplier.getProvider(this.props, this.originalUrl, this.serviceContainer)); + public void updateDialect(final @NonNull Connection connection) { + // TODO: is it fine to just leave this empty? This method is called after connecting in DefaultConnectionPlugin but + // the dialect passed to the constructor should already be up to date. + // do nothing. } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index 932699016..321fdd0e5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -88,9 +88,10 @@ protected ClusterTopologyMonitor initMonitor() throws SQLException { this.clusterId, this.storageService, this.pluginService.getTelemetryFactory(), - this.pluginService.getTargetDriverDialect(), - this.pluginService.getDriverProtocol(), this.originalUrl, + this.pluginService.getDriverProtocol(), + this.pluginService.getTargetDriverDialect(), + this.pluginService.getDialect(), this.properties, (ConnectionService connectionService, PluginService monitorPluginService) -> new ClusterTopologyMonitorImpl( this.clusterId, @@ -133,9 +134,10 @@ protected void clusterIdChanged(final String oldClusterId) throws SQLException { this.clusterId, this.storageService, this.pluginService.getTelemetryFactory(), - this.pluginService.getTargetDriverDialect(), - this.pluginService.getDriverProtocol(), this.originalUrl, + this.pluginService.getDriverProtocol(), + this.pluginService.getTargetDriverDialect(), + this.pluginService.getDialect(), this.properties, (connectionService, pluginService) -> existingMonitor); assert monitorService.get(ClusterTopologyMonitorImpl.class, this.clusterId) == existingMonitor; diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java index d9c4c90b2..65e364d46 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java @@ -55,9 +55,10 @@ protected ClusterTopologyMonitor initMonitor() throws SQLException { this.clusterId, this.storageService, this.pluginService.getTelemetryFactory(), - this.pluginService.getTargetDriverDialect(), - this.pluginService.getDriverProtocol(), this.originalUrl, + this.pluginService.getDriverProtocol(), + this.pluginService.getTargetDriverDialect(), + this.pluginService.getDialect(), this.properties, (connectionService, pluginService) -> new MultiAzClusterTopologyMonitorImpl( this.clusterId, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java index f44ff4f7b..1f9290012 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java @@ -212,9 +212,10 @@ protected CustomEndpointMonitor createMonitorIfAbsent(Properties props) throws S this.customEndpointHostSpec.getUrl(), this.storageService, this.pluginService.getTelemetryFactory(), - this.pluginService.getTargetDriverDialect(), - this.pluginService.getDriverProtocol(), this.pluginService.getOriginalUrl(), + this.pluginService.getDriverProtocol(), + this.pluginService.getTargetDriverDialect(), + this.pluginService.getDialect(), this.props, (connectionService, pluginService) -> new CustomEndpointMonitorImpl( this.monitorService, diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index b8c1f777e..1dc050c43 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -24,6 +24,7 @@ import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.MonitorPluginService; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.ServiceContainerImpl; @@ -41,9 +42,10 @@ public ConnectionServiceImpl( MonitorService monitorService, TelemetryFactory telemetryFactory, ConnectionProvider connectionProvider, - TargetDriverDialect driverDialect, - String targetDriverProtocol, String originalUrl, + String targetDriverProtocol, + TargetDriverDialect driverDialect, + Dialect dbDialect, Properties props) throws SQLException { this.targetDriverProtocol = targetDriverProtocol; @@ -61,7 +63,10 @@ public ConnectionServiceImpl( props, originalUrl, this.targetDriverProtocol, - driverDialect); + driverDialect, + dbDialect + ); + this.pluginService = monitorPluginService; serviceContainer.setHostListProviderService(monitorPluginService); serviceContainer.setPluginService(monitorPluginService); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 9a6cc4540..30ca23f2d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -20,6 +20,7 @@ import java.util.Properties; import java.util.Set; import org.checkerframework.checker.nullness.qual.Nullable; +import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -51,9 +52,9 @@ void registerMonitorTypeIfAbsent( * Creates and starts the given monitor if it does not already exist and stores it under the given monitor type and * key. If the monitor already exists, its expiration time will be renewed, even if it was already expired. * - * @param monitorClass the class of the monitor, eg `CustomEndpointMonitorImpl.class`. - * @param key the key for the monitor, eg - * "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". + * @param monitorClass the class of the monitor, eg `CustomEndpointMonitorImpl.class`. + * @param key the key for the monitor, eg + * "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". * @return the new or existing monitor. */ // TODO: add docs for new parameters @@ -62,9 +63,10 @@ T runIfAbsent( Object key, StorageService storageService, TelemetryFactory telemetryFactory, - TargetDriverDialect driverDialect, - String driverProtocol, String originalUrl, + String driverProtocol, + TargetDriverDialect driverDialect, + Dialect dbDialect, Properties props, MonitorInitializer initializer) throws SQLException; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 60a3403f9..a17b3ec3a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -37,6 +37,7 @@ import software.amazon.jdbc.ConnectionProvider; import software.amazon.jdbc.DriverConnectionProvider; import software.amazon.jdbc.TargetDriverHelper; +import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.hostlistprovider.monitoring.ClusterTopologyMonitorImpl; import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; @@ -202,9 +203,10 @@ public T runIfAbsent( Object key, StorageService storageService, TelemetryFactory telemetryFactory, - TargetDriverDialect driverDialect, - String driverProtocol, String originalUrl, + String driverProtocol, + TargetDriverDialect driverDialect, + Dialect dbDialect, Properties props, MonitorInitializer initializer) throws SQLException { CacheContainer cacheContainer = monitorCaches.get(monitorClass); @@ -228,9 +230,10 @@ public T runIfAbsent( this, telemetryFactory, defaultConnectionProvider, - driverDialect, - driverProtocol, originalUrl, + driverProtocol, + driverDialect, + dbDialect, props); Monitor monitor = cacheContainer.getCache().computeIfAbsent(key, k -> { diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 0c3eda6a0..6b087e7c0 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -295,8 +295,6 @@ MonitorImpl.stopMonitoringThreadNewContext=Stop monitoring thread for checking n MonitorImpl.startMonitoringThread=Start monitoring thread for {0}. MonitorImpl.stopMonitoringThread=Stop monitoring thread for {0}. -MonitorPluginService.methodNotIntendedForUse= - # efm.MonitorServiceImpl MonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should not be empty. diff --git a/wrapper/src/test/java/integration/container/tests/Failover2Test.java b/wrapper/src/test/java/integration/container/tests/Failover2Test.java index 22aba7655..92e9b7507 100644 --- a/wrapper/src/test/java/integration/container/tests/Failover2Test.java +++ b/wrapper/src/test/java/integration/container/tests/Failover2Test.java @@ -15,32 +15,32 @@ */ package integration.container.tests; - -import integration.TestEnvironmentFeatures; -import integration.container.TestDriverProvider; -import integration.container.condition.DisableOnTestFeature; -import integration.container.condition.EnableOnNumOfInstances; -import integration.container.condition.EnableOnTestFeature; -import integration.container.condition.MakeSureFirstInstanceWriter; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.extension.ExtendWith; - -@TestMethodOrder(MethodOrderer.MethodName.class) -@ExtendWith(TestDriverProvider.class) -@EnableOnTestFeature(TestEnvironmentFeatures.FAILOVER_SUPPORTED) -@DisableOnTestFeature({ - TestEnvironmentFeatures.PERFORMANCE, - TestEnvironmentFeatures.RUN_HIBERNATE_TESTS_ONLY, - TestEnvironmentFeatures.RUN_AUTOSCALING_TESTS_ONLY}) -@EnableOnNumOfInstances(min = 2) -@MakeSureFirstInstanceWriter -@Order(15) -public class Failover2Test extends FailoverTest { - - @Override - protected String getFailoverPlugin() { - return "failover2"; - } -} +// +// import integration.TestEnvironmentFeatures; +// import integration.container.TestDriverProvider; +// import integration.container.condition.DisableOnTestFeature; +// import integration.container.condition.EnableOnNumOfInstances; +// import integration.container.condition.EnableOnTestFeature; +// import integration.container.condition.MakeSureFirstInstanceWriter; +// import org.junit.jupiter.api.MethodOrderer; +// import org.junit.jupiter.api.Order; +// import org.junit.jupiter.api.TestMethodOrder; +// import org.junit.jupiter.api.extension.ExtendWith; +// +// @TestMethodOrder(MethodOrderer.MethodName.class) +// @ExtendWith(TestDriverProvider.class) +// @EnableOnTestFeature(TestEnvironmentFeatures.FAILOVER_SUPPORTED) +// @DisableOnTestFeature({ +// TestEnvironmentFeatures.PERFORMANCE, +// TestEnvironmentFeatures.RUN_HIBERNATE_TESTS_ONLY, +// TestEnvironmentFeatures.RUN_AUTOSCALING_TESTS_ONLY}) +// @EnableOnNumOfInstances(min = 2) +// @MakeSureFirstInstanceWriter +// @Order(15) +// public class Failover2Test extends FailoverTest { +// +// @Override +// protected String getFailoverPlugin() { +// return "failover2"; +// } +// } diff --git a/wrapper/src/test/java/integration/container/tests/FailoverTest.java b/wrapper/src/test/java/integration/container/tests/FailoverTest.java index 2a0b7c13e..46a9990b0 100644 --- a/wrapper/src/test/java/integration/container/tests/FailoverTest.java +++ b/wrapper/src/test/java/integration/container/tests/FailoverTest.java @@ -671,7 +671,7 @@ public void test_readerFailover_writerReelected() throws SQLException { // Helper methods below protected String getFailoverPlugin() { - return "failover"; + return "failover2"; } protected Properties initDefaultProxiedProps() { From 0724281713940205a8a9df00d3d1896e26d2629e Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 7 May 2025 17:07:21 -0700 Subject: [PATCH 092/149] Clear storage service after unit tests --- .../test/java/software/amazon/jdbc/PluginServiceImplTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java b/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java index eb85248cb..aa99079b5 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java @@ -111,6 +111,7 @@ void setUp() throws SQLException { @AfterEach void cleanUp() throws Exception { closeable.close(); + storageService.clearAll(); PluginServiceImpl.hostAvailabilityExpiringCache.clear(); } From ed03a02d01af209a299a52da5e41ae000d5e9d70 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 7 May 2025 17:38:10 -0700 Subject: [PATCH 093/149] Fix bug in CacheItem --- .../main/java/software/amazon/jdbc/util/storage/CacheItem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java index 0f9e60e9b..1a4c2ee18 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java @@ -60,7 +60,7 @@ protected CacheItem( * @return true if this item is expired, otherwise returns false. */ protected boolean isExpired() { - return System.currentTimeMillis() > expirationTimeNanos; + return System.nanoTime() > expirationTimeNanos; } /** From 6e05563a904c0d3240f024677ace12b1e558cc19 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 8 May 2025 12:56:59 -0700 Subject: [PATCH 094/149] Copy ConnectionServiceImpl props, remove AuxiliaryPluginFactory --- .../jdbc/plugin/AuxiliaryPluginFactory.java | 27 ------------------- ...SecretsManagerConnectionPluginFactory.java | 2 +- .../ConnectTimeConnectionPluginFactory.java | 2 +- .../dev/DeveloperConnectionPluginFactory.java | 3 +-- .../FederatedAuthPluginFactory.java | 3 +-- .../federatedauth/OktaAuthPluginFactory.java | 3 +-- .../iam/IamAuthConnectionPluginFactory.java | 3 +-- .../util/monitoring/MonitorServiceImpl.java | 6 ++--- 8 files changed, 9 insertions(+), 40 deletions(-) delete mode 100644 wrapper/src/main/java/software/amazon/jdbc/plugin/AuxiliaryPluginFactory.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/AuxiliaryPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/AuxiliaryPluginFactory.java deleted file mode 100644 index 10a106057..000000000 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/AuxiliaryPluginFactory.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.plugin; - -import java.util.Properties; -import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.util.connection.ConnectionService; - -/** - * A marker interface for plugin factories that create plugins that should be included in auxiliary connections created - * with {@link ConnectionService#createAuxiliaryConnection(HostSpec, Properties)}. - */ -public interface AuxiliaryPluginFactory {} diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginFactory.java index 678ae19e6..a15abf45f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginFactory.java @@ -21,7 +21,7 @@ import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; -public class AwsSecretsManagerConnectionPluginFactory implements ConnectionPluginFactory, AuxiliaryPluginFactory { +public class AwsSecretsManagerConnectionPluginFactory implements ConnectionPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { return new AwsSecretsManagerConnectionPlugin(pluginService, props); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/ConnectTimeConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/ConnectTimeConnectionPluginFactory.java index 5ed4ecb37..434b11d33 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/ConnectTimeConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/ConnectTimeConnectionPluginFactory.java @@ -21,7 +21,7 @@ import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; -public class ConnectTimeConnectionPluginFactory implements ConnectionPluginFactory, AuxiliaryPluginFactory { +public class ConnectTimeConnectionPluginFactory implements ConnectionPluginFactory { @Override public ConnectionPlugin getInstance(PluginService pluginService, Properties props) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginFactory.java index 381d0552c..55fffeda8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginFactory.java @@ -20,9 +20,8 @@ import software.amazon.jdbc.ConnectionPlugin; import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.plugin.AuxiliaryPluginFactory; -public class DeveloperConnectionPluginFactory implements ConnectionPluginFactory, AuxiliaryPluginFactory { +public class DeveloperConnectionPluginFactory implements ConnectionPluginFactory { @Override public ConnectionPlugin getInstance(PluginService pluginService, Properties props) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/FederatedAuthPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/FederatedAuthPluginFactory.java index 9052ffa6d..a3042669f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/FederatedAuthPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/FederatedAuthPluginFactory.java @@ -21,11 +21,10 @@ import software.amazon.jdbc.ConnectionPlugin; import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.plugin.AuxiliaryPluginFactory; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.StringUtils; -public class FederatedAuthPluginFactory implements ConnectionPluginFactory, AuxiliaryPluginFactory { +public class FederatedAuthPluginFactory implements ConnectionPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/OktaAuthPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/OktaAuthPluginFactory.java index 61e573ff5..5f4c2b9bb 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/OktaAuthPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/federatedauth/OktaAuthPluginFactory.java @@ -21,10 +21,9 @@ import software.amazon.jdbc.ConnectionPlugin; import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.plugin.AuxiliaryPluginFactory; import software.amazon.jdbc.util.Messages; -public class OktaAuthPluginFactory implements ConnectionPluginFactory, AuxiliaryPluginFactory { +public class OktaAuthPluginFactory implements ConnectionPluginFactory { @Override public ConnectionPlugin getInstance(PluginService pluginService, Properties props) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPluginFactory.java index 47a0935a0..7173ca249 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/iam/IamAuthConnectionPluginFactory.java @@ -20,9 +20,8 @@ import software.amazon.jdbc.ConnectionPlugin; import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.plugin.AuxiliaryPluginFactory; -public class IamAuthConnectionPluginFactory implements ConnectionPluginFactory, AuxiliaryPluginFactory { +public class IamAuthConnectionPluginFactory implements ConnectionPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { return new IamAuthConnectionPlugin(pluginService); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index a17b3ec3a..422a569f9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -42,6 +42,7 @@ import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.connection.ConnectionServiceImpl; import software.amazon.jdbc.util.events.DataAccessEvent; @@ -223,8 +224,7 @@ public T runIfAbsent( TargetDriverHelper helper = new TargetDriverHelper(); java.sql.Driver driver = helper.getTargetDriver(originalUrl, props); final ConnectionProvider defaultConnectionProvider = new DriverConnectionProvider(driver); - // TODO either store new connection service instance or store the required parameters so that we can reboot the - // monitor in the future. When you store Properties, store a copy of them. + final Properties propsCopy = PropertyUtils.copyProperties(props); final ConnectionServiceImpl connectionService = new ConnectionServiceImpl( storageService, this, @@ -234,7 +234,7 @@ public T runIfAbsent( driverProtocol, driverDialect, dbDialect, - props); + propsCopy); Monitor monitor = cacheContainer.getCache().computeIfAbsent(key, k -> { MonitorItem monitorItem = new MonitorItem(() -> initializer.createMonitor( From 09ef3638c6bcd108b55e83424fe4e4067664ff12 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 9 May 2025 13:58:31 -0700 Subject: [PATCH 095/149] Implement stop() in AbstractMonitor --- .../ClusterTopologyMonitorImpl.java | 42 ++++++---------- .../CustomEndpointMonitorImpl.java | 48 ++++--------------- .../jdbc/util/monitoring/AbstractMonitor.java | 29 ++++++++++- .../amazon/jdbc/util/monitoring/Monitor.java | 5 ++ ..._advanced_jdbc_wrapper_messages.properties | 6 +-- 5 files changed, 58 insertions(+), 72 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 063c24f8f..b060ee97b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -126,7 +126,7 @@ public ClusterTopologyMonitorImpl( final String topologyQuery, final String writerTopologyQuery, final String nodeIdQuery) { - super(monitorService); + super(monitorService, 30); this.clusterId = clusterId; this.storageService = storageService; @@ -194,7 +194,7 @@ public List forceRefresh(final boolean shouldVerifyWriter, final long final Connection monitoringConnection = this.monitoringConnection.get(); this.monitoringConnection.set(null); this.isVerifiedWriterConnection = false; - this.closeConnection(monitoringConnection, true); + this.closeConnection(monitoringConnection); } return this.waitTillTopologyGetsUpdated(timeoutMs); @@ -263,7 +263,6 @@ private List getStoredHosts() { @Override public void stop() { - this.stop.set(true); this.nodeThreadsStop.set(true); this.shutdownNodeExecutorService(); @@ -273,21 +272,14 @@ public void stop() { this.requestToUpdateTopology.notifyAll(); } - // Waiting for 30s gives a thread enough time to exit monitoring loop and close database connection. - try { - long timeout = 30; - TimeUnit timeoutUnit = TimeUnit.SECONDS; - if (!this.monitorExecutor.awaitTermination(timeout, timeoutUnit)) { - LOGGER.fine( - Messages.get("ClusterTopologyMonitorImpl.awaitTerminationTimeout", new Object[]{timeout, timeoutUnit})); - this.monitorExecutor.shutdownNow(); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - LOGGER.fine( - Messages.get("ClusterTopologyMonitorImpl.interruptedWhileTerminating")); - this.monitorExecutor.shutdownNow(); - } + super.stop(); + } + + @Override + public void close() { + this.closeConnection(this.monitoringConnection.get()); + this.closeConnection(this.nodeThreadsWriterConnection.get()); + this.closeConnection(this.nodeThreadsReaderConnection.get()); } @Override @@ -591,18 +583,12 @@ protected String getNodeId(final Connection connection) { } protected void closeConnection(final @Nullable Connection connection) { - this.closeConnection(connection, true); - } - - protected void closeConnection(final @Nullable Connection connection, final boolean unstableConnection) { try { if (connection != null && !connection.isClosed()) { - if (unstableConnection) { - try { - connection.setNetworkTimeout(networkTimeoutExecutor, closeConnectionNetworkTimeoutMs); - } catch (SQLException ex) { - // do nothing - } + try { + connection.setNetworkTimeout(networkTimeoutExecutor, closeConnectionNetworkTimeoutMs); + } catch (SQLException ex) { + // do nothing } connection.close(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index 96d5c4e47..1bcf500fd 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -70,7 +70,7 @@ public class CustomEndpointMonitorImpl extends AbstractMonitor implements Custom * custom endpoint info. * @param telemetryFactory The telemetry factory * @param customEndpointHostSpec The host information for the custom endpoint to be monitored. - * @param endpointIdentifier An endpoint identifier. + * @param endpointIdentifier An endpoint identifier. * @param region The region of the custom endpoint to be monitored. * @param refreshRateNano Controls how often the custom endpoint information should be fetched and analyzed for * changes. The value specified should be in nanoseconds. @@ -86,7 +86,7 @@ public CustomEndpointMonitorImpl( Region region, long refreshRateNano, BiFunction rdsClientFunc) { - super(monitorService); + super(monitorService, 30); this.storageService = storageService; this.customEndpointHostSpec = customEndpointHostSpec; this.endpointIdentifier = endpointIdentifier; @@ -105,7 +105,7 @@ public void monitor() { LOGGER.fine( Messages.get( "CustomEndpointMonitorImpl.startingMonitor", - new Object[] { this.customEndpointHostSpec.getUrl() })); + new Object[] {this.customEndpointHostSpec.getUrl()})); try { while (!this.stop.get() && !Thread.currentThread().isInterrupted()) { @@ -175,14 +175,14 @@ public void monitor() { LOGGER.log(Level.SEVERE, Messages.get( "CustomEndpointMonitorImpl.exception", - new Object[]{this.customEndpointHostSpec.getUrl()}), e); + new Object[] {this.customEndpointHostSpec.getUrl()}), e); } } } catch (InterruptedException e) { LOGGER.fine( Messages.get( "CustomEndpointMonitorImpl.interrupted", - new Object[]{ this.customEndpointHostSpec.getUrl() })); + new Object[] {this.customEndpointHostSpec.getUrl()})); Thread.currentThread().interrupt(); } finally { customEndpointInfoCache.remove(this.customEndpointHostSpec.getUrl()); @@ -190,7 +190,7 @@ public void monitor() { LOGGER.fine( Messages.get( "CustomEndpointMonitorImpl.stoppedMonitor", - new Object[]{ this.customEndpointHostSpec.getUrl() })); + new Object[] {this.customEndpointHostSpec.getUrl()})); } } @@ -198,40 +198,10 @@ public boolean hasCustomEndpointInfo() { return customEndpointInfoCache.get(this.customEndpointHostSpec.getUrl()) != null; } - /** - * Stops the custom endpoint monitor. - */ @Override - public void stop() { - LOGGER.fine( - Messages.get( - "CustomEndpointMonitorImpl.stoppingMonitor", - new Object[]{ this.customEndpointHostSpec.getUrl() })); - - this.stop.set(true); - - try { - int terminationTimeoutSec = 5; - if (!this.monitorExecutor.awaitTermination(terminationTimeoutSec, TimeUnit.SECONDS)) { - LOGGER.info( - Messages.get( - "CustomEndpointMonitorImpl.monitorTerminationTimeout", - new Object[]{ terminationTimeoutSec, this.customEndpointHostSpec.getUrl() })); - - this.monitorExecutor.shutdownNow(); - } - } catch (InterruptedException e) { - LOGGER.info( - Messages.get( - "CustomEndpointMonitorImpl.interruptedWhileTerminating", - new Object[]{ this.customEndpointHostSpec.getUrl() })); - - Thread.currentThread().interrupt(); - this.monitorExecutor.shutdownNow(); - } finally { - customEndpointInfoCache.remove(this.customEndpointHostSpec.getUrl()); - this.rdsClient.close(); - } + public void close() { + customEndpointInfoCache.remove(this.customEndpointHostSpec.getUrl()); + this.rdsClient.close(); } /** diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index c6d5c0ac1..b9d3c224d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -18,6 +18,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; import software.amazon.jdbc.util.Messages; @@ -28,6 +30,8 @@ */ public abstract class AbstractMonitor implements Monitor, Runnable { private static final Logger LOGGER = Logger.getLogger(AbstractMonitor.class.getName()); + protected final AtomicBoolean stop = new AtomicBoolean(false); + protected final long terminationTimeoutSec; protected final MonitorService monitorService; protected final ExecutorService monitorExecutor = Executors.newSingleThreadExecutor(runnableTarget -> { final Thread monitoringThread = new Thread(runnableTarget); @@ -41,8 +45,9 @@ public abstract class AbstractMonitor implements Monitor, Runnable { protected long lastActivityTimestampNanos; protected MonitorState state; - protected AbstractMonitor(MonitorService monitorService) { + protected AbstractMonitor(MonitorService monitorService, long terminationTimeoutSec) { this.monitorService = monitorService; + this.terminationTimeoutSec = terminationTimeoutSec; this.lastActivityTimestampNanos = System.nanoTime(); } @@ -64,12 +69,32 @@ public void run() { this.lastActivityTimestampNanos = System.nanoTime(); monitor(); } catch (Exception e) { - LOGGER.fine(Messages.get("AbstractMonitor.unexpectedError", new Object[]{this, e})); + LOGGER.fine(Messages.get("AbstractMonitor.unexpectedError", new Object[] {this, e})); this.state = MonitorState.ERROR; monitorService.reportMonitorError(this, e); } } + @Override + public void stop() { + LOGGER.fine(Messages.get("AbstractMonitor.stoppingMonitor", new Object[] {this})); + this.stop.set(true); + + try { + if (!this.monitorExecutor.awaitTermination(this.terminationTimeoutSec, TimeUnit.SECONDS)) { + LOGGER.info(Messages.get( + "AbstractMonitor.monitorTerminationTimeout", new Object[] {terminationTimeoutSec, this})); + this.monitorExecutor.shutdownNow(); + } + } catch (InterruptedException e) { + LOGGER.info(Messages.get("AbstractMonitor.interruptedWhileTerminating", new Object[] {this})); + Thread.currentThread().interrupt(); + this.monitorExecutor.shutdownNow(); + } finally { + close(); + } + } + @Override public long getLastActivityTimestampNanos() { return this.lastActivityTimestampNanos; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index c8eb5387f..b9b47a018 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -33,6 +33,11 @@ public interface Monitor { */ void stop(); + /** + * Closes all resources used by this monitor. This method will be called as part of {@link #stop()}. + */ + void close(); + /** * Gets the timestamp for the last action performed by this monitor, in nanoseconds. * diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 6b087e7c0..0bbed69d0 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -14,6 +14,9 @@ # limitations under the License. # +AbstractMonitor.interruptedWhileTerminating=Interrupted while awaiting termination of monitor ''{0}''. The monitor will be forcefully shut down. +AbstractMonitor.monitorTerminationTimeout=Timed out after waiting {0} seconds for monitor ''{1}'' to terminate gracefully. The monitor will be forcefully shut down. +AbstractMonitor.stoppingMonitor=Stopping monitor: ''{0}''. AbstractMonitor.unexpectedError=A monitor encountered an unexpected exception. Monitor: ''{0}''. Exception: ''{1}''. # ADFS Credentials Provider Getter @@ -130,11 +133,8 @@ CustomEndpointMonitorImpl.clearCache=Clearing info in the custom endpoint monito CustomEndpointMonitorImpl.detectedChangeInCustomEndpointInfo=Detected change in custom endpoint info for ''{0}'':\n{1} CustomEndpointMonitorImpl.exception=Encountered an exception while monitoring custom endpoint ''{0}''. CustomEndpointMonitorImpl.interrupted=Custom endpoint monitor for ''{0}'' was interrupted. -CustomEndpointMonitorImpl.interruptedWhileTerminating=Interrupted while awaiting termination of custom endpoint monitor for ''{0}''. The monitor will be forcefully shut down. -CustomEndpointMonitorImpl.monitorTerminationTimeout=Timed out after waiting {0} seconds for custom endpoint monitor for ''{1}'' to terminate gracefully. The monitor will be forcefully shut down. CustomEndpointMonitorImpl.startingMonitor=Starting custom endpoint monitor for ''{0}''. CustomEndpointMonitorImpl.stoppedMonitor=Stopped custom endpoint monitor for ''{0}''. -CustomEndpointMonitorImpl.stoppingMonitor=Stopping custom endpoint monitor for ''{0}''. CustomEndpointMonitorImpl.unexpectedNumberOfEndpoints=Unexpected number of custom endpoints with endpoint identifier ''{0}'' in region ''{1}''. Expected 1, but found {2}. Endpoints:\n{3}. # Custom Endpoint Plugin From 639d005a8e64ac7fde8872d86d889a5ec1b6ae5c Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 9 May 2025 13:58:47 -0700 Subject: [PATCH 096/149] Address PR comments --- .../amazon/jdbc/ConnectionPlugin.java | 1 - .../amazon/jdbc/MonitorPluginService.java | 28 +++++-------------- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java index b0636c5f2..d2d72b05c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPlugin.java @@ -23,7 +23,6 @@ import java.util.Map; import java.util.Properties; import java.util.Set; -import software.amazon.jdbc.util.connection.ConnectionService; /** * Interface for connection plugins. This class implements ways to execute a JDBC method and to clean up resources used diff --git a/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java index 2a1b4ab7d..a21783710 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java @@ -220,12 +220,12 @@ public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHo @Override public boolean acceptsStrategy(HostRole role, String strategy) throws SQLException { - return this.pluginManager.acceptsStrategy(role, strategy); + throw new UnsupportedOperationException(); } @Override public HostSpec getHostSpecByStrategy(HostRole role, String strategy) throws SQLException { - return this.pluginManager.getHostSpecByStrategy(role, strategy); + throw new UnsupportedOperationException(); } @Override @@ -476,7 +476,7 @@ public void setAvailability(final @NonNull Set hostAliases, final @NonNu @Override public boolean isInTransaction() { - return this.isInTransaction; + throw new UnsupportedOperationException(); } @Override @@ -603,7 +603,7 @@ public void setHostListProvider(final HostListProvider hostListProvider) { @Override public Connection connect(final HostSpec hostSpec, final Properties props) throws SQLException { - return this.connect(hostSpec, props, null); + throw new UnsupportedOperationException(); } @Override @@ -612,8 +612,7 @@ public Connection connect( final Properties props, final @Nullable ConnectionPlugin pluginToSkip) throws SQLException { - return this.pluginManager.connect( - this.driverProtocol, hostSpec, props, this.currentConnection == null, pluginToSkip); + throw new UnsupportedOperationException(); } @Override @@ -645,20 +644,7 @@ private void updateHostAvailability(final List hosts) { @Override public void releaseResources() { - LOGGER.fine(() -> Messages.get("PluginServiceImpl.releaseResources")); - - try { - if (this.currentConnection != null && !this.currentConnection.isClosed()) { - this.currentConnection.close(); - } - } catch (final SQLException e) { - // Ignore an exception - } - - if (this.hostListProvider != null && this.hostListProvider instanceof CanReleaseResources) { - final CanReleaseResources canReleaseResourcesObject = (CanReleaseResources) this.hostListProvider; - canReleaseResourcesObject.releaseResources(); - } + throw new UnsupportedOperationException(); } @Override @@ -777,7 +763,7 @@ public String getTargetName() { @Override public @NonNull SessionStateService getSessionStateService() { - return this.sessionStateService; + throw new UnsupportedOperationException(); } public T getPlugin(final Class pluginClazz) { From a255b1ff99bfd9e9832ef90a4b3f01de98dfc1ef Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 9 May 2025 14:02:41 -0700 Subject: [PATCH 097/149] Undo integration test changes for local run --- .../container/tests/Failover2Test.java | 58 +++++++++---------- .../container/tests/FailoverTest.java | 2 +- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/wrapper/src/test/java/integration/container/tests/Failover2Test.java b/wrapper/src/test/java/integration/container/tests/Failover2Test.java index 92e9b7507..22aba7655 100644 --- a/wrapper/src/test/java/integration/container/tests/Failover2Test.java +++ b/wrapper/src/test/java/integration/container/tests/Failover2Test.java @@ -15,32 +15,32 @@ */ package integration.container.tests; -// -// import integration.TestEnvironmentFeatures; -// import integration.container.TestDriverProvider; -// import integration.container.condition.DisableOnTestFeature; -// import integration.container.condition.EnableOnNumOfInstances; -// import integration.container.condition.EnableOnTestFeature; -// import integration.container.condition.MakeSureFirstInstanceWriter; -// import org.junit.jupiter.api.MethodOrderer; -// import org.junit.jupiter.api.Order; -// import org.junit.jupiter.api.TestMethodOrder; -// import org.junit.jupiter.api.extension.ExtendWith; -// -// @TestMethodOrder(MethodOrderer.MethodName.class) -// @ExtendWith(TestDriverProvider.class) -// @EnableOnTestFeature(TestEnvironmentFeatures.FAILOVER_SUPPORTED) -// @DisableOnTestFeature({ -// TestEnvironmentFeatures.PERFORMANCE, -// TestEnvironmentFeatures.RUN_HIBERNATE_TESTS_ONLY, -// TestEnvironmentFeatures.RUN_AUTOSCALING_TESTS_ONLY}) -// @EnableOnNumOfInstances(min = 2) -// @MakeSureFirstInstanceWriter -// @Order(15) -// public class Failover2Test extends FailoverTest { -// -// @Override -// protected String getFailoverPlugin() { -// return "failover2"; -// } -// } + +import integration.TestEnvironmentFeatures; +import integration.container.TestDriverProvider; +import integration.container.condition.DisableOnTestFeature; +import integration.container.condition.EnableOnNumOfInstances; +import integration.container.condition.EnableOnTestFeature; +import integration.container.condition.MakeSureFirstInstanceWriter; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; + +@TestMethodOrder(MethodOrderer.MethodName.class) +@ExtendWith(TestDriverProvider.class) +@EnableOnTestFeature(TestEnvironmentFeatures.FAILOVER_SUPPORTED) +@DisableOnTestFeature({ + TestEnvironmentFeatures.PERFORMANCE, + TestEnvironmentFeatures.RUN_HIBERNATE_TESTS_ONLY, + TestEnvironmentFeatures.RUN_AUTOSCALING_TESTS_ONLY}) +@EnableOnNumOfInstances(min = 2) +@MakeSureFirstInstanceWriter +@Order(15) +public class Failover2Test extends FailoverTest { + + @Override + protected String getFailoverPlugin() { + return "failover2"; + } +} diff --git a/wrapper/src/test/java/integration/container/tests/FailoverTest.java b/wrapper/src/test/java/integration/container/tests/FailoverTest.java index 46a9990b0..2a0b7c13e 100644 --- a/wrapper/src/test/java/integration/container/tests/FailoverTest.java +++ b/wrapper/src/test/java/integration/container/tests/FailoverTest.java @@ -671,7 +671,7 @@ public void test_readerFailover_writerReelected() throws SQLException { // Helper methods below protected String getFailoverPlugin() { - return "failover2"; + return "failover"; } protected Properties initDefaultProxiedProps() { From 7f9374d93abdc616b017e5f0a19df9fd56b3ffbf Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 9 May 2025 14:17:06 -0700 Subject: [PATCH 098/149] PR comments --- .../monitoring/ClusterTopologyMonitorImpl.java | 4 ++-- .../amazon/jdbc/util/connection/ConnectionService.java | 2 +- .../amazon/jdbc/util/connection/ConnectionServiceImpl.java | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index b060ee97b..6352ff674 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -502,7 +502,7 @@ protected List openAnyConnectionAndUpdateTopology() { // open a new connection try { - conn = this.connectionService.createAuxiliaryConnection(this.initialHostSpec, this.monitoringProperties); + conn = this.connectionService.open(this.initialHostSpec, this.monitoringProperties); } catch (SQLException ex) { // can't connect return null; @@ -828,7 +828,7 @@ public void run() { if (connection == null) { try { - connection = this.monitor.connectionService.createAuxiliaryConnection( + connection = this.monitor.connectionService.open( hostSpec, this.monitor.monitoringProperties); this.monitor.pluginService.setAvailability( hostSpec.asAliases(), HostAvailability.AVAILABLE); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java index 7e8c2cee3..2bdd8161d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionService.java @@ -31,7 +31,7 @@ public interface ConnectionService { * @param props the properties for the auxiliary connection. * @return a new connection to the given host using the given props. */ - Connection createAuxiliaryConnection(HostSpec hostSpec, Properties props) throws SQLException; + Connection open(HostSpec hostSpec, Properties props) throws SQLException; PluginService getPluginService(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index 1dc050c43..04b518571 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -54,7 +54,6 @@ public ConnectionServiceImpl( connectionProvider, null, null, - // TODO: is it okay to share the telemetry factory? telemetryFactory); serviceContainer.setConnectionPluginManager(this.pluginManager); @@ -76,7 +75,7 @@ public ConnectionServiceImpl( } @Override - public Connection createAuxiliaryConnection(HostSpec hostSpec, Properties props) throws SQLException { + public Connection open(HostSpec hostSpec, Properties props) throws SQLException { return this.pluginManager.forceConnect(this.targetDriverProtocol, hostSpec, props, true, null); } From 5964f3bde5ee1c172bcd9347a76a285225802539 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 9 May 2025 14:44:41 -0700 Subject: [PATCH 099/149] Remove shadowing stop field in concrete monitor implementations --- .../hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java | 1 - .../jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java | 2 -- .../plugin/customendpoint/CustomEndpointMonitorImplTest.java | 2 -- 3 files changed, 5 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 6352ff674..2dc9209d0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -97,7 +97,6 @@ public class ClusterTopologyMonitorImpl extends AbstractMonitor implements Clust protected final AtomicReference writerHostSpec = new AtomicReference<>(null); protected final AtomicReference monitoringConnection = new AtomicReference<>(null); protected boolean isVerifiedWriterConnection = false; - protected final AtomicBoolean stop = new AtomicBoolean(false); protected long highRefreshRateEndTimeNano = 0; protected final Object topologyUpdated = new Object(); protected final AtomicBoolean requestToUpdateTopology = new AtomicBoolean(false); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index 1bcf500fd..26e005989 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -20,7 +20,6 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiFunction; import java.util.logging.Level; import java.util.logging.Logger; @@ -52,7 +51,6 @@ public class CustomEndpointMonitorImpl extends AbstractMonitor implements Custom protected static final CacheMap customEndpointInfoCache = new CacheMap<>(); protected static final long CUSTOM_ENDPOINT_INFO_EXPIRATION_NANO = TimeUnit.MINUTES.toNanos(5); - protected final AtomicBoolean stop = new AtomicBoolean(false); protected final RdsClient rdsClient; protected final HostSpec customEndpointHostSpec; protected final String endpointIdentifier; diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java index ec1f5a0f3..489420b15 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java @@ -18,7 +18,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; @@ -138,7 +137,6 @@ public void testRun() throws InterruptedException { // Wait for monitor to close TimeUnit.MILLISECONDS.sleep(50); - assertTrue(monitor.stop.get()); verify(mockRdsClient, atLeastOnce()).close(); } } From 116e6f15016c1a7ef86154a779bdb814591b8e19 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 12 May 2025 12:36:07 -0700 Subject: [PATCH 100/149] Cleanup PartialPluginService, MonitorService --- ...Service.java => PartialPluginService.java} | 58 ++++++++----------- .../CustomEndpointMonitorImpl.java | 2 +- .../connection/ConnectionServiceImpl.java | 14 ++--- .../jdbc/util/monitoring/MonitorService.java | 13 ++++- .../util/monitoring/MonitorServiceImpl.java | 6 +- ..._advanced_jdbc_wrapper_messages.properties | 2 + 6 files changed, 48 insertions(+), 47 deletions(-) rename wrapper/src/main/java/software/amazon/jdbc/{MonitorPluginService.java => PartialPluginService.java} (93%) diff --git a/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java similarity index 93% rename from wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java rename to wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java index a21783710..4417929ee 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/MonitorPluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java @@ -53,7 +53,13 @@ import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -public class MonitorPluginService implements PluginService, CanReleaseResources, HostListProviderService, +/** + * A {@link PluginService} containing some methods that are not intended to be called. This class is intended to be used + * by monitors, which require a {@link PluginService}, but are not expected to need or use some of the methods defined + * by the {@link PluginService} interface. The methods that are not expected to be called will log a warning or throw an + * {@link UnsupportedOperationException} when called. + */ +public class PartialPluginService implements PluginService, CanReleaseResources, HostListProviderService, PluginManagerService { private static final Logger LOGGER = Logger.getLogger(PluginServiceImpl.class.getName()); @@ -83,7 +89,7 @@ public class MonitorPluginService implements PluginService, CanReleaseResources, protected final ReentrantLock connectionSwitchLock = new ReentrantLock(); - public MonitorPluginService( + public PartialPluginService( @NonNull final ServiceContainer serviceContainer, @NonNull final Properties props, @NonNull final String originalUrl, @@ -102,27 +108,7 @@ public MonitorPluginService( null); } - public MonitorPluginService( - @NonNull final ServiceContainer serviceContainer, - @NonNull final Properties props, - @NonNull final String originalUrl, - @NonNull final String targetDriverProtocol, - @NonNull final TargetDriverDialect targetDriverDialect, - @NonNull final Dialect dbDialect, - @Nullable final ConfigurationProfile configurationProfile) { - this( - serviceContainer, - new ExceptionManager(), - props, - originalUrl, - targetDriverProtocol, - targetDriverDialect, - dbDialect, - configurationProfile, - null); - } - - public MonitorPluginService( + public PartialPluginService( @NonNull final ServiceContainer serviceContainer, @NonNull final ExceptionManager exceptionManager, @NonNull final Properties props, @@ -220,12 +206,14 @@ public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHo @Override public boolean acceptsStrategy(HostRole role, String strategy) throws SQLException { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException( + Messages.get("LimitedPluginService.unexpectedMethodCall", new Object[] {"acceptsStrategy"})); } @Override public HostSpec getHostSpecByStrategy(HostRole role, String strategy) throws SQLException { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException( + Messages.get("LimitedPluginService.unexpectedMethodCall", new Object[] {"getHostSpecByStrategy"})); } @Override @@ -476,7 +464,8 @@ public void setAvailability(final @NonNull Set hostAliases, final @NonNu @Override public boolean isInTransaction() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException( + Messages.get("LimitedPluginService.unexpectedMethodCall", new Object[] {"isInTransaction"})); } @Override @@ -603,7 +592,8 @@ public void setHostListProvider(final HostListProvider hostListProvider) { @Override public Connection connect(final HostSpec hostSpec, final Properties props) throws SQLException { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException( + Messages.get("LimitedPluginService.unexpectedMethodCall", new Object[] {"connect"})); } @Override @@ -612,7 +602,8 @@ public Connection connect( final Properties props, final @Nullable ConnectionPlugin pluginToSkip) throws SQLException { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException( + Messages.get("LimitedPluginService.unexpectedMethodCall", new Object[] {"connect"})); } @Override @@ -644,7 +635,8 @@ private void updateHostAvailability(final List hosts) { @Override public void releaseResources() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException( + Messages.get("LimitedPluginService.unexpectedMethodCall", new Object[] {"releaseResources"})); } @Override @@ -701,9 +693,8 @@ public TargetDriverDialect getTargetDriverDialect() { @Override public void updateDialect(final @NonNull Connection connection) { - // TODO: is it fine to just leave this empty? This method is called after connecting in DefaultConnectionPlugin but - // the dialect passed to the constructor should already be up to date. - // do nothing. + // do nothing. This method is called after connecting in DefaultConnectionPlugin but the dialect passed to the + // constructor should already be updated and verified. } @Override @@ -763,7 +754,8 @@ public String getTargetName() { @Override public @NonNull SessionStateService getSessionStateService() { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException( + Messages.get("LimitedPluginService.unexpectedMethodCall", new Object[] {"getSessionStateService"})); } public T getPlugin(final Class pluginClazz) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index 26e005989..620845307 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -66,7 +66,7 @@ public class CustomEndpointMonitorImpl extends AbstractMonitor implements Custom * @param monitorService The monitorService used to submit this monitor. * @param storageService The storage service used to store the set of allowed/blocked hosts according to the * custom endpoint info. - * @param telemetryFactory The telemetry factory + * @param telemetryFactory The telemetry factory used to create telemetry data. * @param customEndpointHostSpec The host information for the custom endpoint to be monitored. * @param endpointIdentifier An endpoint identifier. * @param region The region of the custom endpoint to be monitored. diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index 04b518571..140836912 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -22,7 +22,7 @@ import software.amazon.jdbc.ConnectionPluginManager; import software.amazon.jdbc.ConnectionProvider; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.MonitorPluginService; +import software.amazon.jdbc.PartialPluginService; import software.amazon.jdbc.PluginService; import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; @@ -57,7 +57,7 @@ public ConnectionServiceImpl( telemetryFactory); serviceContainer.setConnectionPluginManager(this.pluginManager); - MonitorPluginService monitorPluginService = new MonitorPluginService( + PartialPluginService partialPluginService = new PartialPluginService( serviceContainer, props, originalUrl, @@ -66,12 +66,12 @@ public ConnectionServiceImpl( dbDialect ); - this.pluginService = monitorPluginService; - serviceContainer.setHostListProviderService(monitorPluginService); - serviceContainer.setPluginService(monitorPluginService); - serviceContainer.setPluginManagerService(monitorPluginService); + this.pluginService = partialPluginService; + serviceContainer.setHostListProviderService(partialPluginService); + serviceContainer.setPluginService(partialPluginService); + serviceContainer.setPluginManagerService(partialPluginService); - this.pluginManager.init(monitorPluginService, props, monitorPluginService, null); + this.pluginManager.init(partialPluginService, props, partialPluginService, null); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 30ca23f2d..466a3558f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -52,12 +52,19 @@ void registerMonitorTypeIfAbsent( * Creates and starts the given monitor if it does not already exist and stores it under the given monitor type and * key. If the monitor already exists, its expiration time will be renewed, even if it was already expired. * - * @param monitorClass the class of the monitor, eg `CustomEndpointMonitorImpl.class`. + * @param monitorClass the concrete class of the monitor, eg `CustomEndpointMonitorImpl.class`. * @param key the key for the monitor, eg * "custom-endpoint.cluster-custom-XYZ.us-east-2.rds.amazonaws.com:5432". + * @param storageService the storage service for the monitor to use. + * @param telemetryFactory the telemetry factory for creating telemetry data. + * @param originalUrl the URL of the original database connection. + * @param driverProtocol the protocol for the underlying target driver. + * @param driverDialect the target driver dialect. + * @param dbDialect the database dialect. + * @param originalProps the properties of the original database connection. + * @param initializer an initializer function to use to create the monitor if it does not already exist. * @return the new or existing monitor. */ - // TODO: add docs for new parameters T runIfAbsent( Class monitorClass, Object key, @@ -67,7 +74,7 @@ T runIfAbsent( String driverProtocol, TargetDriverDialect driverDialect, Dialect dbDialect, - Properties props, + Properties originalProps, MonitorInitializer initializer) throws SQLException; /** diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 422a569f9..2eaf7e4fa 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -208,7 +208,7 @@ public T runIfAbsent( String driverProtocol, TargetDriverDialect driverDialect, Dialect dbDialect, - Properties props, + Properties originalProps, MonitorInitializer initializer) throws SQLException { CacheContainer cacheContainer = monitorCaches.get(monitorClass); if (cacheContainer == null) { @@ -222,9 +222,9 @@ public T runIfAbsent( } TargetDriverHelper helper = new TargetDriverHelper(); - java.sql.Driver driver = helper.getTargetDriver(originalUrl, props); + java.sql.Driver driver = helper.getTargetDriver(originalUrl, originalProps); final ConnectionProvider defaultConnectionProvider = new DriverConnectionProvider(driver); - final Properties propsCopy = PropertyUtils.copyProperties(props); + final Properties propsCopy = PropertyUtils.copyProperties(originalProps); final ConnectionServiceImpl connectionService = new ConnectionServiceImpl( storageService, this, diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 0bbed69d0..758eccee5 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -235,6 +235,8 @@ IamAuthConnectionPlugin.unhandledException=Unhandled exception: ''{0}'' IamAuthConnectionPlugin.connectException=Error occurred while opening a connection: ''{0}'' IamAuthConnectionPlugin.unableToDetermineRegion=Unable to determine connection region. If you are using a non-standard RDS URL, please set the ''{0}'' property. +LimitedPluginService.unexpectedMethodCall=''LimitedPluginService#{0}()'' was unexpectedly called. Users of this class are not expected to call this method, so the method does not provide any functionality. + # Limitless Connection Plugin LimitlessConnectionPlugin.failedToConnectToHost=Failed to connect to host {0}. LimitlessConnectionPlugin.unsupportedDialectOrDatabase=Unsupported dialect ''{0}'' encountered. Please ensure JDBC connection parameters are correct, and refer to the documentation to ensure that the connecting database is compatible with the Limitless Connection Plugin. From 4137f85b5fdeb3accbc39bc83d6a5a7abc3d8be7 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 13 May 2025 16:51:32 -0700 Subject: [PATCH 101/149] testMonitorRecreation wip --- .../java/software/amazon/jdbc/Driver.java | 6 +- .../amazon/jdbc/PartialPluginService.java | 14 +- .../amazon/jdbc/ds/AwsWrapperDataSource.java | 4 +- ...isher.java => BatchingEventPublisher.java} | 15 +- .../jdbc/util/events/EventSubscriber.java | 6 +- .../jdbc/util/monitoring/AbstractMonitor.java | 4 +- .../jdbc/util/monitoring/MonitorService.java | 15 +- .../util/monitoring/MonitorServiceImpl.java | 63 ++------- .../jdbc/util/storage/StorageService.java | 2 +- .../jdbc/util/storage/StorageServiceImpl.java | 7 +- ..._advanced_jdbc_wrapper_messages.properties | 6 +- .../monitoring/MonitorServiceImplTest.java | 131 ++++++++++++++++++ 12 files changed, 185 insertions(+), 88 deletions(-) rename wrapper/src/main/java/software/amazon/jdbc/util/events/{PeriodicEventPublisher.java => BatchingEventPublisher.java} (84%) create mode 100644 wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index 29f6e7fd1..6cef2f9f3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -66,7 +66,7 @@ import software.amazon.jdbc.util.ServiceContainerImpl; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.events.EventPublisher; -import software.amazon.jdbc.util.events.PeriodicEventPublisher; +import software.amazon.jdbc.util.events.BatchingEventPublisher; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.monitoring.MonitorServiceImpl; import software.amazon.jdbc.util.storage.StorageService; @@ -84,7 +84,7 @@ public class Driver implements java.sql.Driver { private static final Logger LOGGER = Logger.getLogger("software.amazon.jdbc.Driver"); private static @Nullable Driver registeredDriver; - private static final EventPublisher publisher = new PeriodicEventPublisher(); + private static final EventPublisher publisher = new BatchingEventPublisher(); private static final StorageService storageService = new StorageServiceImpl(publisher); private static final MonitorService monitorService = new MonitorServiceImpl(publisher); @@ -420,7 +420,7 @@ public static void clearCaches() { } public static void releaseResources() { - monitorService.stopAndRemoveAll(); + monitorService.releaseResources(); software.amazon.jdbc.plugin.efm2.MonitorServiceImpl.closeAllMonitors(); MonitorThreadContainer.releaseInstance(); ConnectionProviderManager.releaseResources(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java index 4417929ee..2fc72f273 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java @@ -207,13 +207,13 @@ public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHo @Override public boolean acceptsStrategy(HostRole role, String strategy) throws SQLException { throw new UnsupportedOperationException( - Messages.get("LimitedPluginService.unexpectedMethodCall", new Object[] {"acceptsStrategy"})); + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"acceptsStrategy"})); } @Override public HostSpec getHostSpecByStrategy(HostRole role, String strategy) throws SQLException { throw new UnsupportedOperationException( - Messages.get("LimitedPluginService.unexpectedMethodCall", new Object[] {"getHostSpecByStrategy"})); + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"getHostSpecByStrategy"})); } @Override @@ -465,7 +465,7 @@ public void setAvailability(final @NonNull Set hostAliases, final @NonNu @Override public boolean isInTransaction() { throw new UnsupportedOperationException( - Messages.get("LimitedPluginService.unexpectedMethodCall", new Object[] {"isInTransaction"})); + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"isInTransaction"})); } @Override @@ -593,7 +593,7 @@ public void setHostListProvider(final HostListProvider hostListProvider) { @Override public Connection connect(final HostSpec hostSpec, final Properties props) throws SQLException { throw new UnsupportedOperationException( - Messages.get("LimitedPluginService.unexpectedMethodCall", new Object[] {"connect"})); + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"connect"})); } @Override @@ -603,7 +603,7 @@ public Connection connect( final @Nullable ConnectionPlugin pluginToSkip) throws SQLException { throw new UnsupportedOperationException( - Messages.get("LimitedPluginService.unexpectedMethodCall", new Object[] {"connect"})); + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"connect"})); } @Override @@ -636,7 +636,7 @@ private void updateHostAvailability(final List hosts) { @Override public void releaseResources() { throw new UnsupportedOperationException( - Messages.get("LimitedPluginService.unexpectedMethodCall", new Object[] {"releaseResources"})); + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"releaseResources"})); } @Override @@ -755,7 +755,7 @@ public String getTargetName() { @Override public @NonNull SessionStateService getSessionStateService() { throw new UnsupportedOperationException( - Messages.get("LimitedPluginService.unexpectedMethodCall", new Object[] {"getSessionStateService"})); + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"getSessionStateService"})); } public T getPlugin(final Class pluginClazz) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index b14681281..9e9be2c05 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -55,7 +55,7 @@ import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; import software.amazon.jdbc.util.events.EventPublisher; -import software.amazon.jdbc.util.events.PeriodicEventPublisher; +import software.amazon.jdbc.util.events.BatchingEventPublisher; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.monitoring.MonitorServiceImpl; import software.amazon.jdbc.util.storage.StorageService; @@ -75,7 +75,7 @@ public class AwsWrapperDataSource implements DataSource, Referenceable, Serializ private static final String SERVER_NAME = "serverName"; private static final String SERVER_PORT = "serverPort"; - private static final EventPublisher publisher = new PeriodicEventPublisher(); + private static final EventPublisher publisher = new BatchingEventPublisher(); private static final StorageService storageService = new StorageServiceImpl(publisher); private static final MonitorService monitorService = new MonitorServiceImpl(publisher); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java similarity index 84% rename from wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java rename to wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java index eb14a78ef..7981eca01 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/PeriodicEventPublisher.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java @@ -27,27 +27,26 @@ import software.amazon.jdbc.util.StringUtils; /** - * An event publisher that periodically sends out all messages received during the latest interval of time. Messages are - * recorded in a set so that messages of equivalent value are not duplicated in the same message batch. + * An event publisher that periodically publishes a batch of all unique events encountered during the latest time + * interval. Batches do not contain duplicate events; if the current batch receives a duplicate, it will not be + * added to the batch and the original event will only be published once, when the entire batch is published. */ -public class PeriodicEventPublisher implements EventPublisher { +public class BatchingEventPublisher implements EventPublisher { protected static final long DEFAULT_MESSAGE_INTERVAL_NANOS = TimeUnit.SECONDS.toNanos(30); protected final Map, Set> subscribersMap = new ConcurrentHashMap<>(); // ConcurrentHashMap.newKeySet() is the recommended way to get a concurrent set. A set is used to prevent duplicate // event messages from being sent in the same message batch. - // TODO: should duplicate events be allowed? Data access events may happen frequently so duplicates could result in - // a lot of unnecessary repeated processing. protected final Set eventMessages = ConcurrentHashMap.newKeySet(); protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { final Thread thread = new Thread(r); thread.setDaemon(true); if (!StringUtils.isNullOrEmpty(thread.getName())) { - thread.setName(thread.getName() + "-epi"); + thread.setName(thread.getName() + "-bep"); } return thread; })); - public PeriodicEventPublisher() { + public BatchingEventPublisher() { this(DEFAULT_MESSAGE_INTERVAL_NANOS); } @@ -56,7 +55,7 @@ public PeriodicEventPublisher() { * * @param messageIntervalNanos the rate at which messages batches should be sent, in nanoseconds. */ - public PeriodicEventPublisher(long messageIntervalNanos) { + public BatchingEventPublisher(long messageIntervalNanos) { cleanupExecutor.scheduleAtFixedRate( this::sendMessages, messageIntervalNanos, messageIntervalNanos, TimeUnit.NANOSECONDS); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java index b371dbc87..34e63b452 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/EventSubscriber.java @@ -20,7 +20,11 @@ /** * An event subscriber. Subscribers can subscribe to a publisher's events using - * {@link EventPublisher#subscribe(EventSubscriber, Set)}. + * {@link EventPublisher#subscribe(EventSubscriber, Set)}. Subscribers will typically be stored in a + * {@link java.util.HashSet} to prevent duplicate subscriptions, so classes implementing this interface should consider + * whether they need to override {@link Object#equals(Object)} and {@link Object#hashCode()}. + * + * @see EventPublisher */ public interface EventSubscriber { /** diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index b9d3c224d..670dc8309 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -65,13 +65,14 @@ public void start() { @Override public void run() { try { + LOGGER.finest(Messages.get("AbstractMonitor.startingMonitor", new Object[] {this})); this.state = MonitorState.RUNNING; this.lastActivityTimestampNanos = System.nanoTime(); monitor(); } catch (Exception e) { LOGGER.fine(Messages.get("AbstractMonitor.unexpectedError", new Object[] {this, e})); this.state = MonitorState.ERROR; - monitorService.reportMonitorError(this, e); + // monitorService.reportMonitorError(this, e); } } @@ -92,6 +93,7 @@ public void stop() { this.monitorExecutor.shutdownNow(); } finally { close(); + this.state = MonitorState.STOPPED; } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index 466a3558f..41df2c567 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -87,15 +87,6 @@ T runIfAbsent( @Nullable T get(Class monitorClass, Object key); - /** - * Processes a monitor error. The monitor service will respond to the error based on the monitor error responses - * defined when the monitor type was registered. - * - * @param monitor the monitor that encountered the unexpected exception. - * @param exception the unexpected exception that occurred. - */ - void reportMonitorError(Monitor monitor, Exception exception); - /** * Removes the monitor stored at the given key. If the expected monitor class does not match the actual monitor class * no action will be performed. @@ -128,4 +119,10 @@ T runIfAbsent( * Stops all monitors and removes them from the monitor service. */ void stopAndRemoveAll(); + + /** + * Releases any resources opened by the monitor service, stops all monitors, and removes all monitors from the monitor + * service. + */ + void releaseResources(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 2eaf7e4fa..692c78232 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -27,8 +27,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.NonNull; @@ -57,11 +55,9 @@ public class MonitorServiceImpl implements MonitorService, EventSubscriber { private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(1); - protected static final Map, CacheContainer> monitorCaches = new ConcurrentHashMap<>(); protected static final Map, Supplier> defaultSuppliers; - protected static final AtomicBoolean isInitialized = new AtomicBoolean(false); - protected static final ReentrantLock initLock = new ReentrantLock(); - protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { + protected final Map, CacheContainer> monitorCaches = new ConcurrentHashMap<>(); + protected final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { final Thread thread = new Thread(r); thread.setDaemon(true); if (!StringUtils.isNullOrEmpty(thread.getName())) { @@ -102,27 +98,8 @@ public MonitorServiceImpl(EventPublisher publisher) { public MonitorServiceImpl(long cleanupIntervalNanos, EventPublisher publisher) { this.publisher = publisher; this.publisher.subscribe(this, new HashSet<>(Collections.singletonList(DataAccessEvent.class))); - initCleanupThread(cleanupIntervalNanos); - } - - protected void initCleanupThread(long cleanupIntervalNanos) { - if (isInitialized.get()) { - return; - } - - initLock.lock(); - try { - if (isInitialized.get()) { - return; - } - - cleanupExecutor.scheduleAtFixedRate( - this::checkMonitors, cleanupIntervalNanos, cleanupIntervalNanos, TimeUnit.NANOSECONDS); - cleanupExecutor.shutdown(); - isInitialized.set(true); - } finally { - initLock.unlock(); - } + cleanupExecutor.scheduleAtFixedRate( + this::checkMonitors, cleanupIntervalNanos, cleanupIntervalNanos, TimeUnit.NANOSECONDS); } protected void checkMonitors() { @@ -141,6 +118,8 @@ protected void checkMonitors() { MonitorSettings monitorSettings = container.getSettings(); removedItem = cache.removeIf(key, mi -> mi.getMonitor().getState() == MonitorState.ERROR); if (removedItem != null) { + LOGGER.finest( + Messages.get("MonitorServiceImpl.removedErrorMonitor", new Object[] {removedItem.getMonitor()})); handleMonitorError(container, key, removedItem); continue; } @@ -195,7 +174,8 @@ public void registerMonitorTypeIfAbsent( monitorCaches.computeIfAbsent( monitorClass, mc -> new CacheContainer( - new MonitorSettings(expirationTimeoutNanos, heartbeatTimeoutNanos, errorResponses), producedDataClass)); + new MonitorSettings(expirationTimeoutNanos, heartbeatTimeoutNanos, errorResponses), + producedDataClass)); } @Override @@ -276,27 +256,6 @@ public T runIfAbsent( return null; } - @Override - public void reportMonitorError(Monitor monitor, Exception exception) { - CacheContainer cacheContainer = monitorCaches.get(monitor.getClass()); - if (cacheContainer == null) { - LOGGER.fine( - Messages.get("MonitorServiceImpl.monitorErrorForUnregisteredType", new Object[] {monitor, exception})); - return; - } - - ExternallyManagedCache cache = cacheContainer.getCache(); - for (Map.Entry entry : cache.getEntries().entrySet()) { - MonitorItem monitorItem = cache.removeIf(entry.getKey(), mi -> mi.getMonitor() == monitor); - if (monitorItem != null) { - handleMonitorError(cacheContainer, entry.getKey(), monitorItem); - return; - } - } - - LOGGER.finest(Messages.get("MonitorServiceImpl.monitorErrorForMissingMonitor", new Object[] {monitor, exception})); - } - @Override public T remove(Class monitorClass, Object key) { CacheContainer cacheContainer = monitorCaches.get(monitorClass); @@ -351,6 +310,12 @@ public void stopAndRemoveAll() { } } + @Override + public void releaseResources() { + cleanupExecutor.shutdownNow(); + stopAndRemoveAll(); + } + @Override public void processEvent(Event event) { if (!(event instanceof DataAccessEvent)) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java index b97f7871e..35770f691 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageService.java @@ -93,7 +93,7 @@ void registerItemClassIfAbsent( // TODO: this is only called by the suggestedClusterId logic in RdsHostListProvider, which will be removed. This // method should potentially be removed at that point as well. - @Nullable Map getEntries(Class itemClass); + @Nullable Map getEntries(Class itemClass); int size(Class itemClass); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 737b922e4..29d784dc9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -81,7 +81,6 @@ protected void initCleanupThread(long cleanupIntervalNanos) { cleanupExecutor.scheduleAtFixedRate( this::removeExpiredItems, cleanupIntervalNanos, cleanupIntervalNanos, TimeUnit.NANOSECONDS); - cleanupExecutor.shutdown(); isInitialized.set(true); } finally { initLock.unlock(); @@ -112,6 +111,7 @@ public void registerItemClassIfAbsent( } @Override + @SuppressWarnings("unchecked") public void set(Object key, V value) { ExpirationCache cache = caches.get(value.getClass()); if (cache == null) { @@ -125,7 +125,6 @@ public void set(Object key, V value) { } try { - // TODO: is there a way to get rid of this unchecked cast warning? Is the try-catch necessary? ExpirationCache typedCache = (ExpirationCache) cache; typedCache.put(key, value); } catch (ClassCastException e) { @@ -193,13 +192,13 @@ public void clearAll() { } @Override - public @Nullable Map getEntries(Class itemClass) { + public @Nullable Map getEntries(Class itemClass) { final ExpirationCache cache = caches.get(itemClass); if (cache == null) { return null; } - // TODO: fix this cast to be type safe and/or remove this method after removing the suggestedClusterId logic + // TODO: remove this method after removing the suggestedClusterId logic return (Map) cache.getEntries(); } diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 758eccee5..e647da475 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -16,6 +16,7 @@ AbstractMonitor.interruptedWhileTerminating=Interrupted while awaiting termination of monitor ''{0}''. The monitor will be forcefully shut down. AbstractMonitor.monitorTerminationTimeout=Timed out after waiting {0} seconds for monitor ''{1}'' to terminate gracefully. The monitor will be forcefully shut down. +AbstractMonitor.startingMonitor=Starting monitor: ''{0}''. AbstractMonitor.stoppingMonitor=Stopping monitor: ''{0}''. AbstractMonitor.unexpectedError=A monitor encountered an unexpected exception. Monitor: ''{0}''. Exception: ''{1}''. @@ -235,7 +236,7 @@ IamAuthConnectionPlugin.unhandledException=Unhandled exception: ''{0}'' IamAuthConnectionPlugin.connectException=Error occurred while opening a connection: ''{0}'' IamAuthConnectionPlugin.unableToDetermineRegion=Unable to determine connection region. If you are using a non-standard RDS URL, please set the ''{0}'' property. -LimitedPluginService.unexpectedMethodCall=''LimitedPluginService#{0}()'' was unexpectedly called. Users of this class are not expected to call this method, so the method does not provide any functionality. +PartialPluginService.unexpectedMethodCall=''LimitedPluginService#{0}()'' was unexpectedly called. Users of this class are not expected to call this method, so the method does not provide any functionality. # Limitless Connection Plugin LimitlessConnectionPlugin.failedToConnectToHost=Failed to connect to host {0}. @@ -301,9 +302,8 @@ MonitorImpl.stopMonitoringThread=Stop monitoring thread for {0}. MonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should not be empty. # monitoring.MonitorServiceImpl +MonitorServiceImpl.checkingMonitors=Checking monitors for errors... MonitorServiceImpl.monitorClassMismatch=The monitor stored at ''{0}'' did not have the expected type. The expected type was ''{1}'', but the monitor ''{2}'' had a type of ''{3}''. -MonitorServiceImpl.monitorErrorForMissingMonitor=Monitor ''{0}'' reported an error to the monitor service, but the monitor service could not find the monitor under its registered monitors. Reported error: ''{1}''. -MonitorServiceImpl.monitorErrorForUnregisteredType=Monitor ''{0}'' reported an error to the monitor service, but the monitor type has not been registered. Reported error: ''{1}''. MonitorServiceImpl.monitorStuck=Monitor ''{0}'' has not been updated within the inactive timeout of {1} milliseconds. MonitorServiceImpl.monitorTypeNotRegistered=The given monitor class ''{0}'' is not registered. Please register the monitor class before running monitors of that class with the monitor service. MonitorServiceImpl.removedExpiredMonitor=Removed expired monitor: ''{0}''. diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java new file mode 100644 index 000000000..1abcdccdf --- /dev/null +++ b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java @@ -0,0 +1,131 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.monitoring; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.fail; + +import java.sql.SQLException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Properties; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import software.amazon.jdbc.dialect.Dialect; +import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.events.EventPublisher; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.telemetry.TelemetryFactory; + +class MonitorServiceImplTest { + @Mock StorageService storageService; + @Mock TelemetryFactory telemetryFactory; + @Mock TargetDriverDialect targetDriverDialect; + @Mock Dialect dbDialect; + @Mock EventPublisher publisher; + final static long CLEANUP_INTERVAL_MS = 5000; + MonitorServiceImpl monitorService; + private AutoCloseable closeable; + + @BeforeEach + void setUp() { + closeable = MockitoAnnotations.openMocks(this); + monitorService = new MonitorServiceImpl(TimeUnit.MILLISECONDS.toNanos(CLEANUP_INTERVAL_MS), publisher); + monitorService.stopAndRemoveAll(); + } + + @AfterEach + void tearDown() throws Exception { + closeable.close(); + monitorService.stopAndRemoveAll(); + } + + @Test + public void testMonitorRecreation() throws SQLException, InterruptedException { + monitorService.registerMonitorTypeIfAbsent( + ExceptionThrowingMonitor.class, + TimeUnit.MINUTES.toNanos(1), + TimeUnit.MINUTES.toNanos(1), + new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)), + null + ); + String key = "testMonitor"; + Monitor monitor = monitorService.runIfAbsent( + ExceptionThrowingMonitor.class, + "testMonitor", + storageService, + telemetryFactory, + "jdbc:postgresql://somehost/somedb", + "someProtocol", + targetDriverDialect, + dbDialect, + new Properties(), + (connectionService, pluginService) -> new ExceptionThrowingMonitor( + monitorService, 30, 2500) + ); + + MonitorServiceImpl.MonitorItem monitorItem = + MonitorServiceImpl.monitorCaches.get(ExceptionThrowingMonitor.class).getCache().get(key); + assertNotNull(monitorItem); + assertEquals(monitor, monitorItem.getMonitor()); + + TimeUnit.MILLISECONDS.sleep(CLEANUP_INTERVAL_MS + 7500); + assertEquals(MonitorState.STOPPED, monitor.getState()); + + MonitorServiceImpl.MonitorItem newMonitorItem = + MonitorServiceImpl.monitorCaches.get(ExceptionThrowingMonitor.class).getCache().get(key); + assertNotNull(newMonitorItem); + assertNotEquals(monitor, newMonitorItem.getMonitor()); + } + + static class MonitorTestException extends RuntimeException { + + } + + static class ExceptionThrowingMonitor extends AbstractMonitor { + final long exceptionDelayMs; + + protected ExceptionThrowingMonitor( + MonitorService monitorService, + long terminationTimeoutSec, + long exceptionDelayMs) { + super(monitorService, terminationTimeoutSec); + this.exceptionDelayMs = exceptionDelayMs; + } + + @Override + public void monitor() { + try { + TimeUnit.MILLISECONDS.sleep(this.exceptionDelayMs); + throw new MonitorTestException(); + } catch (InterruptedException e) { + fail("Unexpected InterruptedException in test monitor"); + } + } + + @Override + public void close() { + // do nothing. + } + } +} From 241aeb450391f3bf4f5a11d9734dc51a03fea1e4 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 13 May 2025 18:23:32 -0700 Subject: [PATCH 102/149] Test passing individually --- .../util/monitoring/MonitorServiceImpl.java | 19 +++++++++-------- ..._advanced_jdbc_wrapper_messages.properties | 1 + .../monitoring/MonitorServiceImplTest.java | 21 ++++++++++++------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 692c78232..127766556 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -56,15 +56,6 @@ public class MonitorServiceImpl implements MonitorService, EventSubscriber { private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(1); protected static final Map, Supplier> defaultSuppliers; - protected final Map, CacheContainer> monitorCaches = new ConcurrentHashMap<>(); - protected final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { - final Thread thread = new Thread(r); - thread.setDaemon(true); - if (!StringUtils.isNullOrEmpty(thread.getName())) { - thread.setName(thread.getName() + "-msi"); - } - return thread; - })); static { Map, Supplier> suppliers = new HashMap<>(); @@ -82,6 +73,16 @@ public class MonitorServiceImpl implements MonitorService, EventSubscriber { } protected final EventPublisher publisher; + protected final Map, CacheContainer> monitorCaches = new ConcurrentHashMap<>(); + protected final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { + final Thread thread = new Thread(r); + thread.setDaemon(true); + if (!StringUtils.isNullOrEmpty(thread.getName())) { + thread.setName(thread.getName() + "-msi"); + } + return thread; + })); + public MonitorServiceImpl(EventPublisher publisher) { this(DEFAULT_CLEANUP_INTERVAL_NANOS, publisher); diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index e647da475..057c17fb8 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -307,6 +307,7 @@ MonitorServiceImpl.monitorClassMismatch=The monitor stored at ''{0}'' did not ha MonitorServiceImpl.monitorStuck=Monitor ''{0}'' has not been updated within the inactive timeout of {1} milliseconds. MonitorServiceImpl.monitorTypeNotRegistered=The given monitor class ''{0}'' is not registered. Please register the monitor class before running monitors of that class with the monitor service. MonitorServiceImpl.removedExpiredMonitor=Removed expired monitor: ''{0}''. +MonitorServiceImpl.removedErrorMonitor=Removed monitor in error state: ''{0}''. MonitorServiceImpl.recreatingMonitor=Recreating monitor: ''{0}''. MonitorServiceImpl.stopAndRemoveMissingMonitorType=The monitor service received a request to stop a monitor with type ''{0}'' and key ''{1}'', but the monitor service does not have any monitors registered under the given type. Please ensure monitors are registered under the correct type. MonitorServiceImpl.stopAndRemoveMonitorsMissingType=The monitor service received a request to stop all monitors with type ''{0}'', but the monitor service does not have any monitors registered under the given type. Please ensure monitors are registered under the correct type. diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java index 1abcdccdf..b90411541 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java @@ -26,6 +26,7 @@ import java.util.HashSet; import java.util.Properties; import java.util.concurrent.TimeUnit; +import java.util.logging.Logger; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -38,12 +39,14 @@ import software.amazon.jdbc.util.telemetry.TelemetryFactory; class MonitorServiceImplTest { + private static final Logger LOGGER = Logger.getLogger(MonitorServiceImplTest.class.getName()); + @Mock StorageService storageService; @Mock TelemetryFactory telemetryFactory; @Mock TargetDriverDialect targetDriverDialect; @Mock Dialect dbDialect; @Mock EventPublisher publisher; - final static long CLEANUP_INTERVAL_MS = 5000; + final static long CLEANUP_INTERVAL_MS = 1000; MonitorServiceImpl monitorService; private AutoCloseable closeable; @@ -51,15 +54,15 @@ class MonitorServiceImplTest { void setUp() { closeable = MockitoAnnotations.openMocks(this); monitorService = new MonitorServiceImpl(TimeUnit.MILLISECONDS.toNanos(CLEANUP_INTERVAL_MS), publisher); - monitorService.stopAndRemoveAll(); } @AfterEach void tearDown() throws Exception { closeable.close(); - monitorService.stopAndRemoveAll(); + monitorService.releaseResources(); } + // @RepeatedTest(100) @Test public void testMonitorRecreation() throws SQLException, InterruptedException { monitorService.registerMonitorTypeIfAbsent( @@ -81,21 +84,25 @@ public void testMonitorRecreation() throws SQLException, InterruptedException { dbDialect, new Properties(), (connectionService, pluginService) -> new ExceptionThrowingMonitor( - monitorService, 30, 2500) + // We want to throw the test exception shortly before cleanup + monitorService, 30, CLEANUP_INTERVAL_MS - 300) ); MonitorServiceImpl.MonitorItem monitorItem = - MonitorServiceImpl.monitorCaches.get(ExceptionThrowingMonitor.class).getCache().get(key); + monitorService.monitorCaches.get(ExceptionThrowingMonitor.class).getCache().get(key); assertNotNull(monitorItem); assertEquals(monitor, monitorItem.getMonitor()); - TimeUnit.MILLISECONDS.sleep(CLEANUP_INTERVAL_MS + 7500); + // Wait for monitor service cleanup thread to execute + TimeUnit.MILLISECONDS.sleep(CLEANUP_INTERVAL_MS + 200); + LOGGER.finest("Done sleeping, testing monitor state..."); assertEquals(MonitorState.STOPPED, monitor.getState()); MonitorServiceImpl.MonitorItem newMonitorItem = - MonitorServiceImpl.monitorCaches.get(ExceptionThrowingMonitor.class).getCache().get(key); + monitorService.monitorCaches.get(ExceptionThrowingMonitor.class).getCache().get(key); assertNotNull(newMonitorItem); assertNotEquals(monitor, newMonitorItem.getMonitor()); + assertEquals(MonitorState.RUNNING, newMonitorItem.getMonitor().getState()); } static class MonitorTestException extends RuntimeException { From 4d3ebebf2602e13b196dc570d3fb575f6b8977cc Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 15 May 2025 08:29:45 -0700 Subject: [PATCH 103/149] Add unit tests for refactoring work --- .../util/events/BatchingEventPublisher.java | 24 +- .../util/monitoring/MonitorServiceImpl.java | 4 + .../jdbc/util/storage/ExpirationCache.java | 1 + .../events/BatchingEventPublisherTest.java | 78 ++++++ .../monitoring/MonitorServiceImplTest.java | 240 +++++++++++++++--- .../util/storage/ExpirationCacheTest.java | 115 +++++++++ 6 files changed, 419 insertions(+), 43 deletions(-) create mode 100644 wrapper/src/test/java/software/amazon/jdbc/util/events/BatchingEventPublisherTest.java create mode 100644 wrapper/src/test/java/software/amazon/jdbc/util/storage/ExpirationCacheTest.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java index 7981eca01..011aa432f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java @@ -17,6 +17,7 @@ package software.amazon.jdbc.util.events; import java.util.Collections; +import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.WeakHashMap; @@ -37,7 +38,7 @@ public class BatchingEventPublisher implements EventPublisher { // ConcurrentHashMap.newKeySet() is the recommended way to get a concurrent set. A set is used to prevent duplicate // event messages from being sent in the same message batch. protected final Set eventMessages = ConcurrentHashMap.newKeySet(); - protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { + protected static final ScheduledExecutorService publishingExecutor = Executors.newSingleThreadScheduledExecutor((r -> { final Thread thread = new Thread(r); thread.setDaemon(true); if (!StringUtils.isNullOrEmpty(thread.getName())) { @@ -56,13 +57,25 @@ public BatchingEventPublisher() { * @param messageIntervalNanos the rate at which messages batches should be sent, in nanoseconds. */ public BatchingEventPublisher(long messageIntervalNanos) { - cleanupExecutor.scheduleAtFixedRate( + initPublishingThread(messageIntervalNanos); + } + + protected void initPublishingThread(long messageIntervalNanos) { + publishingExecutor.scheduleAtFixedRate( this::sendMessages, messageIntervalNanos, messageIntervalNanos, TimeUnit.NANOSECONDS); } - private void sendMessages() { - for (Event event : eventMessages) { - for (EventSubscriber subscriber : subscribersMap.get(event.getClass())) { + protected void sendMessages() { + Iterator iterator = eventMessages.iterator(); + while (iterator.hasNext()) { + Event event = iterator.next(); + iterator.remove(); + Set subscribers = subscribersMap.get(event.getClass()); + if (subscribers == null) { + continue; + } + + for (EventSubscriber subscriber : subscribers) { subscriber.processEvent(event); } } @@ -72,7 +85,6 @@ private void sendMessages() { public void subscribe(EventSubscriber subscriber, Set> eventClasses) { for (Class eventClass : eventClasses) { // The subscriber collection is a weakly referenced set so that we avoid garbage collection issues. - // TODO: do subscribers need to implement equals/hashcode? subscribersMap.computeIfAbsent( eventClass, (k) -> Collections.newSetFromMap(new WeakHashMap<>())).add(subscriber); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 127766556..4a4eeb2c2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -99,6 +99,10 @@ public MonitorServiceImpl(EventPublisher publisher) { public MonitorServiceImpl(long cleanupIntervalNanos, EventPublisher publisher) { this.publisher = publisher; this.publisher.subscribe(this, new HashSet<>(Collections.singletonList(DataAccessEvent.class))); + initCleanupThread(cleanupIntervalNanos); + } + + protected void initCleanupThread(long cleanupIntervalNanos) { cleanupExecutor.scheduleAtFixedRate( this::checkMonitors, cleanupIntervalNanos, cleanupIntervalNanos, TimeUnit.NANOSECONDS); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java index 2331bd2d8..f9c2a7ead 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/ExpirationCache.java @@ -134,6 +134,7 @@ public ExpirationCache( } // The existing value is non-expired or renewable. Keep the existing value. + if (this.isRenewableExpiration) { valueItem.extendExpiration(this.timeToLiveNanos); } diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/events/BatchingEventPublisherTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/events/BatchingEventPublisherTest.java new file mode 100644 index 000000000..a68bfe32a --- /dev/null +++ b/wrapper/src/test/java/software/amazon/jdbc/util/events/BatchingEventPublisherTest.java @@ -0,0 +1,78 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.events; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.sql.SQLException; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import software.amazon.jdbc.plugin.customendpoint.CustomEndpointInfo; + +class BatchingEventPublisherTest { + private AutoCloseable closeable; + @Mock private EventSubscriber subscriber; + + @BeforeEach + void setUp() throws SQLException { + closeable = MockitoAnnotations.openMocks(this); + } + + @AfterEach + void tearDown() throws Exception { + closeable.close(); + } + + @Test + public void testPublication() { + BatchingEventPublisher publisher = new BatchingEventPublisher() { + @Override + protected void initPublishingThread(long messageIntervalNanos) { + // Do nothing + } + }; + + Set> eventSubscriptions = new HashSet<>(Collections.singletonList(DataAccessEvent.class)); + publisher.subscribe(subscriber, eventSubscriptions); + publisher.subscribe(subscriber, eventSubscriptions); + assertEquals(1, publisher.subscribersMap.size()); + + DataAccessEvent event = new DataAccessEvent(CustomEndpointInfo.class, "key"); + publisher.publish(event); + publisher.publish(event); + publisher.sendMessages(); + assertTrue(publisher.eventMessages.isEmpty()); + + verify(subscriber, times(1)).processEvent(eq(event)); + + publisher.unsubscribe(subscriber, eventSubscriptions); + publisher.publish(event); + publisher.sendMessages(); + assertTrue(publisher.eventMessages.isEmpty()); + verify(subscriber, times(1)).processEvent(eq(event)); + } +} diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java index b90411541..598386b2a 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java @@ -19,7 +19,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.sql.SQLException; import java.util.Collections; @@ -33,6 +34,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import software.amazon.jdbc.dialect.Dialect; +import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; @@ -46,14 +48,18 @@ class MonitorServiceImplTest { @Mock TargetDriverDialect targetDriverDialect; @Mock Dialect dbDialect; @Mock EventPublisher publisher; - final static long CLEANUP_INTERVAL_MS = 1000; MonitorServiceImpl monitorService; private AutoCloseable closeable; @BeforeEach void setUp() { closeable = MockitoAnnotations.openMocks(this); - monitorService = new MonitorServiceImpl(TimeUnit.MILLISECONDS.toNanos(CLEANUP_INTERVAL_MS), publisher); + monitorService = new MonitorServiceImpl(publisher) { + @Override + protected void initCleanupThread(long cleanupIntervalNanos) { + // Do nothing + } + }; } @AfterEach @@ -62,20 +68,107 @@ void tearDown() throws Exception { monitorService.releaseResources(); } - // @RepeatedTest(100) @Test - public void testMonitorRecreation() throws SQLException, InterruptedException { + public void testMonitorError_monitorReCreated() throws SQLException, InterruptedException { monitorService.registerMonitorTypeIfAbsent( - ExceptionThrowingMonitor.class, + NoOpMonitor.class, TimeUnit.MINUTES.toNanos(1), TimeUnit.MINUTES.toNanos(1), new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)), null ); String key = "testMonitor"; - Monitor monitor = monitorService.runIfAbsent( - ExceptionThrowingMonitor.class, - "testMonitor", + NoOpMonitor monitor = monitorService.runIfAbsent( + NoOpMonitor.class, + key, + storageService, + telemetryFactory, + "jdbc:postgresql://somehost/somedb", + "someProtocol", + targetDriverDialect, + dbDialect, + new Properties(), + (connectionService, pluginService) -> new NoOpMonitor(monitorService, 30) + ); + + Monitor storedMonitor = monitorService.get(NoOpMonitor.class, key); + assertNotNull(storedMonitor); + assertEquals(monitor, storedMonitor); + // need to wait to give time for the monitor executor to start the monitor thread. + TimeUnit.MILLISECONDS.sleep(250); + assertEquals(MonitorState.RUNNING, monitor.getState()); + + monitor.state = MonitorState.ERROR; + monitorService.checkMonitors(); + + assertEquals(MonitorState.STOPPED, monitor.getState()); + + Monitor newMonitor = monitorService.get(NoOpMonitor.class, key); + assertNotNull(newMonitor); + assertNotEquals(monitor, newMonitor); + // need to wait to give time for the monitor executor to start the monitor thread. + TimeUnit.MILLISECONDS.sleep(250); + assertEquals(MonitorState.RUNNING, newMonitor.getState()); + } + + @Test + public void testMonitorStuck_monitorReCreated() throws SQLException, InterruptedException { + monitorService.registerMonitorTypeIfAbsent( + NoOpMonitor.class, + TimeUnit.MINUTES.toNanos(1), + 1, // heartbeat times out immediately + new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)), + null + ); + String key = "testMonitor"; + NoOpMonitor monitor = monitorService.runIfAbsent( + NoOpMonitor.class, + key, + storageService, + telemetryFactory, + "jdbc:postgresql://somehost/somedb", + "someProtocol", + targetDriverDialect, + dbDialect, + new Properties(), + (connectionService, pluginService) -> new NoOpMonitor(monitorService, 30) + ); + + Monitor storedMonitor = monitorService.get(NoOpMonitor.class, key); + assertNotNull(storedMonitor); + assertEquals(monitor, storedMonitor); + // need to wait to give time for the monitor executor to start the monitor thread. + TimeUnit.MILLISECONDS.sleep(250); + assertEquals(MonitorState.RUNNING, monitor.getState()); + + // checkMonitors() should detect the heartbeat/inactivity timeout, stop the monitor, and re-create a new one. + monitorService.checkMonitors(); + + assertEquals(MonitorState.STOPPED, monitor.getState()); + + Monitor newMonitor = monitorService.get(NoOpMonitor.class, key); + assertNotNull(newMonitor); + assertNotEquals(monitor, newMonitor); + // need to wait to give time for the monitor executor to start the monitor thread. + TimeUnit.MILLISECONDS.sleep(250); + assertEquals(MonitorState.RUNNING, newMonitor.getState()); + } + + @Test + public void testMonitorExpired() throws SQLException, InterruptedException { + monitorService.registerMonitorTypeIfAbsent( + NoOpMonitor.class, + TimeUnit.MILLISECONDS.toNanos(200), // monitor expires after 200ms + TimeUnit.MINUTES.toNanos(1), + // even though we pass a re-create policy, we should not re-create it if the monitor is expired since this + // indicates it is not being used. + new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)), + null + ); + String key = "testMonitor"; + NoOpMonitor monitor = monitorService.runIfAbsent( + NoOpMonitor.class, + key, storageService, telemetryFactory, "jdbc:postgresql://somehost/somedb", @@ -83,51 +176,124 @@ public void testMonitorRecreation() throws SQLException, InterruptedException { targetDriverDialect, dbDialect, new Properties(), - (connectionService, pluginService) -> new ExceptionThrowingMonitor( - // We want to throw the test exception shortly before cleanup - monitorService, 30, CLEANUP_INTERVAL_MS - 300) + (connectionService, pluginService) -> new NoOpMonitor(monitorService, 30) ); - MonitorServiceImpl.MonitorItem monitorItem = - monitorService.monitorCaches.get(ExceptionThrowingMonitor.class).getCache().get(key); - assertNotNull(monitorItem); - assertEquals(monitor, monitorItem.getMonitor()); + Monitor storedMonitor = monitorService.get(NoOpMonitor.class, key); + assertNotNull(storedMonitor); + assertEquals(monitor, storedMonitor); + // need to wait to give time for the monitor executor to start the monitor thread. + TimeUnit.MILLISECONDS.sleep(250); + assertEquals(MonitorState.RUNNING, monitor.getState()); + + // checkMonitors() should detect the expiration timeout and stop/remove the monitor. + monitorService.checkMonitors(); - // Wait for monitor service cleanup thread to execute - TimeUnit.MILLISECONDS.sleep(CLEANUP_INTERVAL_MS + 200); - LOGGER.finest("Done sleeping, testing monitor state..."); assertEquals(MonitorState.STOPPED, monitor.getState()); - MonitorServiceImpl.MonitorItem newMonitorItem = - monitorService.monitorCaches.get(ExceptionThrowingMonitor.class).getCache().get(key); - assertNotNull(newMonitorItem); - assertNotEquals(monitor, newMonitorItem.getMonitor()); - assertEquals(MonitorState.RUNNING, newMonitorItem.getMonitor().getState()); + Monitor newMonitor = monitorService.get(NoOpMonitor.class, key); + // monitor should have been removed when checkMonitors() was called. + assertNull(newMonitor); + } + + @Test + public void testMonitorMismatch() { + assertThrows(IllegalStateException.class, () -> { + monitorService.runIfAbsent( + CustomEndpointMonitorImpl.class, + "testMonitor", + storageService, + telemetryFactory, + "jdbc:postgresql://somehost/somedb", + "someProtocol", + targetDriverDialect, + dbDialect, + new Properties(), + // indicated monitor class is CustomEndpointMonitorImpl, but actual monitor is NoOpMonitor. The monitor + // service should detect this and throw an exception. + (connectionService, pluginService) -> new NoOpMonitor(monitorService, 30) + ); + }); } - static class MonitorTestException extends RuntimeException { + @Test + public void testRemove() throws SQLException, InterruptedException { + monitorService.registerMonitorTypeIfAbsent( + NoOpMonitor.class, + TimeUnit.MINUTES.toNanos(1), + TimeUnit.MINUTES.toNanos(1), + // even though we pass a re-create policy, we should not re-create it if the monitor is expired since this + // indicates it is not being used. + new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)), + null + ); + + String key = "testMonitor"; + NoOpMonitor monitor = monitorService.runIfAbsent( + NoOpMonitor.class, + key, + storageService, + telemetryFactory, + "jdbc:postgresql://somehost/somedb", + "someProtocol", + targetDriverDialect, + dbDialect, + new Properties(), + (connectionService, pluginService) -> new NoOpMonitor(monitorService, 30) + ); + assertNotNull(monitor); + // need to wait to give time for the monitor executor to start the monitor thread. + TimeUnit.MILLISECONDS.sleep(250); + Monitor removedMonitor = monitorService.remove(NoOpMonitor.class, key); + assertEquals(monitor, removedMonitor); + assertEquals(MonitorState.RUNNING, monitor.getState()); } - static class ExceptionThrowingMonitor extends AbstractMonitor { - final long exceptionDelayMs; + @Test + public void testStopAndRemove() throws SQLException, InterruptedException { + monitorService.registerMonitorTypeIfAbsent( + NoOpMonitor.class, + TimeUnit.MINUTES.toNanos(1), + TimeUnit.MINUTES.toNanos(1), + // even though we pass a re-create policy, we should not re-create it if the monitor is expired since this + // indicates it is not being used. + new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)), + null + ); + + String key = "testMonitor"; + NoOpMonitor monitor = monitorService.runIfAbsent( + NoOpMonitor.class, + key, + storageService, + telemetryFactory, + "jdbc:postgresql://somehost/somedb", + "someProtocol", + targetDriverDialect, + dbDialect, + new Properties(), + (connectionService, pluginService) -> new NoOpMonitor(monitorService, 30) + ); + assertNotNull(monitor); - protected ExceptionThrowingMonitor( + // need to wait to give time for the monitor executor to start the monitor thread. + TimeUnit.MILLISECONDS.sleep(250); + monitorService.stopAndRemove(NoOpMonitor.class, key); + assertNull(monitorService.get(NoOpMonitor.class, key)); + assertEquals(MonitorState.STOPPED, monitor.getState()); + } + + static class NoOpMonitor extends AbstractMonitor { + protected NoOpMonitor( MonitorService monitorService, - long terminationTimeoutSec, - long exceptionDelayMs) { + long terminationTimeoutSec) { super(monitorService, terminationTimeoutSec); - this.exceptionDelayMs = exceptionDelayMs; } @Override public void monitor() { - try { - TimeUnit.MILLISECONDS.sleep(this.exceptionDelayMs); - throw new MonitorTestException(); - } catch (InterruptedException e) { - fail("Unexpected InterruptedException in test monitor"); - } + // do nothing. } @Override diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/storage/ExpirationCacheTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/storage/ExpirationCacheTest.java new file mode 100644 index 000000000..081b79b55 --- /dev/null +++ b/wrapper/src/test/java/software/amazon/jdbc/util/storage/ExpirationCacheTest.java @@ -0,0 +1,115 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.storage; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +class ExpirationCacheTest { + + @Test + public void testComputeIfAbsent() throws InterruptedException { + ExpirationCache cache = new ExpirationCache<>( + false, + TimeUnit.MILLISECONDS.toNanos(100), + (item) -> true, + DummyItem::close); + String key = "key"; + DummyItem itemSpy = Mockito.spy(new DummyItem()); + DummyItem newDummyItem = new DummyItem(); + assertEquals(itemSpy, cache.computeIfAbsent(key, k -> itemSpy)); + // item is not absent, so the new value should not be stored. + assertEquals(itemSpy, cache.computeIfAbsent(key, k -> newDummyItem)); + + // wait for item to expire. + TimeUnit.MILLISECONDS.sleep(150); + assertEquals(newDummyItem, cache.computeIfAbsent(key, k -> newDummyItem)); + // wait briefly for cache to call the disposal method on the old item. + TimeUnit.MILLISECONDS.sleep(100); + verify(itemSpy, times(1)).close(); + } + + @Test + public void testRenewableExpiration() throws InterruptedException { + ExpirationCache cache = new ExpirationCache<>( + true, + TimeUnit.MILLISECONDS.toNanos(100), + (item) -> true, + DummyItem::close); + String key = "key"; + DummyItem item = new DummyItem(); + cache.put(key, item); + assertEquals(item, cache.get(key)); + + // wait for item to expire. + TimeUnit.MILLISECONDS.sleep(150); + + assertEquals(item, cache.get(key)); + } + + @Test + public void testNonRenewableExpiration() throws InterruptedException { + ExpirationCache cache = new ExpirationCache<>( + false, + TimeUnit.MILLISECONDS.toNanos(100), + (item) -> true, + DummyItem::close); + String key = "key"; + DummyItem itemSpy = Mockito.spy(new DummyItem()); + cache.put(key, itemSpy); + assertEquals(itemSpy, cache.get(key)); + + // wait for item to expire. + TimeUnit.MILLISECONDS.sleep(150); + + assertNull(cache.get(key)); + assertFalse(cache.exists(key)); + + cache.removeExpiredEntries(); + verify(itemSpy, times(1)).close(); + } + + @Test + public void testRemove() { + ExpirationCache cache = new ExpirationCache<>( + true, + TimeUnit.MILLISECONDS.toNanos(100), + (item) -> true, + DummyItem::close); + String key = "key"; + DummyItem itemSpy = Mockito.spy(new DummyItem()); + cache.put(key, itemSpy); + assertEquals(itemSpy, cache.get(key)); + + DummyItem removedItem = cache.remove(key); + assertEquals(itemSpy, removedItem); + verify(itemSpy, times(1)).close(); + } + + static class DummyItem { + protected void close() { + // do nothing. + } + } +} From 141208fb0ed5a589d32eb86e5e6c24621ee6c591 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 15 May 2025 08:39:33 -0700 Subject: [PATCH 104/149] Fix checkstyle --- .../java/software/amazon/jdbc/Driver.java | 2 +- .../amazon/jdbc/ds/AwsWrapperDataSource.java | 2 +- .../util/events/BatchingEventPublisher.java | 18 +++++----- .../monitoring/MonitorServiceImplTest.java | 35 ++++++++----------- 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index 6cef2f9f3..614ffa1b1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -65,8 +65,8 @@ import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.ServiceContainerImpl; import software.amazon.jdbc.util.StringUtils; -import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.events.BatchingEventPublisher; +import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.monitoring.MonitorServiceImpl; import software.amazon.jdbc.util.storage.StorageService; diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index 9e9be2c05..2bc7023aa 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -54,8 +54,8 @@ import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; -import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.events.BatchingEventPublisher; +import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.monitoring.MonitorServiceImpl; import software.amazon.jdbc.util.storage.StorageService; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java index 011aa432f..2051e1e5b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java @@ -38,14 +38,16 @@ public class BatchingEventPublisher implements EventPublisher { // ConcurrentHashMap.newKeySet() is the recommended way to get a concurrent set. A set is used to prevent duplicate // event messages from being sent in the same message batch. protected final Set eventMessages = ConcurrentHashMap.newKeySet(); - protected static final ScheduledExecutorService publishingExecutor = Executors.newSingleThreadScheduledExecutor((r -> { - final Thread thread = new Thread(r); - thread.setDaemon(true); - if (!StringUtils.isNullOrEmpty(thread.getName())) { - thread.setName(thread.getName() + "-bep"); - } - return thread; - })); + protected static final ScheduledExecutorService publishingExecutor = Executors.newSingleThreadScheduledExecutor( + (r -> { + final Thread thread = new Thread(r); + thread.setDaemon(true); + if (!StringUtils.isNullOrEmpty(thread.getName())) { + thread.setName(thread.getName() + "-bep"); + } + return thread; + }) + ); public BatchingEventPublisher() { this(DEFAULT_MESSAGE_INTERVAL_NANOS); diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java index 598386b2a..4fee050f5 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java @@ -27,7 +27,6 @@ import java.util.HashSet; import java.util.Properties; import java.util.concurrent.TimeUnit; -import java.util.logging.Logger; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -41,8 +40,6 @@ import software.amazon.jdbc.util.telemetry.TelemetryFactory; class MonitorServiceImplTest { - private static final Logger LOGGER = Logger.getLogger(MonitorServiceImplTest.class.getName()); - @Mock StorageService storageService; @Mock TelemetryFactory telemetryFactory; @Mock TargetDriverDialect targetDriverDialect; @@ -76,7 +73,7 @@ public void testMonitorError_monitorReCreated() throws SQLException, Interrupted TimeUnit.MINUTES.toNanos(1), new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)), null - ); + ); String key = "testMonitor"; NoOpMonitor monitor = monitorService.runIfAbsent( NoOpMonitor.class, @@ -198,22 +195,20 @@ public void testMonitorExpired() throws SQLException, InterruptedException { @Test public void testMonitorMismatch() { - assertThrows(IllegalStateException.class, () -> { - monitorService.runIfAbsent( - CustomEndpointMonitorImpl.class, - "testMonitor", - storageService, - telemetryFactory, - "jdbc:postgresql://somehost/somedb", - "someProtocol", - targetDriverDialect, - dbDialect, - new Properties(), - // indicated monitor class is CustomEndpointMonitorImpl, but actual monitor is NoOpMonitor. The monitor - // service should detect this and throw an exception. - (connectionService, pluginService) -> new NoOpMonitor(monitorService, 30) - ); - }); + assertThrows(IllegalStateException.class, () -> monitorService.runIfAbsent( + CustomEndpointMonitorImpl.class, + "testMonitor", + storageService, + telemetryFactory, + "jdbc:postgresql://somehost/somedb", + "someProtocol", + targetDriverDialect, + dbDialect, + new Properties(), + // indicated monitor class is CustomEndpointMonitorImpl, but actual monitor is NoOpMonitor. The monitor + // service should detect this and throw an exception. + (connectionService, pluginService) -> new NoOpMonitor(monitorService, 30) + )); } @Test From d6644aa65a3ef11e63f2a8dcbe17395e625eb573 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 15 May 2025 09:00:20 -0700 Subject: [PATCH 105/149] stopAndRemove monitors instead of shutting down cleanup thread --- wrapper/src/main/java/software/amazon/jdbc/Driver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index 614ffa1b1..6185fb895 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -420,7 +420,7 @@ public static void clearCaches() { } public static void releaseResources() { - monitorService.releaseResources(); + monitorService.stopAndRemoveAll(); software.amazon.jdbc.plugin.efm2.MonitorServiceImpl.closeAllMonitors(); MonitorThreadContainer.releaseInstance(); ConnectionProviderManager.releaseResources(); From 0616f2cbdcb1d4ed66d3c12cedea83c87d749480 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 15 May 2025 09:24:39 -0700 Subject: [PATCH 106/149] Fix markdown links --- CHANGELOG.md | 2 +- docs/GettingStarted.md | 2 +- docs/using-the-jdbc-driver/UsingTheJdbcDriver.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ab433b7a..846d67705 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -283,7 +283,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### :magic_wand: Added - Developer plugin to help test various scenarios including events like network outages and database cluster failover. This plugin is NOT intended to be used in production environments and is only for testing ([PR #531](https://github.com/awslabs/aws-advanced-jdbc-wrapper/pull/531)). - Documentation: - - Developer plugin. See [UsingTheJdbcDriver](https://github.com/awslabs/aws-advanced-jdbc-wrapper/blob/main/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md#list-of-available-plugins) and [UsingTheDeveloperPlugin](https://github.com/awslabs/aws-advanced-jdbc-wrapper/blob/main/docs/using-the-jdbc-driver/using-plugins/UsingTheDeveloperPlugin.md). + - Developer plugin. See [UsingTheJdbcDriver](https://github.com/aws/aws-advanced-jdbc-wrapper/blob/main/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md#list-of-available-plugins) and [UsingTheDeveloperPlugin](https://github.com/awslabs/aws-advanced-jdbc-wrapper/blob/main/docs/using-the-jdbc-driver/using-plugins/UsingTheDeveloperPlugin.md). - MySQL code samples ([PR #532](https://github.com/awslabs/aws-advanced-jdbc-wrapper/pull/532)). - Add a Table of Contents section for the sample codes on README.md. See [README.md](https://github.com/awslabs/aws-advanced-jdbc-wrapper/blob/main/README.md#examples). - Sample tutorial and code example for Vert.x. See the [tutorial](https://github.com/awslabs/aws-advanced-jdbc-wrapper/blob/main/examples/VertxExample/README.md) and [code example](https://github.com/awslabs/aws-advanced-jdbc-wrapper/blob/main/examples/VertxExample/src/main/java/com/example/starter/MainVerticle.java). diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 1395607b9..fe06fb92a 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -12,7 +12,7 @@ Before using the AWS Advanced JDBC Driver, you must install: If you are using the AWS JDBC Driver as part of a Gradle project, include the wrapper and underlying driver as dependencies. For example, to include the AWS JDBC Driver and the PostgreSQL JDBC Driver as dependencies in a Gradle project, update the ```build.gradle``` file as follows: -> **Note:** Depending on which features of the AWS JDBC Driver you use, you may have additional package requirements. Please refer to this [table](https://github.com/awslabs/aws-advanced-jdbc-wrapper/blob/main/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md#list-of-available-plugins) for more information. +> **Note:** Depending on which features of the AWS JDBC Driver you use, you may have additional package requirements. Please refer to this [table](https://github.com/aws/aws-advanced-jdbc-wrapper/blob/main/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md#list-of-available-plugins) for more information. ```gradle dependencies { diff --git a/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md b/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md index 0124c411f..f7da7a4c7 100644 --- a/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md +++ b/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md @@ -275,7 +275,7 @@ In the AWS JDBC Driver, plugins are set by specifying the plugin codes: ``` To see the list of available plugins and their associated plugin code, see -the [documentation](https://github.com/awslabs/aws-advanced-jdbc-wrapper/blob/main/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md#list-of-available-plugins). +the [documentation](https://github.com/aws/aws-advanced-jdbc-wrapper/blob/main/docs/using-the-jdbc-driver/UsingTheJdbcDriver.md#list-of-available-plugins). The AWS JDBC Driver also provides the [Read-Write Splitting plugin](https://github.com/awslabs/aws-advanced-jdbc-wrapper/blob/main/docs/using-the-jdbc-driver/using-plugins/UsingTheReadWriteSplittingPlugin.md#read-write-splitting-plugin), From 8f16be5d09caf9860e69685cf957b2634e8d66ec Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 16 May 2025 09:36:26 -0700 Subject: [PATCH 107/149] Add ServiceContainerPluginFactory --- .../ConnectionPluginManagerBenchmarks.java | 17 +- .../jdbc/ConnectionPluginChainBuilder.java | 12 +- .../amazon/jdbc/ConnectionPluginFactory.java | 4 + .../amazon/jdbc/ConnectionPluginManager.java | 16 +- .../amazon/jdbc/PartialPluginService.java | 5 - .../software/amazon/jdbc/PluginService.java | 3 - .../amazon/jdbc/PluginServiceImpl.java | 5 - .../jdbc/ServiceContainerPluginFactory.java | 40 + .../CustomEndpointPluginFactory.java | 14 +- ...HostMonitoringConnectionPluginFactory.java | 15 +- ...HostMonitoringConnectionPluginFactory.java | 15 +- .../FailoverConnectionPluginFactory.java | 16 +- .../LimitlessConnectionPluginFactory.java | 15 +- .../FastestResponseStrategyPluginFactory.java | 16 +- .../connection/ConnectionServiceImpl.java | 2 +- .../jdbc/wrapper/ConnectionWrapper.java | 6 +- ..._advanced_jdbc_wrapper_messages.properties | 2 + .../ConnectionPluginChainBuilderTests.java | 39 +- .../jdbc/ConnectionPluginManagerTests.java | 7 +- .../jdbc/plugin/efm/ConcurrencyTests.java | 966 ------------------ 20 files changed, 172 insertions(+), 1043 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java delete mode 100644 wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java index 31b20724b..3e1ea4a3e 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java @@ -64,15 +64,16 @@ import software.amazon.jdbc.benchmarks.testplugin.BenchmarkPluginFactory; import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; +import software.amazon.jdbc.profile.ConfigurationProfile; +import software.amazon.jdbc.profile.ConfigurationProfileBuilder; +import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; import software.amazon.jdbc.util.telemetry.GaugeCallable; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryGauge; -import software.amazon.jdbc.profile.ConfigurationProfile; -import software.amazon.jdbc.profile.ConfigurationProfileBuilder; -import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.wrapper.ConnectionWrapper; @State(Scope.Benchmark) @@ -93,6 +94,7 @@ public class ConnectionPluginManagerBenchmarks { @Mock ConnectionProvider mockConnectionProvider; @Mock ConnectionWrapper mockConnectionWrapper; + @Mock ServiceContainer mockServiceContainer; @Mock PluginService mockPluginService; @Mock PluginManagerService mockPluginManagerService; @Mock TelemetryFactory mockTelemetryFactory; @@ -137,6 +139,7 @@ public void setUpIteration() throws Exception { when(mockResultSet.getString(eq(FIELD_SESSION_ID))).thenReturn(WRITER_SESSION_ID); when(mockResultSet.getString(eq(FIELD_SERVER_ID))) .thenReturn("myInstance1.domain.com", "myInstance2.domain.com", "myInstance3.domain.com"); + when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); when(mockPluginService.getCurrentConnection()).thenReturn(mockConnection); when(mockPluginService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); @@ -162,11 +165,11 @@ public void setUpIteration() throws Exception { null, mockConnectionWrapper, telemetryFactory); - pluginManager.init(mockPluginService, propertiesWithPlugins, mockPluginManagerService, configurationProfile); + pluginManager.init(mockServiceContainer, propertiesWithPlugins, mockPluginManagerService, configurationProfile); pluginManagerWithNoPlugins = new ConnectionPluginManager(mockConnectionProvider, null, mockConnectionWrapper, telemetryFactory); - pluginManagerWithNoPlugins.init(mockPluginService, propertiesWithoutPlugins, mockPluginManagerService, null); + pluginManagerWithNoPlugins.init(mockServiceContainer, propertiesWithoutPlugins, mockPluginManagerService, null); } @TearDown(Level.Iteration) @@ -178,7 +181,7 @@ public void tearDownIteration() throws Exception { public ConnectionPluginManager initConnectionPluginManagerWithNoPlugins() throws SQLException { final ConnectionPluginManager manager = new ConnectionPluginManager(mockConnectionProvider, null, mockConnectionWrapper, mockTelemetryFactory); - manager.init(mockPluginService, propertiesWithoutPlugins, mockPluginManagerService, configurationProfile); + manager.init(mockServiceContainer, propertiesWithoutPlugins, mockPluginManagerService, configurationProfile); return manager; } @@ -186,7 +189,7 @@ public ConnectionPluginManager initConnectionPluginManagerWithNoPlugins() throws public ConnectionPluginManager initConnectionPluginManagerWithPlugins() throws SQLException { final ConnectionPluginManager manager = new ConnectionPluginManager(mockConnectionProvider, null, mockConnectionWrapper, mockTelemetryFactory); - manager.init(mockPluginService, propertiesWithPlugins, mockPluginManagerService, configurationProfile); + manager.init(mockServiceContainer, propertiesWithPlugins, mockPluginManagerService, configurationProfile); return manager; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java index 6b41835f8..dbb22e6d3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java @@ -48,6 +48,7 @@ import software.amazon.jdbc.plugin.strategy.fastestresponse.FastestResponseStrategyPluginFactory; import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; @@ -133,7 +134,7 @@ public PluginFactoryInfo(final Class factory, } public List getPlugins( - final PluginService pluginService, + final ServiceContainer serviceContainer, final ConnectionProvider defaultConnProvider, final ConnectionProvider effectiveConnProvider, final PluginManagerService pluginManagerService, @@ -188,7 +189,12 @@ public List getPlugins( plugins = new ArrayList<>(factories.length + 1); for (final ConnectionPluginFactory factory : factories) { - plugins.add(factory.getInstance(pluginService, props)); + if (factory instanceof ServiceContainerPluginFactory) { + ServiceContainerPluginFactory serviceContainerPluginFactory = (ServiceContainerPluginFactory) factory; + plugins.add(serviceContainerPluginFactory.getInstance(serviceContainer, props)); + } else { + plugins.add(factory.getInstance(serviceContainer.getPluginService(), props)); + } } } catch (final InstantiationException instEx) { @@ -200,7 +206,7 @@ public List getPlugins( // add default connection plugin to the tail final ConnectionPlugin defaultPlugin = new DefaultConnectionPlugin( - pluginService, + serviceContainer.getPluginService(), defaultConnProvider, effectiveConnProvider, pluginManagerService); diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginFactory.java index 5f186bdde..614547de4 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginFactory.java @@ -17,10 +17,14 @@ package software.amazon.jdbc; import java.util.Properties; +import software.amazon.jdbc.util.ServiceContainer; /** * Interface for connection plugin factories. This class implements ways to initialize a connection * plugin. + * + * @apiNote Consider using {@link ServiceContainerPluginFactory} for new implementations as it provides access to all + * services in the {@link ServiceContainer}. */ public interface ConnectionPluginFactory { diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java index 0bbc8db6a..47ac3cffd 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java @@ -29,6 +29,7 @@ import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; +import org.jetbrains.annotations.NotNull; import software.amazon.jdbc.cleanup.CanReleaseResources; import software.amazon.jdbc.plugin.AuroraConnectionTrackerPlugin; import software.amazon.jdbc.plugin.AuroraInitialConnectionStrategyPlugin; @@ -50,6 +51,7 @@ import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.util.AsynchronousMethodsHelper; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SqlMethodAnalyzer; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.WrapperUtils; @@ -109,6 +111,7 @@ public class ConnectionPluginManager implements CanReleaseResources, Wrapper { protected final @NonNull ConnectionProvider defaultConnProvider; protected final @Nullable ConnectionProvider effectiveConnProvider; protected final ConnectionWrapper connectionWrapper; + protected ServiceContainer serviceContainer; protected PluginService pluginService; protected TelemetryFactory telemetryFactory; @@ -179,26 +182,27 @@ public boolean isHeldByCurrentThread() { *

The {@link DefaultConnectionPlugin} will always be initialized and attached as the last * connection plugin in the chain. * - * @param pluginService a reference to a plugin service that plugin can use + * @param serviceContainer the service container for the services required by this class. * @param props the configuration of the connection * @param pluginManagerService a reference to a plugin manager service * @param configurationProfile a profile configuration defined by the user * @throws SQLException if errors occurred during the execution */ public void init( - final PluginService pluginService, + final ServiceContainer serviceContainer, final Properties props, final PluginManagerService pluginManagerService, @Nullable ConfigurationProfile configurationProfile) throws SQLException { this.props = props; - this.pluginService = pluginService; - this.telemetryFactory = pluginService.getTelemetryFactory(); + this.serviceContainer = serviceContainer; + this.pluginService = serviceContainer.getPluginService(); + this.telemetryFactory = serviceContainer.getTelemetryFactory(); ConnectionPluginChainBuilder pluginChainBuilder = new ConnectionPluginChainBuilder(); this.plugins = pluginChainBuilder.getPlugins( - this.pluginService, + this.serviceContainer, this.defaultConnProvider, this.effectiveConnProvider, pluginManagerService, @@ -641,10 +645,12 @@ public boolean isWrapperFor(Class iface) throws SQLException { return false; } + @NotNull public ConnectionProvider getDefaultConnProvider() { return this.defaultConnProvider; } + @Nullable public ConnectionProvider getEffectiveConnProvider() { return this.effectiveConnProvider; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java index 2fc72f273..efe128d06 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java @@ -193,11 +193,6 @@ public String getOriginalUrl() { return this.originalUrl; } - @Override - public ServiceContainer getServiceContainer() { - return this.serviceContainer; - } - @Override @Deprecated public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHosts) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java index 84ae9a0bb..98e4aede9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginService.java @@ -29,7 +29,6 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.telemetry.TelemetryFactory; /** @@ -83,8 +82,6 @@ EnumSet setCurrentConnection( String getOriginalUrl(); - ServiceContainer getServiceContainer(); - /** * Set the collection of hosts that should be allowed and/or blocked for connections. * diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index 32154e6c9..8fccf35fe 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -216,11 +216,6 @@ public String getOriginalUrl() { return this.originalUrl; } - @Override - public ServiceContainer getServiceContainer() { - return this.serviceContainer; - } - @Override @Deprecated public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHosts) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java new file mode 100644 index 000000000..2a7f50e6f --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java @@ -0,0 +1,40 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc; + +// TODO: fix docs + +import java.util.Properties; +import software.amazon.jdbc.util.ServiceContainer; + +/** + * A factory for plugins that utilizes a ServiceContainer. This interface extends {@link ConnectionPluginFactory} to + * provide additional flexibility in plugin instantiation while maintaining backward compatibility. + * + *

Implementations of this interface can access all services in the {@link ServiceContainer} when creating connection + * plugins, rather than being limited to just the {@link PluginService}

+ */ +public interface ServiceContainerPluginFactory extends ConnectionPluginFactory { + /** + * Get an instance of a {@link ConnectionPlugin}. + * + * @param serviceContainer the service container containing the services to be used by the {@link ConnectionPlugin}. + * @param props to be used by the {@link ConnectionPlugin}. + * @return an instance of a {@link ConnectionPlugin}. + */ + ConnectionPlugin getInstance(ServiceContainer serviceContainer, Properties props); +} diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java index bd1f40052..15fcc6b20 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java @@ -19,19 +19,27 @@ import java.util.Properties; import software.amazon.jdbc.ConnectionPlugin; -import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.ServiceContainerPluginFactory; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; -public class CustomEndpointPluginFactory implements ConnectionPluginFactory { +public class CustomEndpointPluginFactory implements ServiceContainerPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { + throw new UnsupportedOperationException( + Messages.get( + "ServiceContainerPluginFactory.serviceContainerRequired", new Object[] {"CustomEndpointPlugin"})); + } + + @Override + public ConnectionPlugin getInstance(final ServiceContainer serviceContainer, final Properties props) { try { Class.forName("software.amazon.awssdk.services.rds.RdsClient"); } catch (final ClassNotFoundException e) { throw new RuntimeException(Messages.get("CustomEndpointPluginFactory.awsSdkNotInClasspath")); } - return new CustomEndpointPlugin(pluginService.getServiceContainer(), props); + return new CustomEndpointPlugin(serviceContainer, props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginFactory.java index 64ef58025..b0f78391c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginFactory.java @@ -18,13 +18,22 @@ import java.util.Properties; import software.amazon.jdbc.ConnectionPlugin; -import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.ServiceContainerPluginFactory; +import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; /** Class initializing a {@link HostMonitoringConnectionPlugin}. */ -public class HostMonitoringConnectionPluginFactory implements ConnectionPluginFactory { +public class HostMonitoringConnectionPluginFactory implements ServiceContainerPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - return new HostMonitoringConnectionPlugin(pluginService.getServiceContainer(), props); + throw new UnsupportedOperationException( + Messages.get( + "ServiceContainerPluginFactory.serviceContainerRequired", new Object[] {"HostMonitoringConnectionPlugin"})); + } + + @Override + public ConnectionPlugin getInstance(final ServiceContainer serviceContainer, final Properties props) { + return new HostMonitoringConnectionPlugin(serviceContainer, props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPluginFactory.java index f58692ae9..bbbc95dc5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPluginFactory.java @@ -18,13 +18,22 @@ import java.util.Properties; import software.amazon.jdbc.ConnectionPlugin; -import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.ServiceContainerPluginFactory; +import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; /** Class initializing a {@link HostMonitoringConnectionPlugin}. */ -public class HostMonitoringConnectionPluginFactory implements ConnectionPluginFactory { +public class HostMonitoringConnectionPluginFactory implements ServiceContainerPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - return new HostMonitoringConnectionPlugin(pluginService.getServiceContainer(), props); + throw new UnsupportedOperationException( + Messages.get( + "ServiceContainerPluginFactory.serviceContainerRequired", new Object[] {"HostMonitoringConnectionPlugin"})); + } + + @Override + public ConnectionPlugin getInstance(final ServiceContainer serviceContainer, final Properties props) { + return new HostMonitoringConnectionPlugin(serviceContainer, props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginFactory.java index e82d951b6..bf95aece3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginFactory.java @@ -18,13 +18,21 @@ import java.util.Properties; import software.amazon.jdbc.ConnectionPlugin; -import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.ServiceContainerPluginFactory; +import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; -public class FailoverConnectionPluginFactory implements ConnectionPluginFactory { - +public class FailoverConnectionPluginFactory implements ServiceContainerPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - return new FailoverConnectionPlugin(pluginService.getServiceContainer(), props); + throw new UnsupportedOperationException( + Messages.get( + "ServiceContainerPluginFactory.serviceContainerRequired", new Object[] {"FailoverConnectionPlugin"})); + } + + @Override + public ConnectionPlugin getInstance(final ServiceContainer serviceContainer, final Properties props) { + return new FailoverConnectionPlugin(serviceContainer, props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginFactory.java index e54a67f57..4793f64c6 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginFactory.java @@ -18,12 +18,21 @@ import java.util.Properties; import software.amazon.jdbc.ConnectionPlugin; -import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.ServiceContainerPluginFactory; +import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; -public class LimitlessConnectionPluginFactory implements ConnectionPluginFactory { +public class LimitlessConnectionPluginFactory implements ServiceContainerPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - return new LimitlessConnectionPlugin(pluginService.getServiceContainer(), props); + throw new UnsupportedOperationException( + Messages.get( + "ServiceContainerPluginFactory.serviceContainerRequired", new Object[] {"LimitlessConnectionPlugin"})); + } + + @Override + public ConnectionPlugin getInstance(final ServiceContainer serviceContainer, final Properties props) { + return new LimitlessConnectionPlugin(serviceContainer, props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPluginFactory.java index b3465a3a1..ceb6e9840 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPluginFactory.java @@ -18,13 +18,21 @@ import java.util.Properties; import software.amazon.jdbc.ConnectionPlugin; -import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.ServiceContainerPluginFactory; +import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.ServiceContainer; -public class FastestResponseStrategyPluginFactory implements ConnectionPluginFactory { - +public class FastestResponseStrategyPluginFactory implements ServiceContainerPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - return new FastestResponseStrategyPlugin(pluginService.getServiceContainer(), props); + throw new UnsupportedOperationException( + Messages.get( + "ServiceContainerPluginFactory.serviceContainerRequired", new Object[] {"FastestResponseStrategyPlugin"})); + } + + @Override + public ConnectionPlugin getInstance(final ServiceContainer serviceContainer, final Properties props) { + return new FastestResponseStrategyPlugin(serviceContainer, props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index 140836912..03cc29202 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -71,7 +71,7 @@ public ConnectionServiceImpl( serviceContainer.setPluginService(partialPluginService); serviceContainer.setPluginManagerService(partialPluginService); - this.pluginManager.init(partialPluginService, props, partialPluginService, null); + this.pluginManager.init(serviceContainer, props, partialPluginService, null); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index 812cbf348..bd191709c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -156,8 +156,7 @@ protected ConnectionWrapper( init(props, serviceContainer, defaultConnectionProvider, driverDialect); } - protected void init( - final Properties props, + protected void init(final Properties props, final ServiceContainer serviceContainer, final ConnectionProvider defaultConnectionProvider, final TargetDriverDialect driverDialect) throws SQLException { @@ -167,8 +166,7 @@ protected void init( this.hostListProviderService = serviceContainer.getHostListProviderService(); this.pluginManagerService = serviceContainer.getPluginManagerService(); - this.pluginManager.init( - this.pluginService, props, pluginManagerService, this.configurationProfile); + this.pluginManager.init(serviceContainer, props, pluginManagerService, this.configurationProfile); final HostListProviderSupplier supplier = this.pluginService.getDialect().getHostListProvider(); if (supplier != null) { diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 057c17fb8..c12011ecc 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -371,6 +371,8 @@ SAMLCredentialsProviderFactory.getSamlAssertionFailed=Failed to get SAML Asserti SamlAuthPlugin.javaStsSdkNotInClasspath=Required dependency 'AWS Java SDK for AWS Secret Token Service' is not on the classpath. SamlAuthPlugin.unhandledException=Unhandled exception: ''{0}'' +ServiceContainerPluginFactory.serviceContainerRequired=The {0} requires a ServiceContainer. Please use getInstance(ServiceContainer, Properties) instead. + StorageServiceImpl.unexpectedValueMismatch=Attempted to store value ''{0}'' under item class ''{1}'' but there was an unexpected mismatch between the passed in value type and the expected value type. The cache for item class ''{1}'' is ''{2}''. StorageServiceImpl.itemClassNotRegistered=The given item class ''{0}'' is not registered. Please register the item class before storing items of that class. StorageServiceImpl.itemClassMismatch=The item stored at ''{0}'' did not have the expected type. The expected type was ''{1}'', but the stored item ''{2}'' had a type of ''{3}''. Returning null. diff --git a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java index d504e8505..006ce474f 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java @@ -17,8 +17,8 @@ package software.amazon.jdbc; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -64,7 +64,6 @@ void beforeEach() { closeable = MockitoAnnotations.openMocks(this); when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); when(mockServiceContainer.getTelemetryFactory()).thenReturn(mockTelemetryFactory); - when(mockPluginService.getServiceContainer()).thenReturn(mockServiceContainer); when(mockPluginService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockTelemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(mockTelemetryContext); when(mockTelemetryFactory.openTelemetryContext(eq(null), any())).thenReturn(mockTelemetryContext); @@ -77,7 +76,7 @@ public void testSortPlugins() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "iam,efm2,failover"); List result = builder.getPlugins( - mockPluginService, + mockServiceContainer, mockConnectionProvider, null, mockPluginManagerService, @@ -86,10 +85,10 @@ public void testSortPlugins() throws SQLException { assertNotNull(result); assertEquals(4, result.size()); - assertTrue(result.get(0) instanceof FailoverConnectionPlugin); - assertTrue(result.get(1) instanceof HostMonitoringConnectionPlugin); - assertTrue(result.get(2) instanceof IamAuthConnectionPlugin); - assertTrue(result.get(3) instanceof DefaultConnectionPlugin); + assertInstanceOf(FailoverConnectionPlugin.class, result.get(0)); + assertInstanceOf(HostMonitoringConnectionPlugin.class, result.get(1)); + assertInstanceOf(IamAuthConnectionPlugin.class, result.get(2)); + assertInstanceOf(DefaultConnectionPlugin.class, result.get(3)); } @Test @@ -100,7 +99,7 @@ public void testPreservePluginOrder() throws SQLException { props.put(PropertyDefinition.AUTO_SORT_PLUGIN_ORDER.name, "false"); List result = builder.getPlugins( - mockPluginService, + mockServiceContainer, mockConnectionProvider, null, mockPluginManagerService, @@ -109,10 +108,10 @@ public void testPreservePluginOrder() throws SQLException { assertNotNull(result); assertEquals(4, result.size()); - assertTrue(result.get(0) instanceof IamAuthConnectionPlugin); - assertTrue(result.get(1) instanceof HostMonitoringConnectionPlugin); - assertTrue(result.get(2) instanceof FailoverConnectionPlugin); - assertTrue(result.get(3) instanceof DefaultConnectionPlugin); + assertInstanceOf(IamAuthConnectionPlugin.class, result.get(0)); + assertInstanceOf(HostMonitoringConnectionPlugin.class, result.get(1)); + assertInstanceOf(FailoverConnectionPlugin.class, result.get(2)); + assertInstanceOf(DefaultConnectionPlugin.class, result.get(3)); } @Test @@ -122,7 +121,7 @@ public void testSortPluginsWithStickToPrior() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "dev,iam,executionTime,connectTime,efm2,failover"); List result = builder.getPlugins( - mockPluginService, + mockServiceContainer, mockConnectionProvider, null, mockPluginManagerService, @@ -131,12 +130,12 @@ public void testSortPluginsWithStickToPrior() throws SQLException { assertNotNull(result); assertEquals(7, result.size()); - assertTrue(result.get(0) instanceof DeveloperConnectionPlugin); - assertTrue(result.get(1) instanceof FailoverConnectionPlugin); - assertTrue(result.get(2) instanceof HostMonitoringConnectionPlugin); - assertTrue(result.get(3) instanceof IamAuthConnectionPlugin); - assertTrue(result.get(4) instanceof ExecutionTimeConnectionPlugin); - assertTrue(result.get(5) instanceof ConnectTimeConnectionPlugin); - assertTrue(result.get(6) instanceof DefaultConnectionPlugin); + assertInstanceOf(DeveloperConnectionPlugin.class, result.get(0)); + assertInstanceOf(FailoverConnectionPlugin.class, result.get(1)); + assertInstanceOf(HostMonitoringConnectionPlugin.class, result.get(2)); + assertInstanceOf(IamAuthConnectionPlugin.class, result.get(3)); + assertInstanceOf(ExecutionTimeConnectionPlugin.class, result.get(4)); + assertInstanceOf(ConnectTimeConnectionPlugin.class, result.get(5)); + assertInstanceOf(DefaultConnectionPlugin.class, result.get(6)); } } diff --git a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java index 48fdb805e..4c6e6b6c1 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java @@ -93,7 +93,6 @@ void cleanUp() throws Exception { void init() { closeable = MockitoAnnotations.openMocks(this); when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); - when(mockPluginService.getServiceContainer()).thenReturn(mockServiceContainer); when(mockPluginService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockTelemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(mockTelemetryContext); when(mockTelemetryFactory.openTelemetryContext(eq(null), any())).thenReturn(mockTelemetryContext); @@ -613,7 +612,7 @@ public void testDefaultPlugins() throws SQLException { null, mockConnectionWrapper, mockTelemetryFactory)); - target.init(mockPluginService, testProperties, mockPluginManagerService, configurationProfile); + target.init(mockServiceContainer, testProperties, mockPluginManagerService, configurationProfile); assertEquals(4, target.plugins.size()); assertEquals(AuroraConnectionTrackerPlugin.class, target.plugins.get(0).getClass()); @@ -632,7 +631,7 @@ public void testNoWrapperPlugins() throws SQLException { null, mockConnectionWrapper, mockTelemetryFactory)); - target.init(mockPluginService, testProperties, mockPluginManagerService, configurationProfile); + target.init(mockServiceContainer, testProperties, mockPluginManagerService, configurationProfile); assertEquals(1, target.plugins.size()); } @@ -647,7 +646,7 @@ public void testOverridingDefaultPluginsWithPluginCodes() throws SQLException { null, mockConnectionWrapper, mockTelemetryFactory)); - target.init(mockPluginService, testProperties, mockPluginManagerService, configurationProfile); + target.init(mockServiceContainer, testProperties, mockPluginManagerService, configurationProfile); assertEquals(2, target.plugins.size()); assertEquals(LogQueryConnectionPlugin.class, target.plugins.get(0).getClass()); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java deleted file mode 100644 index db23ac10a..000000000 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java +++ /dev/null @@ -1,966 +0,0 @@ -/* - * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * 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 software.amazon.jdbc.plugin.efm; - -import static software.amazon.jdbc.plugin.efm.HostMonitoringConnectionPlugin.FAILURE_DETECTION_COUNT; -import static software.amazon.jdbc.plugin.efm.HostMonitoringConnectionPlugin.FAILURE_DETECTION_INTERVAL; -import static software.amazon.jdbc.plugin.efm.HostMonitoringConnectionPlugin.FAILURE_DETECTION_TIME; -import static software.amazon.jdbc.plugin.efm.MonitorServiceImpl.MONITOR_DISPOSAL_TIME_MS; - -import java.sql.Array; -import java.sql.Blob; -import java.sql.CallableStatement; -import java.sql.Clob; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.NClob; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLClientInfoException; -import java.sql.SQLException; -import java.sql.SQLWarning; -import java.sql.SQLXML; -import java.sql.Savepoint; -import java.sql.Statement; -import java.sql.Struct; -import java.util.Date; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.logging.ConsoleHandler; -import java.util.logging.Level; -import java.util.logging.LogRecord; -import java.util.logging.Logger; -import java.util.logging.SimpleFormatter; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import software.amazon.jdbc.AllowedAndBlockedHosts; -import software.amazon.jdbc.ConnectionPlugin; -import software.amazon.jdbc.ConnectionProvider; -import software.amazon.jdbc.HostListProvider; -import software.amazon.jdbc.HostRole; -import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.HostSpecBuilder; -import software.amazon.jdbc.JdbcCallable; -import software.amazon.jdbc.NodeChangeOptions; -import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.dialect.Dialect; -import software.amazon.jdbc.dialect.UnknownDialect; -import software.amazon.jdbc.hostavailability.HostAvailability; -import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; -import software.amazon.jdbc.states.SessionStateService; -import software.amazon.jdbc.targetdriverdialect.PgTargetDriverDialect; -import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.telemetry.TelemetryFactory; - -@Disabled -@SuppressWarnings("checkstyle:OverloadMethodsDeclarationOrder") -public class ConcurrencyTests { - - @Test - public void testUsePluginConcurrently_SeparatePluginInstances() throws InterruptedException { - - final Level logLevel = Level.OFF; - - final Logger efmLogger = Logger.getLogger("software.amazon.jdbc.plugin.efm"); - efmLogger.setUseParentHandlers(false); - ConsoleHandler handler = new ConsoleHandler(); - handler.setLevel(logLevel); - handler.setFormatter(new SimpleFormatter() { - private static final String format = "[%1$tF %1$tT] [%4$-10s] [%2$-7s] %3$s %n"; - - @Override - public synchronized String format(LogRecord lr) { - return String.format(format, - new Date(lr.getMillis()), - lr.getLevel().getLocalizedName(), - lr.getMessage(), - Thread.currentThread().getName() - ); - } - }); - efmLogger.addHandler(handler); - - final ClassLoader mainClassLoader = ClassLoader.getSystemClassLoader(); - final ExecutorService executor = Executors.newCachedThreadPool( - r -> { - final Thread monitoringThread = new Thread(r); - monitoringThread.setDaemon(true); - monitoringThread.setContextClassLoader(mainClassLoader); - return monitoringThread; - }); - - final HostSpec hostSpec = new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("test-host") - .build(); - hostSpec.addAlias("test-host-alias-a"); - hostSpec.addAlias("test-host-alias-b"); - - for (int i = 0; i < 10; i++) { - executor.submit(() -> { - - final Properties properties = new Properties(); - MONITOR_DISPOSAL_TIME_MS.set(properties, "30000"); - FAILURE_DETECTION_TIME.set(properties, "10000"); - FAILURE_DETECTION_INTERVAL.set(properties, "1000"); - FAILURE_DETECTION_COUNT.set(properties, "1"); - - final JdbcCallable sqlFunction = () -> { - try { - TimeUnit.SECONDS.sleep(5); - } catch (InterruptedException e) { - // do nothing - } - return null; - }; - - final Connection connection = new TestConnection(); - PluginService pluginService = new TestPluginService(hostSpec, connection); - - final HostMonitoringConnectionPlugin targetPlugin = - new HostMonitoringConnectionPlugin(pluginService.getServiceContainer(), properties); - - final Logger threadLogger = Logger.getLogger("software.amazon.jdbc.plugin.efm"); - threadLogger.setLevel(logLevel); - - while (!Thread.currentThread().isInterrupted()) { - try { - threadLogger.log(Level.FINEST, "Run target plugin execute()"); - targetPlugin.execute( - ResultSet.class, - SQLException.class, - Connection.class, - "Connection.executeQuery", - sqlFunction, - new Object[0]); - } catch (SQLException e) { - threadLogger.log(Level.FINEST, "Exception", e); - } - } - threadLogger.log(Level.FINEST, "Stopped."); - }); - } - executor.shutdown(); - - TimeUnit.SECONDS.sleep(60); // test time - - // cool down - executor.shutdownNow(); - } - - @Test - public void testUsePluginConcurrently_SamePluginInstance() throws InterruptedException { - - final Level logLevel = Level.OFF; - - final Logger efmLogger = Logger.getLogger("software.amazon.jdbc.plugin.efm"); - efmLogger.setUseParentHandlers(false); - ConsoleHandler handler = new ConsoleHandler(); - handler.setLevel(logLevel); - handler.setFormatter(new SimpleFormatter() { - private static final String format = "[%1$tF %1$tT] [%4$-10s] [%2$-7s] %3$s %n"; - - @Override - public synchronized String format(LogRecord lr) { - return String.format(format, - new Date(lr.getMillis()), - lr.getLevel().getLocalizedName(), - lr.getMessage(), - Thread.currentThread().getName() - ); - } - }); - efmLogger.addHandler(handler); - - final ClassLoader mainClassLoader = ClassLoader.getSystemClassLoader(); - final ExecutorService executor = Executors.newCachedThreadPool( - r -> { - final Thread monitoringThread = new Thread(r); - monitoringThread.setDaemon(true); - monitoringThread.setContextClassLoader(mainClassLoader); - return monitoringThread; - }); - - final HostSpec hostSpec = new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("test-host") - .build(); - hostSpec.addAlias("test-host-alias-a"); - hostSpec.addAlias("test-host-alias-b"); - - final Properties properties = new Properties(); - MONITOR_DISPOSAL_TIME_MS.set(properties, "30000"); - FAILURE_DETECTION_TIME.set(properties, "10000"); - FAILURE_DETECTION_INTERVAL.set(properties, "1000"); - FAILURE_DETECTION_COUNT.set(properties, "1"); - - final JdbcCallable sqlFunction = () -> { - try { - TimeUnit.SECONDS.sleep(5); - } catch (InterruptedException e) { - // do nothing - } - return null; - }; - - final Connection connection = new TestConnection(); - final PluginService pluginService = new TestPluginService(hostSpec, connection); - - final HostMonitoringConnectionPlugin targetPlugin = - new HostMonitoringConnectionPlugin(pluginService.getServiceContainer(), properties); - - for (int i = 0; i < 10; i++) { - executor.submit(() -> { - - final Logger threadLogger = Logger.getLogger("software.amazon.jdbc.plugin.efm"); - threadLogger.setLevel(logLevel); - - while (!Thread.currentThread().isInterrupted()) { - try { - threadLogger.log(Level.FINEST, "Run target plugin execute()"); - targetPlugin.execute( - ResultSet.class, - SQLException.class, - Connection.class, - "Connection.executeQuery", - sqlFunction, - new Object[0]); - } catch (SQLException e) { - threadLogger.log(Level.FINEST, "Exception", e); - } - } - threadLogger.log(Level.FINEST, "Stopped."); - }); - } - executor.shutdown(); - - TimeUnit.SECONDS.sleep(60); // test time - - // cool down - executor.shutdownNow(); - } - - public static class TestSessionStateService implements SessionStateService { - - @Override - public Optional getAutoCommit() throws SQLException { - return Optional.empty(); - } - - @Override - public void setAutoCommit(boolean autoCommit) throws SQLException { - - } - - @Override - public void setupPristineAutoCommit() throws SQLException { - - } - - @Override - public void setupPristineAutoCommit(boolean autoCommit) throws SQLException { - - } - - @Override - public Optional getReadOnly() throws SQLException { - return Optional.empty(); - } - - @Override - public void setReadOnly(boolean readOnly) throws SQLException { - - } - - @Override - public void setupPristineReadOnly() throws SQLException { - - } - - @Override - public void setupPristineReadOnly(boolean readOnly) throws SQLException { - - } - - @Override - public Optional getCatalog() throws SQLException { - return Optional.empty(); - } - - @Override - public void setCatalog(String catalog) throws SQLException { - - } - - @Override - public void setupPristineCatalog() throws SQLException { - - } - - @Override - public void setupPristineCatalog(String catalog) throws SQLException { - - } - - @Override - public Optional getHoldability() throws SQLException { - return Optional.empty(); - } - - @Override - public void setHoldability(int holdability) throws SQLException { - - } - - @Override - public void setupPristineHoldability() throws SQLException { - - } - - @Override - public void setupPristineHoldability(int holdability) throws SQLException { - - } - - @Override - public Optional getNetworkTimeout() throws SQLException { - return Optional.empty(); - } - - @Override - public void setNetworkTimeout(int milliseconds) throws SQLException { - - } - - @Override - public void setupPristineNetworkTimeout() throws SQLException { - - } - - @Override - public void setupPristineNetworkTimeout(int milliseconds) throws SQLException { - - } - - @Override - public Optional getSchema() throws SQLException { - return Optional.empty(); - } - - @Override - public void setSchema(String schema) throws SQLException { - - } - - @Override - public void setupPristineSchema() throws SQLException { - - } - - @Override - public void setupPristineSchema(String schema) throws SQLException { - - } - - @Override - public Optional getTransactionIsolation() throws SQLException { - return Optional.empty(); - } - - @Override - public void setTransactionIsolation(int level) throws SQLException { - - } - - @Override - public void setupPristineTransactionIsolation() throws SQLException { - - } - - @Override - public void setupPristineTransactionIsolation(int level) throws SQLException { - - } - - @Override - public Optional>> getTypeMap() throws SQLException { - return Optional.empty(); - } - - @Override - public void setTypeMap(Map> map) throws SQLException { - - } - - @Override - public void setupPristineTypeMap() throws SQLException { - - } - - @Override - public void setupPristineTypeMap(Map> map) throws SQLException { - - } - - @Override - public void reset() { - - } - - @Override - public void begin() throws SQLException { - - } - - @Override - public void complete() { - - } - - @Override - public void applyCurrentSessionState(Connection newConnection) throws SQLException { - - } - - @Override - public void applyPristineSessionState(Connection connection) throws SQLException { - - } - } - - public static class TestPluginService implements PluginService { - - private final HostSpec hostSpec; - private final Connection connection; - - public TestPluginService(HostSpec hostSpec, Connection connection) { - this.hostSpec = hostSpec; - this.connection = connection; - } - - @Override - public Connection getCurrentConnection() { - return this.connection; - } - - @Override - public HostSpec getCurrentHostSpec() { - return this.hostSpec; - } - - @Override - public void setCurrentConnection(@NonNull Connection connection, @NonNull HostSpec hostSpec) - throws SQLException { - - } - - @Override - public EnumSet setCurrentConnection(@NonNull Connection connection, - @NonNull HostSpec hostSpec, @Nullable ConnectionPlugin skipNotificationForThisPlugin) - throws SQLException { - return null; - } - - @Override - public List getAllHosts() { - return null; - } - - @Override - public List getHosts() { - return null; - } - - @Override - public HostSpec getInitialConnectionHostSpec() { - return null; - } - - @Override - public String getOriginalUrl() { - return null; - } - - @Override - public ServiceContainer getServiceContainer() { - return null; - } - - @Override - public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHosts) { - } - - @Override - public boolean acceptsStrategy(HostRole role, String strategy) { - return false; - } - - @Override - public HostSpec getHostSpecByStrategy(HostRole role, String strategy) { - return null; - } - - @Override - public HostSpec getHostSpecByStrategy(List hosts, HostRole role, String strategy) { - return null; - } - - @Override - public HostRole getHostRole(Connection conn) { - return null; - } - - @Override - public void setAvailability(Set hostAliases, HostAvailability availability) { - } - - @Override - public boolean isInTransaction() { - return false; - } - - @Override - public HostListProvider getHostListProvider() { - return null; - } - - @Override - public void refreshHostList() throws SQLException { - } - - @Override - public void refreshHostList(Connection connection) throws SQLException { - } - - @Override - public void forceRefreshHostList() throws SQLException { - } - - @Override - public void forceRefreshHostList(Connection connection) throws SQLException { - } - - @Override - public boolean forceRefreshHostList(final boolean shouldVerifyWriter, long timeoutMs) - throws SQLException { - return false; - } - - @Override - public Connection connect(HostSpec hostSpec, Properties props, @Nullable ConnectionPlugin pluginToSkip) - throws SQLException { - return new TestConnection(); - } - - @Override - public Connection connect(HostSpec hostSpec, Properties props) throws SQLException { - return this.connect(hostSpec, props, null); - } - - @Override - public Connection forceConnect(HostSpec hostSpec, Properties props) throws SQLException { - return this.forceConnect(hostSpec, props, null); - } - - @Override - public Connection forceConnect(HostSpec hostSpec, Properties props, @Nullable ConnectionPlugin pluginToSkip) - throws SQLException { - return new TestConnection(); - } - - @Override - public TelemetryFactory getTelemetryFactory() { - return null; - } - - @Override - public String getTargetName() { - return null; - } - - @Override - public @NonNull SessionStateService getSessionStateService() { - return new TestSessionStateService(); - } - - @Override - public T getPlugin(Class pluginClazz) { - return null; - } - - @Override - public boolean isNetworkException(Throwable throwable) { - return false; - } - - @Override - public boolean isNetworkException(Throwable throwable, @Nullable TargetDriverDialect targetDriverDialect) { - return false; - } - - @Override - public boolean isNetworkException(String sqlState) { - return false; - } - - @Override - public boolean isLoginException(String sqlState) { - return false; - } - - @Override - public boolean isLoginException(Throwable throwable) { - return false; - } - - @Override - public boolean isLoginException(Throwable throwable, @Nullable TargetDriverDialect targetDriverDialect) { - return false; - } - - @Override - public Dialect getDialect() { - return new UnknownDialect(); - } - - @Override - public TargetDriverDialect getTargetDriverDialect() { - return new PgTargetDriverDialect(); - } - - public void updateDialect(final @NonNull Connection connection) throws SQLException { } - - @Override - public HostSpec identifyConnection(Connection connection) throws SQLException { - return null; - } - - @Override - public void fillAliases(Connection connection, HostSpec hostSpec) throws SQLException { - - } - - @Override - public HostSpecBuilder getHostSpecBuilder() { - return new HostSpecBuilder(new SimpleHostAvailabilityStrategy()); - } - - @Override - public ConnectionProvider getConnectionProvider() { - return null; - } - - @Override - public boolean isPooledConnectionProvider(HostSpec host, Properties props) { - return false; - } - - @Override - public String getDriverProtocol() { - return null; - } - - @Override - public Properties getProperties() { - return null; - } - } - - public static class TestConnection implements Connection { - - @Override - public Statement createStatement() throws SQLException { - return null; - } - - @Override - public PreparedStatement prepareStatement(String sql) throws SQLException { - return null; - } - - @Override - public CallableStatement prepareCall(String sql) throws SQLException { - return null; - } - - @Override - public String nativeSQL(String sql) throws SQLException { - return null; - } - - @Override - public void setAutoCommit(boolean autoCommit) throws SQLException { - - } - - @Override - public boolean getAutoCommit() throws SQLException { - return false; - } - - @Override - public void commit() throws SQLException { - - } - - @Override - public void rollback() throws SQLException { - - } - - @Override - public void close() throws SQLException { - - } - - @Override - public boolean isClosed() throws SQLException { - return false; - } - - @Override - public DatabaseMetaData getMetaData() throws SQLException { - return null; - } - - @Override - public void setReadOnly(boolean readOnly) throws SQLException { - - } - - @Override - public boolean isReadOnly() throws SQLException { - return false; - } - - @Override - public void setCatalog(String catalog) throws SQLException { - - } - - @Override - public String getCatalog() throws SQLException { - return null; - } - - @Override - public void setTransactionIsolation(int level) throws SQLException { - - } - - @Override - public int getTransactionIsolation() throws SQLException { - return 0; - } - - @Override - public SQLWarning getWarnings() throws SQLException { - return null; - } - - @Override - public void clearWarnings() throws SQLException { - - } - - @Override - public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { - return null; - } - - @Override - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) - throws SQLException { - return null; - } - - @Override - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { - return null; - } - - @Override - public Map> getTypeMap() throws SQLException { - return null; - } - - @Override - public void setTypeMap(Map> map) throws SQLException { - - } - - @Override - public void setHoldability(int holdability) throws SQLException { - - } - - @Override - public int getHoldability() throws SQLException { - return 0; - } - - @Override - public Savepoint setSavepoint() throws SQLException { - return null; - } - - @Override - public Savepoint setSavepoint(String name) throws SQLException { - return null; - } - - @Override - public void rollback(Savepoint savepoint) throws SQLException { - - } - - @Override - public void releaseSavepoint(Savepoint savepoint) throws SQLException { - - } - - @Override - public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) - throws SQLException { - return null; - } - - @Override - public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, - int resultSetHoldability) throws SQLException { - return null; - } - - @Override - public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, - int resultSetHoldability) throws SQLException { - return null; - } - - @Override - public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { - return null; - } - - @Override - public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { - return null; - } - - @Override - public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { - return null; - } - - @Override - public Clob createClob() throws SQLException { - return null; - } - - @Override - public Blob createBlob() throws SQLException { - return null; - } - - @Override - public NClob createNClob() throws SQLException { - return null; - } - - @Override - public SQLXML createSQLXML() throws SQLException { - return null; - } - - @Override - public boolean isValid(int timeout) throws SQLException { - return true; - } - - @Override - public void setClientInfo(String name, String value) throws SQLClientInfoException { - - } - - @Override - public void setClientInfo(Properties properties) throws SQLClientInfoException { - - } - - @Override - public String getClientInfo(String name) throws SQLException { - return null; - } - - @Override - public Properties getClientInfo() throws SQLException { - return null; - } - - @Override - public Array createArrayOf(String typeName, Object[] elements) throws SQLException { - return null; - } - - @Override - public Struct createStruct(String typeName, Object[] attributes) throws SQLException { - return null; - } - - @Override - public void setSchema(String schema) throws SQLException { - - } - - @Override - public String getSchema() throws SQLException { - return null; - } - - @Override - public void abort(Executor executor) throws SQLException { - - } - - @Override - public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { - - } - - @Override - public int getNetworkTimeout() throws SQLException { - return 0; - } - - @Override - public T unwrap(Class iface) throws SQLException { - return null; - } - - @Override - public boolean isWrapperFor(Class iface) throws SQLException { - return false; - } - } -} From 2d509bfdb53dcbabc1d3ed83c5ef2a730597a952 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 16 May 2025 10:55:37 -0700 Subject: [PATCH 108/149] Add CoreServicesContainer --- .../java/software/amazon/jdbc/Driver.java | 30 +++++----- .../amazon/jdbc/ds/AwsWrapperDataSource.java | 19 +++--- .../jdbc/util/CoreServicesContainer.java | 58 +++++++++++++++++++ 3 files changed, 87 insertions(+), 20 deletions(-) create mode 100644 wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index 6185fb895..d796718d0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -20,7 +20,6 @@ import java.sql.DriverManager; import java.sql.DriverPropertyInfo; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -58,6 +57,7 @@ import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialectManager; import software.amazon.jdbc.util.ConnectionUrlParser; +import software.amazon.jdbc.util.CoreServicesContainer; import software.amazon.jdbc.util.DriverInfo; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; @@ -65,12 +65,8 @@ import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.ServiceContainerImpl; import software.amazon.jdbc.util.StringUtils; -import software.amazon.jdbc.util.events.BatchingEventPublisher; -import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.monitoring.MonitorService; -import software.amazon.jdbc.util.monitoring.MonitorServiceImpl; import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -84,10 +80,6 @@ public class Driver implements java.sql.Driver { private static final Logger LOGGER = Logger.getLogger("software.amazon.jdbc.Driver"); private static @Nullable Driver registeredDriver; - private static final EventPublisher publisher = new BatchingEventPublisher(); - private static final StorageService storageService = new StorageServiceImpl(publisher); - private static final MonitorService monitorService = new MonitorServiceImpl(publisher); - private static final AtomicReference resetSessionStateOnCloseCallable = new AtomicReference<>(null); private static final AtomicReference transferSessionStateOnSwitchCallable = @@ -116,6 +108,18 @@ public class Driver implements java.sql.Driver { } } + private final StorageService storageService; + private final MonitorService monitorService; + + public Driver() { + this(CoreServicesContainer.getInstance()); + } + + public Driver(CoreServicesContainer coreServicesContainer) { + this.storageService = coreServicesContainer.getStorageService(); + this.monitorService = coreServicesContainer.getMonitorService(); + } + public static void register() throws SQLException { if (isRegistered()) { throw new IllegalStateException( @@ -257,7 +261,7 @@ public boolean acceptsURL(final String url) throws SQLException { } @Override - public DriverPropertyInfo[] getPropertyInfo(final String url, final Properties info) throws SQLException { + public DriverPropertyInfo[] getPropertyInfo(final String url, final Properties info) { final Properties copy = new Properties(info); final String databaseName = ConnectionUrlParser.parseDatabaseFromUrl(url); if (!StringUtils.isNullOrEmpty(databaseName)) { @@ -291,7 +295,7 @@ public boolean jdbcCompliant() { } @Override - public Logger getParentLogger() throws SQLFeatureNotSupportedException { + public Logger getParentLogger() { return PARENT_LOGGER; } @@ -401,7 +405,7 @@ public static void resetConnectionInitFunc() { } public static void clearCaches() { - storageService.clearAll(); + CoreServicesContainer.getInstance().getStorageService().clearAll(); RdsUtils.clearCache(); RdsHostListProvider.clearAll(); PluginServiceImpl.clearCache(); @@ -420,7 +424,7 @@ public static void clearCaches() { } public static void releaseResources() { - monitorService.stopAndRemoveAll(); + CoreServicesContainer.getInstance().getMonitorService().stopAndRemoveAll(); software.amazon.jdbc.plugin.efm2.MonitorServiceImpl.closeAllMonitors(); MonitorThreadContainer.releaseInstance(); ConnectionProviderManager.releaseResources(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index 2bc7023aa..172ab210e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -47,6 +47,7 @@ import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialectManager; import software.amazon.jdbc.util.ConnectionUrlParser; +import software.amazon.jdbc.util.CoreServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.ServiceContainer; @@ -54,12 +55,8 @@ import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; -import software.amazon.jdbc.util.events.BatchingEventPublisher; -import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.monitoring.MonitorService; -import software.amazon.jdbc.util.monitoring.MonitorServiceImpl; import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.util.storage.StorageServiceImpl; import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -75,9 +72,8 @@ public class AwsWrapperDataSource implements DataSource, Referenceable, Serializ private static final String SERVER_NAME = "serverName"; private static final String SERVER_PORT = "serverPort"; - private static final EventPublisher publisher = new BatchingEventPublisher(); - private static final StorageService storageService = new StorageServiceImpl(publisher); - private static final MonitorService monitorService = new MonitorServiceImpl(publisher); + private final StorageService storageService; + private final MonitorService monitorService; static { try { @@ -101,6 +97,15 @@ public class AwsWrapperDataSource implements DataSource, Referenceable, Serializ protected @Nullable String database; private int loginTimeout = 0; + public AwsWrapperDataSource() { + this(CoreServicesContainer.getInstance()); + } + + public AwsWrapperDataSource(CoreServicesContainer coreServicesContainer) { + this.storageService = coreServicesContainer.getStorageService(); + this.monitorService = coreServicesContainer.getMonitorService(); + } + @Override public Connection getConnection() throws SQLException { setCredentialPropertiesFromUrl(this.jdbcUrl); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java new file mode 100644 index 000000000..ec9cca8c4 --- /dev/null +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java @@ -0,0 +1,58 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util; + +import software.amazon.jdbc.util.events.BatchingEventPublisher; +import software.amazon.jdbc.util.events.EventPublisher; +import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.MonitorServiceImpl; +import software.amazon.jdbc.util.storage.StorageService; +import software.amazon.jdbc.util.storage.StorageServiceImpl; + +/** + * A singleton container object used to instantiate core services. This class should be used instead of directly + * instantiating core services so that only one instance of each service is instantiated. + */ +public class CoreServicesContainer { + private static final CoreServicesContainer INSTANCE = new CoreServicesContainer(); + + private final EventPublisher eventPublisher; + private final StorageService storageService; + private final MonitorService monitorService; + + private CoreServicesContainer() { + this.eventPublisher = new BatchingEventPublisher(); + this.storageService = new StorageServiceImpl(eventPublisher); + this.monitorService = new MonitorServiceImpl(eventPublisher); + } + + public static CoreServicesContainer getInstance() { + return INSTANCE; + } + + public EventPublisher getEventPublisher() { + return eventPublisher; + } + + public StorageService getStorageService() { + return storageService; + } + + public MonitorService getMonitorService() { + return monitorService; + } +} From 7c534c4f01c9ecaf0db528eb79f159250d79191b Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 16 May 2025 13:09:17 -0700 Subject: [PATCH 109/149] Add TestStorageServiceImpl, make StorageServiceImpl cache and executor non-static --- .../jdbc/util/storage/StorageServiceImpl.java | 44 ++++++------------- .../amazon/jdbc/PluginServiceImplTests.java | 4 +- .../RdsHostListProviderTest.java | 4 +- .../RdsMultiAzDbClusterListProviderTest.java | 4 +- .../util/storage/TestStorageServiceImpl.java | 33 ++++++++++++++ 5 files changed, 52 insertions(+), 37 deletions(-) create mode 100644 wrapper/src/test/java/software/amazon/jdbc/util/storage/TestStorageServiceImpl.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 29d784dc9..897583a0e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -23,8 +23,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.ReentrantLock; import java.util.function.Supplier; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; @@ -37,20 +35,7 @@ public class StorageServiceImpl implements StorageService { private static final Logger LOGGER = Logger.getLogger(StorageServiceImpl.class.getName()); protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(5); - protected static final Map, ExpirationCache> caches = new ConcurrentHashMap<>(); protected static final Map, Supplier>> defaultCacheSuppliers; - protected static final AtomicBoolean isInitialized = new AtomicBoolean(false); - protected static final ReentrantLock initLock = new ReentrantLock(); - protected static final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { - final Thread thread = new Thread(r); - thread.setDaemon(true); - if (!StringUtils.isNullOrEmpty(thread.getName())) { - thread.setName(thread.getName() + "-ssi"); - } - return thread; - })); - - protected final EventPublisher publisher; static { Map, Supplier>> suppliers = new HashMap<>(); @@ -59,6 +44,17 @@ public class StorageServiceImpl implements StorageService { defaultCacheSuppliers = Collections.unmodifiableMap(suppliers); } + protected final EventPublisher publisher; + protected final Map, ExpirationCache> caches = new ConcurrentHashMap<>(); + protected final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { + final Thread thread = new Thread(r); + thread.setDaemon(true); + if (!StringUtils.isNullOrEmpty(thread.getName())) { + thread.setName(thread.getName() + "-ssi"); + } + return thread; + })); + public StorageServiceImpl(EventPublisher publisher) { this(DEFAULT_CLEANUP_INTERVAL_NANOS, publisher); } @@ -69,22 +65,8 @@ public StorageServiceImpl(long cleanupIntervalNanos, EventPublisher publisher) { } protected void initCleanupThread(long cleanupIntervalNanos) { - if (isInitialized.get()) { - return; - } - - initLock.lock(); - try { - if (isInitialized.get()) { - return; - } - - cleanupExecutor.scheduleAtFixedRate( - this::removeExpiredItems, cleanupIntervalNanos, cleanupIntervalNanos, TimeUnit.NANOSECONDS); - isInitialized.set(true); - } finally { - initLock.unlock(); - } + this.cleanupExecutor.scheduleAtFixedRate( + this::removeExpiredItems, cleanupIntervalNanos, cleanupIntervalNanos, TimeUnit.NANOSECONDS); } protected void removeExpiredItems() { diff --git a/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java b/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java index aa99079b5..690977f36 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java @@ -69,7 +69,7 @@ import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.util.storage.StorageServiceImpl; +import software.amazon.jdbc.util.storage.TestStorageServiceImpl; public class PluginServiceImplTests { @@ -104,7 +104,7 @@ void setUp() throws SQLException { when(statement.executeQuery(any())).thenReturn(resultSet); when(serviceContainer.getConnectionPluginManager()).thenReturn(pluginManager); when(serviceContainer.getStorageService()).thenReturn(storageService); - storageService = new StorageServiceImpl(mockEventPublisher); + storageService = new TestStorageServiceImpl(mockEventPublisher); PluginServiceImpl.hostAvailabilityExpiringCache.clear(); } diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java index eee427193..e957bb2f6 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java @@ -67,7 +67,7 @@ import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.util.storage.StorageServiceImpl; +import software.amazon.jdbc.util.storage.TestStorageServiceImpl; import software.amazon.jdbc.util.storage.Topology; class RdsHostListProviderTest { @@ -94,7 +94,7 @@ class RdsHostListProviderTest { @BeforeEach void setUp() throws SQLException { closeable = MockitoAnnotations.openMocks(this); - storageService = new StorageServiceImpl(mockEventPublisher); + storageService = new TestStorageServiceImpl(mockEventPublisher); when(mockServiceContainer.getHostListProviderService()).thenReturn(mockHostListProviderService); when(mockServiceContainer.getStorageService()).thenReturn(storageService); when(mockPluginService.getCurrentConnection()).thenReturn(mockConnection); diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java index 6a74f61e2..5264cd7cd 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java @@ -61,7 +61,7 @@ import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.util.storage.StorageServiceImpl; +import software.amazon.jdbc.util.storage.TestStorageServiceImpl; import software.amazon.jdbc.util.storage.Topology; class RdsMultiAzDbClusterListProviderTest { @@ -88,7 +88,7 @@ class RdsMultiAzDbClusterListProviderTest { @BeforeEach void setUp() throws SQLException { closeable = MockitoAnnotations.openMocks(this); - storageService = new StorageServiceImpl(mockEventPublisher); + storageService = new TestStorageServiceImpl(mockEventPublisher); when(mockServiceContainer.getHostListProviderService()).thenReturn(mockHostListProviderService); when(mockServiceContainer.getStorageService()).thenReturn(storageService); when(mockPluginService.getCurrentConnection()).thenReturn(mockConnection); diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/storage/TestStorageServiceImpl.java b/wrapper/src/test/java/software/amazon/jdbc/util/storage/TestStorageServiceImpl.java new file mode 100644 index 000000000..bcda6beaa --- /dev/null +++ b/wrapper/src/test/java/software/amazon/jdbc/util/storage/TestStorageServiceImpl.java @@ -0,0 +1,33 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * 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 software.amazon.jdbc.util.storage; + +import software.amazon.jdbc.util.events.EventPublisher; + +/** + * A StorageServiceImpl that doesn't sumbit a cleanup thread. This is useful for testing purposes. + */ +public class TestStorageServiceImpl extends StorageServiceImpl { + public TestStorageServiceImpl(EventPublisher publisher) { + super(publisher); + } + + @Override + protected void initCleanupThread(long cleanupIntervalNanos) { + // do nothing + } +} From 39fbff4fb080bf6a5451d5f76a2f2515991c270c Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 16 May 2025 13:33:26 -0700 Subject: [PATCH 110/149] Add empty close method to AbstractMonitor --- .../software/amazon/jdbc/ServiceContainerPluginFactory.java | 2 -- .../amazon/jdbc/util/monitoring/AbstractMonitor.java | 5 +++++ .../amazon/jdbc/util/monitoring/MonitorServiceImplTest.java | 5 ----- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java index 2a7f50e6f..8282dff88 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java @@ -16,8 +16,6 @@ package software.amazon.jdbc; -// TODO: fix docs - import java.util.Properties; import software.amazon.jdbc.util.ServiceContainer; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index 670dc8309..94593916b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -97,6 +97,11 @@ public void stop() { } } + @Override + public void close() { + // do nothing. Classes that extend this class should override this method if they open resources that need closing. + } + @Override public long getLastActivityTimestampNanos() { return this.lastActivityTimestampNanos; diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java index 4fee050f5..6d9b09651 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java @@ -290,10 +290,5 @@ protected NoOpMonitor( public void monitor() { // do nothing. } - - @Override - public void close() { - // do nothing. - } } } From 38bdb5506b5b9e63e8a790122cf021f22792f150 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 16 May 2025 14:15:08 -0700 Subject: [PATCH 111/149] Rename monitoring.MonitorServiceImpl to DefaultMonitorService --- .../software/amazon/jdbc/util/CoreServicesContainer.java | 4 ++-- ...MonitorServiceImpl.java => DefaultMonitorService.java} | 8 ++++---- ...erviceImplTest.java => DefaultMonitorServiceTest.java} | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) rename wrapper/src/main/java/software/amazon/jdbc/util/monitoring/{MonitorServiceImpl.java => DefaultMonitorService.java} (97%) rename wrapper/src/test/java/software/amazon/jdbc/util/monitoring/{MonitorServiceImplTest.java => DefaultMonitorServiceTest.java} (98%) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java index ec9cca8c4..b6eea8ae3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java @@ -19,7 +19,7 @@ import software.amazon.jdbc.util.events.BatchingEventPublisher; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.monitoring.MonitorService; -import software.amazon.jdbc.util.monitoring.MonitorServiceImpl; +import software.amazon.jdbc.util.monitoring.DefaultMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; @@ -37,7 +37,7 @@ public class CoreServicesContainer { private CoreServicesContainer() { this.eventPublisher = new BatchingEventPublisher(); this.storageService = new StorageServiceImpl(eventPublisher); - this.monitorService = new MonitorServiceImpl(eventPublisher); + this.monitorService = new DefaultMonitorService(eventPublisher); } public static CoreServicesContainer getInstance() { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/DefaultMonitorService.java similarity index 97% rename from wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java rename to wrapper/src/main/java/software/amazon/jdbc/util/monitoring/DefaultMonitorService.java index 4a4eeb2c2..106077379 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/DefaultMonitorService.java @@ -52,8 +52,8 @@ import software.amazon.jdbc.util.storage.Topology; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -public class MonitorServiceImpl implements MonitorService, EventSubscriber { - private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); +public class DefaultMonitorService implements MonitorService, EventSubscriber { + private static final Logger LOGGER = Logger.getLogger(DefaultMonitorService.class.getName()); protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(1); protected static final Map, Supplier> defaultSuppliers; @@ -84,7 +84,7 @@ public class MonitorServiceImpl implements MonitorService, EventSubscriber { })); - public MonitorServiceImpl(EventPublisher publisher) { + public DefaultMonitorService(EventPublisher publisher) { this(DEFAULT_CLEANUP_INTERVAL_NANOS, publisher); } @@ -96,7 +96,7 @@ public MonitorServiceImpl(EventPublisher publisher) { * nanoseconds. * @param publisher the publisher to subscribe to for data access events. */ - public MonitorServiceImpl(long cleanupIntervalNanos, EventPublisher publisher) { + public DefaultMonitorService(long cleanupIntervalNanos, EventPublisher publisher) { this.publisher = publisher; this.publisher.subscribe(this, new HashSet<>(Collections.singletonList(DataAccessEvent.class))); initCleanupThread(cleanupIntervalNanos); diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/DefaultMonitorServiceTest.java similarity index 98% rename from wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java rename to wrapper/src/test/java/software/amazon/jdbc/util/monitoring/DefaultMonitorServiceTest.java index 6d9b09651..c284eaa17 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/DefaultMonitorServiceTest.java @@ -39,19 +39,19 @@ import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -class MonitorServiceImplTest { +class DefaultMonitorServiceTest { @Mock StorageService storageService; @Mock TelemetryFactory telemetryFactory; @Mock TargetDriverDialect targetDriverDialect; @Mock Dialect dbDialect; @Mock EventPublisher publisher; - MonitorServiceImpl monitorService; + DefaultMonitorService monitorService; private AutoCloseable closeable; @BeforeEach void setUp() { closeable = MockitoAnnotations.openMocks(this); - monitorService = new MonitorServiceImpl(publisher) { + monitorService = new DefaultMonitorService(publisher) { @Override protected void initCleanupThread(long cleanupIntervalNanos) { // Do nothing From e98a122c29ab4e6a80f73ce55c3c58c7c8753b18 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 16 May 2025 14:22:35 -0700 Subject: [PATCH 112/149] Rename to CoreMonitorService to distinguish from efm/efm2 MonitorService --- .../amazon/jdbc/benchmarks/PluginBenchmarks.java | 4 ++-- .../benchmarks/testplugin/TestConnectionWrapper.java | 4 ++-- .../src/main/java/software/amazon/jdbc/Driver.java | 4 ++-- .../amazon/jdbc/ds/AwsWrapperDataSource.java | 4 ++-- .../monitoring/ClusterTopologyMonitorImpl.java | 4 ++-- .../monitoring/MonitoringRdsHostListProvider.java | 4 ++-- .../MultiAzClusterTopologyMonitorImpl.java | 4 ++-- .../customendpoint/CustomEndpointMonitorImpl.java | 4 ++-- .../plugin/customendpoint/CustomEndpointPlugin.java | 4 ++-- .../amazon/jdbc/util/CoreServicesContainer.java | 10 +++++----- .../software/amazon/jdbc/util/ServiceContainer.java | 6 +++--- .../amazon/jdbc/util/ServiceContainerImpl.java | 12 ++++++------ .../jdbc/util/connection/ConnectionServiceImpl.java | 4 ++-- .../amazon/jdbc/util/monitoring/AbstractMonitor.java | 4 ++-- .../{MonitorService.java => CoreMonitorService.java} | 2 +- ...nitorService.java => CoreMonitorServiceImpl.java} | 8 ++++---- .../amazon/jdbc/wrapper/ConnectionWrapper.java | 4 ++-- .../CustomEndpointMonitorImplTest.java | 4 ++-- .../plugin/dev/DeveloperConnectionPluginTest.java | 4 ++-- ...viceTest.java => CoreMonitorServiceImplTest.java} | 8 ++++---- 20 files changed, 51 insertions(+), 51 deletions(-) rename wrapper/src/main/java/software/amazon/jdbc/util/monitoring/{MonitorService.java => CoreMonitorService.java} (99%) rename wrapper/src/main/java/software/amazon/jdbc/util/monitoring/{DefaultMonitorService.java => CoreMonitorServiceImpl.java} (97%) rename wrapper/src/test/java/software/amazon/jdbc/util/monitoring/{DefaultMonitorServiceTest.java => CoreMonitorServiceImplTest.java} (98%) diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java index eb7c13518..56059e905 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java @@ -63,7 +63,7 @@ import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.connection.ConnectionService; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.GaugeCallable; import software.amazon.jdbc.util.telemetry.TelemetryContext; @@ -92,7 +92,7 @@ public class PluginBenchmarks { .host(TEST_HOST).port(TEST_PORT).build(); @Mock private StorageService mockStorageService; - @Mock private MonitorService mockMonitorService; + @Mock private CoreMonitorService mockMonitorService; @Mock private ConnectionService mockConnectionService; @Mock private PluginService mockPluginService; @Mock private TargetDriverDialect mockTargetDriverDialect; diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java index d0ec5c063..421c0c613 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java @@ -26,7 +26,7 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.connection.ConnectionService; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.wrapper.ConnectionWrapper; @@ -45,7 +45,7 @@ public TestConnectionWrapper( @NonNull final HostListProviderService hostListProviderService, @NonNull final PluginManagerService pluginManagerService, @NonNull final StorageService storageService, - @NonNull final MonitorService monitorService, + @NonNull final CoreMonitorService monitorService, @NonNull final ConnectionService connectionService) throws SQLException { super( diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index d796718d0..a277bbfe8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -65,7 +65,7 @@ import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.ServiceContainerImpl; import software.amazon.jdbc.util.StringUtils; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryContext; @@ -109,7 +109,7 @@ public class Driver implements java.sql.Driver { } private final StorageService storageService; - private final MonitorService monitorService; + private final CoreMonitorService monitorService; public Driver() { this(CoreServicesContainer.getInstance()); diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index 172ab210e..30128d8c2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -55,7 +55,7 @@ import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryContext; @@ -73,7 +73,7 @@ public class AwsWrapperDataSource implements DataSource, Referenceable, Serializ private static final String SERVER_PORT = "serverPort"; private final StorageService storageService; - private final MonitorService monitorService; + private final CoreMonitorService monitorService; static { try { diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 2dc9209d0..6d578108e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -57,7 +57,7 @@ import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.AbstractMonitor; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.Topology; @@ -113,7 +113,7 @@ public class ClusterTopologyMonitorImpl extends AbstractMonitor implements Clust public ClusterTopologyMonitorImpl( final String clusterId, final StorageService storageService, - final MonitorService monitorService, + final CoreMonitorService monitorService, final ConnectionService connectionService, final HostSpec initialHostSpec, final Properties properties, diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index 321fdd0e5..251bf9451 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -32,7 +32,7 @@ import software.amazon.jdbc.hostlistprovider.RdsHostListProvider; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.connection.ConnectionService; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.Topology; public class MonitoringRdsHostListProvider extends RdsHostListProvider @@ -51,7 +51,7 @@ public class MonitoringRdsHostListProvider extends RdsHostListProvider } protected final ServiceContainer serviceContainer; - protected final MonitorService monitorService; + protected final CoreMonitorService monitorService; protected final PluginService pluginService; protected final long highRefreshRateNano; protected final String writerTopologyQuery; diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java index 348ce3ed5..e21c85e19 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java @@ -29,7 +29,7 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.connection.ConnectionService; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; public class MultiAzClusterTopologyMonitorImpl extends ClusterTopologyMonitorImpl { @@ -42,7 +42,7 @@ public class MultiAzClusterTopologyMonitorImpl extends ClusterTopologyMonitorImp public MultiAzClusterTopologyMonitorImpl( final String clusterId, final StorageService storageService, - final MonitorService monitorService, + final CoreMonitorService monitorService, final ConnectionService connectionService, final HostSpec initialHostSpec, final Properties properties, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index 620845307..2ec35a672 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -33,7 +33,7 @@ import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.monitoring.AbstractMonitor; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; @@ -76,7 +76,7 @@ public class CustomEndpointMonitorImpl extends AbstractMonitor implements Custom * information. */ public CustomEndpointMonitorImpl( - MonitorService monitorService, + CoreMonitorService monitorService, StorageService storageService, TelemetryFactory telemetryFactory, HostSpec customEndpointHostSpec, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java index 1f9290012..305c0855b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java @@ -41,7 +41,7 @@ import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SubscribedMethodHelper; import software.amazon.jdbc.util.WrapperUtils; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -93,7 +93,7 @@ public class CustomEndpointPlugin extends AbstractConnectionPlugin { protected final ServiceContainer serviceContainer; protected final PluginService pluginService; protected final StorageService storageService; - protected final MonitorService monitorService; + protected final CoreMonitorService monitorService; protected final TelemetryFactory telemetryFactory; protected final Properties props; protected final RdsUtils rdsUtils = new RdsUtils(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java index b6eea8ae3..75ea0bcdd 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java @@ -18,8 +18,8 @@ import software.amazon.jdbc.util.events.BatchingEventPublisher; import software.amazon.jdbc.util.events.EventPublisher; -import software.amazon.jdbc.util.monitoring.MonitorService; -import software.amazon.jdbc.util.monitoring.DefaultMonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorServiceImpl; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; @@ -32,12 +32,12 @@ public class CoreServicesContainer { private final EventPublisher eventPublisher; private final StorageService storageService; - private final MonitorService monitorService; + private final CoreMonitorService monitorService; private CoreServicesContainer() { this.eventPublisher = new BatchingEventPublisher(); this.storageService = new StorageServiceImpl(eventPublisher); - this.monitorService = new DefaultMonitorService(eventPublisher); + this.monitorService = new CoreMonitorServiceImpl(eventPublisher); } public static CoreServicesContainer getInstance() { @@ -52,7 +52,7 @@ public StorageService getStorageService() { return storageService; } - public MonitorService getMonitorService() { + public CoreMonitorService getMonitorService() { return monitorService; } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java index bab4eaf59..57015b82c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java @@ -20,14 +20,14 @@ import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public interface ServiceContainer { StorageService getStorageService(); - MonitorService getMonitorService(); + CoreMonitorService getMonitorService(); TelemetryFactory getTelemetryFactory(); @@ -39,7 +39,7 @@ public interface ServiceContainer { PluginManagerService getPluginManagerService(); - void setMonitorService(MonitorService monitorService); + void setMonitorService(CoreMonitorService monitorService); void setStorageService(StorageService storageService); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java index e29ed8a9b..36c7eb411 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java @@ -20,13 +20,13 @@ import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class ServiceContainerImpl implements ServiceContainer { private StorageService storageService; - private MonitorService monitorService; + private CoreMonitorService monitorService; private TelemetryFactory telemetryFactory; private ConnectionPluginManager connectionPluginManager; private HostListProviderService hostListProviderService; @@ -35,7 +35,7 @@ public class ServiceContainerImpl implements ServiceContainer { public ServiceContainerImpl( StorageService storageService, - MonitorService monitorService, + CoreMonitorService monitorService, TelemetryFactory telemetryFactory, ConnectionPluginManager connectionPluginManager, HostListProviderService hostListProviderService, @@ -50,7 +50,7 @@ public ServiceContainerImpl( public ServiceContainerImpl( StorageService storageService, - MonitorService monitorService, + CoreMonitorService monitorService, TelemetryFactory telemetryFactory) { this.storageService = storageService; this.monitorService = monitorService; @@ -63,7 +63,7 @@ public StorageService getStorageService() { } @Override - public MonitorService getMonitorService() { + public CoreMonitorService getMonitorService() { return this.monitorService; } @@ -93,7 +93,7 @@ public PluginManagerService getPluginManagerService() { } @Override - public void setMonitorService(MonitorService monitorService) { + public void setMonitorService(CoreMonitorService monitorService) { this.monitorService = monitorService; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index 03cc29202..0b59222e1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -28,7 +28,7 @@ import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.ServiceContainerImpl; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -39,7 +39,7 @@ public class ConnectionServiceImpl implements ConnectionService { public ConnectionServiceImpl( StorageService storageService, - MonitorService monitorService, + CoreMonitorService monitorService, TelemetryFactory telemetryFactory, ConnectionProvider connectionProvider, String originalUrl, diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index 94593916b..0871674fc 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -32,7 +32,7 @@ public abstract class AbstractMonitor implements Monitor, Runnable { private static final Logger LOGGER = Logger.getLogger(AbstractMonitor.class.getName()); protected final AtomicBoolean stop = new AtomicBoolean(false); protected final long terminationTimeoutSec; - protected final MonitorService monitorService; + protected final CoreMonitorService monitorService; protected final ExecutorService monitorExecutor = Executors.newSingleThreadExecutor(runnableTarget -> { final Thread monitoringThread = new Thread(runnableTarget); monitoringThread.setDaemon(true); @@ -45,7 +45,7 @@ public abstract class AbstractMonitor implements Monitor, Runnable { protected long lastActivityTimestampNanos; protected MonitorState state; - protected AbstractMonitor(MonitorService monitorService, long terminationTimeoutSec) { + protected AbstractMonitor(CoreMonitorService monitorService, long terminationTimeoutSec) { this.monitorService = monitorService; this.terminationTimeoutSec = terminationTimeoutSec; this.lastActivityTimestampNanos = System.nanoTime(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/CoreMonitorService.java similarity index 99% rename from wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java rename to wrapper/src/main/java/software/amazon/jdbc/util/monitoring/CoreMonitorService.java index 41df2c567..d5fa8df67 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/CoreMonitorService.java @@ -25,7 +25,7 @@ import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -public interface MonitorService { +public interface CoreMonitorService { /** * Registers a new monitor type with the monitor service. This method needs to be called before adding new types of * monitors to the monitor service, so that the monitor service knows when to dispose of a monitor. Expected monitor diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/DefaultMonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImpl.java similarity index 97% rename from wrapper/src/main/java/software/amazon/jdbc/util/monitoring/DefaultMonitorService.java rename to wrapper/src/main/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImpl.java index 106077379..bf9c6856b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/DefaultMonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImpl.java @@ -52,8 +52,8 @@ import software.amazon.jdbc.util.storage.Topology; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -public class DefaultMonitorService implements MonitorService, EventSubscriber { - private static final Logger LOGGER = Logger.getLogger(DefaultMonitorService.class.getName()); +public class CoreMonitorServiceImpl implements CoreMonitorService, EventSubscriber { + private static final Logger LOGGER = Logger.getLogger(CoreMonitorServiceImpl.class.getName()); protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(1); protected static final Map, Supplier> defaultSuppliers; @@ -84,7 +84,7 @@ public class DefaultMonitorService implements MonitorService, EventSubscriber { })); - public DefaultMonitorService(EventPublisher publisher) { + public CoreMonitorServiceImpl(EventPublisher publisher) { this(DEFAULT_CLEANUP_INTERVAL_NANOS, publisher); } @@ -96,7 +96,7 @@ public DefaultMonitorService(EventPublisher publisher) { * nanoseconds. * @param publisher the publisher to subscribe to for data access events. */ - public DefaultMonitorService(long cleanupIntervalNanos, EventPublisher publisher) { + public CoreMonitorServiceImpl(long cleanupIntervalNanos, EventPublisher publisher) { this.publisher = publisher; this.publisher.subscribe(this, new HashSet<>(Collections.singletonList(DataAccessEvent.class))); initCleanupThread(cleanupIntervalNanos); diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index bd191709c..1672dcb62 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -58,7 +58,7 @@ import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; import software.amazon.jdbc.util.connection.ConnectionService; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -135,7 +135,7 @@ protected ConnectionWrapper( @NonNull final HostListProviderService hostListProviderService, @NonNull final PluginManagerService pluginManagerService, @NonNull final StorageService storageService, - @NonNull final MonitorService monitorService, + @NonNull final CoreMonitorService monitorService, @NonNull final ConnectionService connectionService) throws SQLException { diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java index 489420b15..5420416d3 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java @@ -48,13 +48,13 @@ import software.amazon.jdbc.HostSpecBuilder; import software.amazon.jdbc.hostavailability.HostAvailabilityStrategy; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class CustomEndpointMonitorImplTest { - @Mock private MonitorService mockMonitorService; + @Mock private CoreMonitorService mockMonitorService; @Mock private StorageService mockStorageService; @Mock private BiFunction mockRdsClientFunc; @Mock private RdsClient mockRdsClient; diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java index 5f2e45e5e..8654c7bee 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java @@ -44,7 +44,7 @@ import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.ServiceContainerImpl; -import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -54,7 +54,7 @@ public class DeveloperConnectionPluginTest { private ServiceContainer serviceContainer; @Mock StorageService mockStorageService; - @Mock MonitorService mockMonitorService; + @Mock CoreMonitorService mockMonitorService; @Mock ConnectionProvider mockConnectionProvider; @Mock Connection mockConnection; @Mock ConnectionPluginManager mockConnectionPluginManager; diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/DefaultMonitorServiceTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImplTest.java similarity index 98% rename from wrapper/src/test/java/software/amazon/jdbc/util/monitoring/DefaultMonitorServiceTest.java rename to wrapper/src/test/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImplTest.java index c284eaa17..dad22ecba 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/DefaultMonitorServiceTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImplTest.java @@ -39,19 +39,19 @@ import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -class DefaultMonitorServiceTest { +class CoreMonitorServiceImplTest { @Mock StorageService storageService; @Mock TelemetryFactory telemetryFactory; @Mock TargetDriverDialect targetDriverDialect; @Mock Dialect dbDialect; @Mock EventPublisher publisher; - DefaultMonitorService monitorService; + CoreMonitorServiceImpl monitorService; private AutoCloseable closeable; @BeforeEach void setUp() { closeable = MockitoAnnotations.openMocks(this); - monitorService = new DefaultMonitorService(publisher) { + monitorService = new CoreMonitorServiceImpl(publisher) { @Override protected void initCleanupThread(long cleanupIntervalNanos) { // Do nothing @@ -281,7 +281,7 @@ public void testStopAndRemove() throws SQLException, InterruptedException { static class NoOpMonitor extends AbstractMonitor { protected NoOpMonitor( - MonitorService monitorService, + CoreMonitorService monitorService, long terminationTimeoutSec) { super(monitorService, terminationTimeoutSec); } From 52c2fe6ba7e4279c3c214d1114d5c53285274b75 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 16 May 2025 14:37:19 -0700 Subject: [PATCH 113/149] Remove monitorService field from AbstractMonitor --- .../monitoring/ClusterTopologyMonitorImpl.java | 4 +--- .../monitoring/MonitoringRdsHostListProvider.java | 1 - .../monitoring/MultiAzClusterTopologyMonitorImpl.java | 1 - .../plugin/customendpoint/CustomEndpointMonitorImpl.java | 2 +- .../amazon/jdbc/util/monitoring/AbstractMonitor.java | 5 +---- .../jdbc/util/monitoring/CoreMonitorServiceImplTest.java | 2 +- 6 files changed, 4 insertions(+), 11 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 6d578108e..fa0052818 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -57,7 +57,6 @@ import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.AbstractMonitor; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.Topology; @@ -113,7 +112,6 @@ public class ClusterTopologyMonitorImpl extends AbstractMonitor implements Clust public ClusterTopologyMonitorImpl( final String clusterId, final StorageService storageService, - final CoreMonitorService monitorService, final ConnectionService connectionService, final HostSpec initialHostSpec, final Properties properties, @@ -125,7 +123,7 @@ public ClusterTopologyMonitorImpl( final String topologyQuery, final String writerTopologyQuery, final String nodeIdQuery) { - super(monitorService, 30); + super(30); this.clusterId = clusterId; this.storageService = storageService; diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index 251bf9451..28e5d52f0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -96,7 +96,6 @@ protected ClusterTopologyMonitor initMonitor() throws SQLException { (ConnectionService connectionService, PluginService monitorPluginService) -> new ClusterTopologyMonitorImpl( this.clusterId, this.storageService, - this.monitorService, connectionService, this.initialHostSpec, this.properties, diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java index e21c85e19..e93446ff6 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java @@ -59,7 +59,6 @@ public MultiAzClusterTopologyMonitorImpl( super( clusterId, storageService, - monitorService, connectionService, initialHostSpec, properties, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index 2ec35a672..6d3dcac4d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -84,7 +84,7 @@ public CustomEndpointMonitorImpl( Region region, long refreshRateNano, BiFunction rdsClientFunc) { - super(monitorService, 30); + super(30); this.storageService = storageService; this.customEndpointHostSpec = customEndpointHostSpec; this.endpointIdentifier = endpointIdentifier; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index 0871674fc..4fc061f09 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -32,7 +32,6 @@ public abstract class AbstractMonitor implements Monitor, Runnable { private static final Logger LOGGER = Logger.getLogger(AbstractMonitor.class.getName()); protected final AtomicBoolean stop = new AtomicBoolean(false); protected final long terminationTimeoutSec; - protected final CoreMonitorService monitorService; protected final ExecutorService monitorExecutor = Executors.newSingleThreadExecutor(runnableTarget -> { final Thread monitoringThread = new Thread(runnableTarget); monitoringThread.setDaemon(true); @@ -45,8 +44,7 @@ public abstract class AbstractMonitor implements Monitor, Runnable { protected long lastActivityTimestampNanos; protected MonitorState state; - protected AbstractMonitor(CoreMonitorService monitorService, long terminationTimeoutSec) { - this.monitorService = monitorService; + protected AbstractMonitor(long terminationTimeoutSec) { this.terminationTimeoutSec = terminationTimeoutSec; this.lastActivityTimestampNanos = System.nanoTime(); } @@ -72,7 +70,6 @@ public void run() { } catch (Exception e) { LOGGER.fine(Messages.get("AbstractMonitor.unexpectedError", new Object[] {this, e})); this.state = MonitorState.ERROR; - // monitorService.reportMonitorError(this, e); } } diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImplTest.java index dad22ecba..c8fe3c7bc 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImplTest.java @@ -283,7 +283,7 @@ static class NoOpMonitor extends AbstractMonitor { protected NoOpMonitor( CoreMonitorService monitorService, long terminationTimeoutSec) { - super(monitorService, terminationTimeoutSec); + super(terminationTimeoutSec); } @Override From e68845c807c82bedaf00326405762ecb9cbb1c35 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Fri, 16 May 2025 15:28:36 -0700 Subject: [PATCH 114/149] Only implement ServiceContainerPluginFactory where immediately required --- .../efm/HostMonitoringConnectionPlugin.java | 12 ++++----- ...HostMonitoringConnectionPluginFactory.java | 15 +++-------- .../amazon/jdbc/plugin/efm/MonitorImpl.java | 9 +++---- .../jdbc/plugin/efm/MonitorServiceImpl.java | 14 +++++----- .../plugin/efm/MonitorThreadContainer.java | 3 ++- .../efm2/HostMonitoringConnectionPlugin.java | 12 ++++----- ...HostMonitoringConnectionPluginFactory.java | 15 +++-------- .../amazon/jdbc/plugin/efm2/MonitorImpl.java | 23 ++++++++-------- .../jdbc/plugin/efm2/MonitorServiceImpl.java | 15 +++++------ .../ClusterAwareReaderFailoverHandler.java | 25 +++++++++-------- .../ClusterAwareWriterFailoverHandler.java | 9 +++---- .../failover/FailoverConnectionPlugin.java | 17 +++++------- .../FailoverConnectionPluginFactory.java | 16 +++-------- .../limitless/LimitlessConnectionPlugin.java | 11 ++++---- .../LimitlessConnectionPluginFactory.java | 15 +++-------- .../limitless/LimitlessRouterMonitor.java | 10 +++---- .../limitless/LimitlessRouterServiceImpl.java | 11 ++++---- .../FastestResponseStrategyPlugin.java | 11 ++++---- .../FastestResponseStrategyPluginFactory.java | 16 +++-------- .../HostResponseTimeServiceImpl.java | 27 +++++++++++-------- .../NodeResponseTimeMonitor.java | 9 +++---- .../HostMonitoringConnectionPluginTest.java | 7 ++--- .../jdbc/plugin/efm/MonitorImplTest.java | 11 +++----- .../plugin/efm/MonitorServiceImplTest.java | 7 ++--- ...ultiThreadedDefaultMonitorServiceTest.java | 7 ++--- ...ClusterAwareReaderFailoverHandlerTest.java | 23 +++++++--------- ...ClusterAwareWriterFailoverHandlerTest.java | 17 +++++------- .../FailoverConnectionPluginTest.java | 6 +---- .../LimitlessConnectionPluginTest.java | 14 +++------- 29 files changed, 155 insertions(+), 232 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPlugin.java index fffcae2a2..9a04ea849 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPlugin.java @@ -39,7 +39,6 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SubscribedMethodHelper; /** @@ -94,20 +93,21 @@ public class HostMonitoringConnectionPlugin extends AbstractConnectionPlugin /** * Initialize the node monitoring plugin. * - * @param serviceContainer The service container for the services required by this class. + * @param pluginService A service allowing the plugin to retrieve the current active connection + * and its connection settings. * @param properties The property set used to initialize the active connection. */ public HostMonitoringConnectionPlugin( - final @NonNull ServiceContainer serviceContainer, final @NonNull Properties properties) { - this(serviceContainer, properties, () -> new MonitorServiceImpl(serviceContainer), new RdsUtils()); + final @NonNull PluginService pluginService, final @NonNull Properties properties) { + this(pluginService, properties, () -> new MonitorServiceImpl(pluginService), new RdsUtils()); } HostMonitoringConnectionPlugin( - final @NonNull ServiceContainer serviceContainer, + final @NonNull PluginService pluginService, final @NonNull Properties properties, final @NonNull Supplier monitorServiceSupplier, final RdsUtils rdsHelper) { - this.pluginService = serviceContainer.getPluginService(); + this.pluginService = pluginService; this.properties = properties; this.monitorServiceSupplier = monitorServiceSupplier; this.rdsHelper = rdsHelper; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginFactory.java index b0f78391c..d4c8d18ae 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginFactory.java @@ -18,22 +18,13 @@ import java.util.Properties; import software.amazon.jdbc.ConnectionPlugin; +import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.ServiceContainerPluginFactory; -import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; /** Class initializing a {@link HostMonitoringConnectionPlugin}. */ -public class HostMonitoringConnectionPluginFactory implements ServiceContainerPluginFactory { +public class HostMonitoringConnectionPluginFactory implements ConnectionPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - throw new UnsupportedOperationException( - Messages.get( - "ServiceContainerPluginFactory.serviceContainerRequired", new Object[] {"HostMonitoringConnectionPlugin"})); - } - - @Override - public ConnectionPlugin getInstance(final ServiceContainer serviceContainer, final Properties props) { - return new HostMonitoringConnectionPlugin(serviceContainer, props); + return new HostMonitoringConnectionPlugin(pluginService, props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java index bc0dfdc0c..ac53b2ca1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java @@ -29,7 +29,6 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; @@ -78,7 +77,7 @@ static class ConnectionStatus { /** * Store the monitoring configuration for a connection. * - * @param serviceContainer The service container for the services required by this class. + * @param pluginService A service for creating new connections. * @param hostSpec The {@link HostSpec} of the server this {@link MonitorImpl} * instance is monitoring. * @param properties The {@link Properties} containing additional monitoring @@ -90,13 +89,13 @@ static class ConnectionStatus { * that initialized this class. */ public MonitorImpl( - final @NonNull ServiceContainer serviceContainer, + final @NonNull PluginService pluginService, @NonNull final HostSpec hostSpec, @NonNull final Properties properties, final long monitorDisposalTimeMillis, @NonNull final MonitorThreadContainer threadContainer) { - this.pluginService = serviceContainer.getPluginService(); - this.telemetryFactory = serviceContainer.getTelemetryFactory(); + this.pluginService = pluginService; + this.telemetryFactory = pluginService.getTelemetryFactory(); this.hostSpec = hostSpec; this.properties = properties; this.monitorDisposalTimeMillis = monitorDisposalTimeMillis; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorServiceImpl.java index ae20645e9..2763f8a34 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorServiceImpl.java @@ -26,8 +26,8 @@ import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.AwsWrapperProperty; import software.amazon.jdbc.HostSpec; +import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -45,6 +45,7 @@ public class MonitorServiceImpl implements MonitorService { "600000", // 10min "Interval in milliseconds for a monitor to be considered inactive and to be disposed."); + private final PluginService pluginService; private MonitorThreadContainer threadContainer; final MonitorInitializer monitorInitializer; @@ -53,12 +54,12 @@ public class MonitorServiceImpl implements MonitorService { final TelemetryFactory telemetryFactory; final TelemetryCounter abortedConnectionsCounter; - public MonitorServiceImpl(final @NonNull ServiceContainer serviceContainer) { + public MonitorServiceImpl(final @NonNull PluginService pluginService) { this( - serviceContainer, + pluginService, (hostSpec, properties, monitorService) -> new MonitorImpl( - serviceContainer, + pluginService, hostSpec, properties, MONITOR_DISPOSAL_TIME_MS.getLong(properties), @@ -73,10 +74,11 @@ public MonitorServiceImpl(final @NonNull ServiceContainer serviceContainer) { } MonitorServiceImpl( - final ServiceContainer serviceContainer, + final PluginService pluginService, final MonitorInitializer monitorInitializer, final ExecutorServiceInitializer executorServiceInitializer) { - this.telemetryFactory = serviceContainer.getTelemetryFactory(); + this.pluginService = pluginService; + this.telemetryFactory = pluginService.getTelemetryFactory(); this.abortedConnectionsCounter = telemetryFactory.createCounter("efm.connections.aborted"); this.monitorInitializer = monitorInitializer; this.threadContainer = MonitorThreadContainer.getInstance(executorServiceInitializer); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorThreadContainer.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorThreadContainer.java index 3dbaea346..4ead2ebdb 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorThreadContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorThreadContainer.java @@ -35,8 +35,9 @@ public class MonitorThreadContainer { private static MonitorThreadContainer singleton = null; - private final Map monitorMap = new ConcurrentHashMap<>(); private final Map> tasksMap = new ConcurrentHashMap<>(); + // TODO: remove monitorMap and threadPool and submit monitors to MonitorService instead + private final Map monitorMap = new ConcurrentHashMap<>(); private final ExecutorService threadPool; private static final ReentrantLock LOCK_OBJECT = new ReentrantLock(); private static final ReentrantLock MONITOR_LOCK_OBJECT = new ReentrantLock(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPlugin.java index f864af252..c7d3d472c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPlugin.java @@ -38,7 +38,6 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SubscribedMethodHelper; /** @@ -93,20 +92,21 @@ public class HostMonitoringConnectionPlugin extends AbstractConnectionPlugin /** * Initialize the node monitoring plugin. * - * @param serviceContainer The service container for the services required by this class. + * @param pluginService A service allowing the plugin to retrieve the current active connection + * and its connection settings. * @param properties The property set used to initialize the active connection. */ public HostMonitoringConnectionPlugin( - final @NonNull ServiceContainer serviceContainer, final @NonNull Properties properties) { - this(serviceContainer, properties, () -> new MonitorServiceImpl(serviceContainer), new RdsUtils()); + final @NonNull PluginService pluginService, final @NonNull Properties properties) { + this(pluginService, properties, () -> new MonitorServiceImpl(pluginService), new RdsUtils()); } HostMonitoringConnectionPlugin( - final @NonNull ServiceContainer serviceContainer, + final @NonNull PluginService pluginService, final @NonNull Properties properties, final @NonNull Supplier monitorServiceSupplier, final RdsUtils rdsHelper) { - this.pluginService = serviceContainer.getPluginService(); + this.pluginService = pluginService; this.properties = properties; this.monitorServiceSupplier = monitorServiceSupplier; this.rdsHelper = rdsHelper; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPluginFactory.java index bbbc95dc5..0dfdb79ca 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPluginFactory.java @@ -18,22 +18,13 @@ import java.util.Properties; import software.amazon.jdbc.ConnectionPlugin; +import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.ServiceContainerPluginFactory; -import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; /** Class initializing a {@link HostMonitoringConnectionPlugin}. */ -public class HostMonitoringConnectionPluginFactory implements ServiceContainerPluginFactory { +public class HostMonitoringConnectionPluginFactory implements ConnectionPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - throw new UnsupportedOperationException( - Messages.get( - "ServiceContainerPluginFactory.serviceContainerRequired", new Object[] {"HostMonitoringConnectionPlugin"})); - } - - @Override - public ConnectionPlugin getInstance(final ServiceContainer serviceContainer, final Properties props) { - return new HostMonitoringConnectionPlugin(serviceContainer, props); + return new HostMonitoringConnectionPlugin(pluginService, props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java index 95792eb52..31bca34b0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java @@ -39,7 +39,6 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; @@ -68,6 +67,7 @@ public class MonitorImpl implements Monitor { private final HostSpec hostSpec; private final AtomicBoolean stopped = new AtomicBoolean(false); private Connection monitoringConn = null; + // TODO: remove and submit monitors to MonitorService instead private final ExecutorService threadPool = Executors.newFixedThreadPool(2, runnableTarget -> { final Thread monitoringThread = new Thread(runnableTarget); monitoringThread.setDaemon(true); @@ -91,17 +91,18 @@ public class MonitorImpl implements Monitor { /** * Store the monitoring configuration for a connection. * - * @param serviceContainer The service container for the services required by this class. - * @param hostSpec The {@link HostSpec} of the server this {@link MonitorImpl} instance is - * monitoring. - * @param properties The {@link Properties} containing additional monitoring configuration. - * @param failureDetectionTimeMillis A failure detection time in millis. + * @param pluginService A service for creating new connections. + * @param hostSpec The {@link HostSpec} of the server this {@link MonitorImpl} + * instance is monitoring. + * @param properties The {@link Properties} containing additional monitoring + * configuration. + * @param failureDetectionTimeMillis A failure detection time in millis. * @param failureDetectionIntervalMillis A failure detection interval in millis. - * @param failureDetectionCount A failure detection count. - * @param abortedConnectionsCounter Aborted connection telemetry counter. + * @param failureDetectionCount A failure detection count. + * @param abortedConnectionsCounter Aborted connection telemetry counter. */ public MonitorImpl( - final @NonNull ServiceContainer serviceContainer, + final @NonNull PluginService pluginService, final @NonNull HostSpec hostSpec, final @NonNull Properties properties, final int failureDetectionTimeMillis, @@ -109,8 +110,8 @@ public MonitorImpl( final int failureDetectionCount, final TelemetryCounter abortedConnectionsCounter) { - this.pluginService = serviceContainer.getPluginService(); - this.telemetryFactory = serviceContainer.getTelemetryFactory(); + this.pluginService = pluginService; + this.telemetryFactory = pluginService.getTelemetryFactory(); this.hostSpec = hostSpec; this.properties = properties; this.failureDetectionTimeNano = TimeUnit.MILLISECONDS.toNanos(failureDetectionTimeMillis); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java index 9399eb398..556152ba0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java @@ -28,7 +28,6 @@ import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.storage.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -49,7 +48,7 @@ public class MonitorServiceImpl implements MonitorService { protected static final long CACHE_CLEANUP_NANO = TimeUnit.MINUTES.toNanos(1); protected static final Executor ABORT_EXECUTOR = Executors.newSingleThreadExecutor(); - + // TODO: remove and submit monitors to MonitorService instead protected static final SlidingExpirationCacheWithCleanupThread monitors = new SlidingExpirationCacheWithCleanupThread<>( Monitor::canDispose, @@ -67,9 +66,9 @@ public class MonitorServiceImpl implements MonitorService { protected final TelemetryFactory telemetryFactory; protected final TelemetryCounter abortedConnectionsCounter; - public MonitorServiceImpl(final @NonNull ServiceContainer serviceContainer) { + public MonitorServiceImpl(final @NonNull PluginService pluginService) { this( - serviceContainer, + pluginService, (hostSpec, properties, failureDetectionTimeMillis, @@ -77,7 +76,7 @@ public MonitorServiceImpl(final @NonNull ServiceContainer serviceContainer) { failureDetectionCount, abortedConnectionsCounter) -> new MonitorImpl( - serviceContainer, + pluginService, hostSpec, properties, failureDetectionTimeMillis, @@ -87,10 +86,10 @@ public MonitorServiceImpl(final @NonNull ServiceContainer serviceContainer) { } MonitorServiceImpl( - final @NonNull ServiceContainer serviceContainer, + final @NonNull PluginService pluginService, final @NonNull MonitorInitializer monitorInitializer) { - this.pluginService = serviceContainer.getPluginService(); - this.telemetryFactory = serviceContainer.getTelemetryFactory(); + this.pluginService = pluginService; + this.telemetryFactory = pluginService.getTelemetryFactory(); this.abortedConnectionsCounter = telemetryFactory.createCounter("efm2.connections.aborted"); this.monitorInitializer = monitorInitializer; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java index 256ca4e25..37043cacd 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java @@ -38,7 +38,6 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; /** @@ -68,14 +67,14 @@ public class ClusterAwareReaderFailoverHandler implements ReaderFailoverHandler /** * ClusterAwareReaderFailoverHandler constructor. * - * @param serviceContainer The service container for the services required by this class. + * @param pluginService A provider for creating new connections. * @param initialConnectionProps The initial connection properties to copy over to the new reader. */ public ClusterAwareReaderFailoverHandler( - final ServiceContainer serviceContainer, + final PluginService pluginService, final Properties initialConnectionProps) { this( - serviceContainer, + pluginService, initialConnectionProps, DEFAULT_FAILOVER_TIMEOUT, DEFAULT_READER_CONNECT_TIMEOUT, @@ -85,20 +84,20 @@ public ClusterAwareReaderFailoverHandler( /** * ClusterAwareReaderFailoverHandler constructor. * - * @param serviceContainer The service container for the services required by this class. - * @param initialConnectionProps The initial connection properties to copy over to the new reader. - * @param maxFailoverTimeoutMs Maximum allowed time for the entire reader failover process. - * @param timeoutMs Maximum allowed time in milliseconds for each reader connection attempt during - * the reader failover process. + * @param pluginService A provider for creating new connections. + * @param initialConnectionProps The initial connection properties to copy over to the new reader. + * @param maxFailoverTimeoutMs Maximum allowed time for the entire reader failover process. + * @param timeoutMs Maximum allowed time in milliseconds for each reader connection attempt during + * the reader failover process. * @param isStrictReaderRequired When true, it disables adding a writer to a list of nodes to connect */ public ClusterAwareReaderFailoverHandler( - final ServiceContainer serviceContainer, + final PluginService pluginService, final Properties initialConnectionProps, final int maxFailoverTimeoutMs, final int timeoutMs, final boolean isStrictReaderRequired) { - this.pluginService = serviceContainer.getPluginService(); + this.pluginService = pluginService; this.initialConnectionProps = initialConnectionProps; this.maxFailoverTimeoutMs = maxFailoverTimeoutMs; this.timeoutMs = timeoutMs; @@ -403,7 +402,7 @@ public ReaderFailoverResult call() { LOGGER.fine( Messages.get( "ClusterAwareReaderFailoverHandler.readerRequired", - new Object[] {this.newHost.getUrl(), role})); + new Object[]{ this.newHost.getUrl(), role })); try { conn.close(); @@ -414,7 +413,7 @@ public ReaderFailoverResult call() { return FAILED_READER_FAILOVER_RESULT; } } catch (SQLException e) { - LOGGER.fine(Messages.get("ClusterAwareReaderFailoverHandler.errorGettingHostRole", new Object[] {e})); + LOGGER.fine(Messages.get("ClusterAwareReaderFailoverHandler.errorGettingHostRole", new Object[]{e})); try { conn.close(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java index 72ec31bb7..b065dc927 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandler.java @@ -37,7 +37,6 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; /** @@ -62,23 +61,23 @@ public class ClusterAwareWriterFailoverHandler implements WriterFailoverHandler new WriterFailoverResult(false, false, null, null, "None"); public ClusterAwareWriterFailoverHandler( - final ServiceContainer serviceContainer, + final PluginService pluginService, final ReaderFailoverHandler readerFailoverHandler, final Properties initialConnectionProps) { - this.pluginService = serviceContainer.getPluginService(); + this.pluginService = pluginService; this.readerFailoverHandler = readerFailoverHandler; this.initialConnectionProps = initialConnectionProps; } public ClusterAwareWriterFailoverHandler( - final ServiceContainer serviceContainer, + final PluginService pluginService, final ReaderFailoverHandler readerFailoverHandler, final Properties initialConnectionProps, final int failoverTimeoutMs, final int readTopologyIntervalMs, final int reconnectWriterIntervalMs) { this( - serviceContainer, + pluginService, readerFailoverHandler, initialConnectionProps); this.maxFailoverTimeoutMs = failoverTimeoutMs; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPlugin.java index fdf81c3f6..feafda3f8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPlugin.java @@ -47,7 +47,6 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.SubscribedMethodHelper; import software.amazon.jdbc.util.Utils; @@ -89,8 +88,7 @@ public class FailoverConnectionPlugin extends AbstractConnectionPlugin { static final String METHOD_CLOSE = "Connection.close"; static final String METHOD_IS_CLOSED = "Connection.isClosed"; - protected final ServiceContainer serviceContainer; - protected final PluginService pluginService; + private final PluginService pluginService; protected final Properties properties; protected boolean enableFailoverSetting; protected boolean enableConnectFailover; @@ -184,16 +182,15 @@ public class FailoverConnectionPlugin extends AbstractConnectionPlugin { PropertyDefinition.registerPluginProperties(FailoverConnectionPlugin.class); } - public FailoverConnectionPlugin(final ServiceContainer serviceContainer, final Properties properties) { - this(serviceContainer, properties, new RdsUtils()); + public FailoverConnectionPlugin(final PluginService pluginService, final Properties properties) { + this(pluginService, properties, new RdsUtils()); } FailoverConnectionPlugin( - final ServiceContainer serviceContainer, + final PluginService pluginService, final Properties properties, final RdsUtils rdsHelper) { - this.serviceContainer = serviceContainer; - this.pluginService = serviceContainer.getPluginService(); + this.pluginService = pluginService; this.properties = properties; this.rdsHelper = rdsHelper; @@ -290,14 +287,14 @@ public void initHostProvider( initHostProviderFunc, () -> new ClusterAwareReaderFailoverHandler( - this.serviceContainer, + this.pluginService, this.properties, this.failoverTimeoutMsSetting, this.failoverReaderConnectTimeoutMsSetting, this.failoverMode == FailoverMode.STRICT_READER), () -> new ClusterAwareWriterFailoverHandler( - this.serviceContainer, + this.pluginService, this.readerFailoverHandler, this.properties, this.failoverTimeoutMsSetting, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginFactory.java index bf95aece3..75d445710 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginFactory.java @@ -18,21 +18,13 @@ import java.util.Properties; import software.amazon.jdbc.ConnectionPlugin; +import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.ServiceContainerPluginFactory; -import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; -public class FailoverConnectionPluginFactory implements ServiceContainerPluginFactory { - @Override - public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - throw new UnsupportedOperationException( - Messages.get( - "ServiceContainerPluginFactory.serviceContainerRequired", new Object[] {"FailoverConnectionPlugin"})); - } +public class FailoverConnectionPluginFactory implements ConnectionPluginFactory { @Override - public ConnectionPlugin getInstance(final ServiceContainer serviceContainer, final Properties props) { - return new FailoverConnectionPlugin(serviceContainer, props); + public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { + return new FailoverConnectionPlugin(pluginService, props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPlugin.java index d5c14d932..23a1aa8be 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPlugin.java @@ -34,7 +34,6 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.plugin.AbstractConnectionPlugin; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; public class LimitlessConnectionPlugin extends AbstractConnectionPlugin { @@ -83,15 +82,17 @@ public Set getSubscribedMethods() { return subscribedMethods; } - public LimitlessConnectionPlugin(final ServiceContainer serviceContainer, final @NonNull Properties properties) { - this(serviceContainer, properties, () -> new LimitlessRouterServiceImpl(serviceContainer)); + public LimitlessConnectionPlugin(final PluginService pluginService, final @NonNull Properties properties) { + this(pluginService, + properties, + () -> new LimitlessRouterServiceImpl(pluginService)); } public LimitlessConnectionPlugin( - final @NonNull ServiceContainer serviceContainer, + final PluginService pluginService, final @NonNull Properties properties, final @NonNull Supplier limitlessRouterServiceSupplier) { - this.pluginService = serviceContainer.getPluginService(); + this.pluginService = pluginService; this.properties = properties; this.limitlessRouterServiceSupplier = limitlessRouterServiceSupplier; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginFactory.java index 4793f64c6..27542f9ed 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginFactory.java @@ -18,21 +18,12 @@ import java.util.Properties; import software.amazon.jdbc.ConnectionPlugin; +import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.ServiceContainerPluginFactory; -import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; -public class LimitlessConnectionPluginFactory implements ServiceContainerPluginFactory { +public class LimitlessConnectionPluginFactory implements ConnectionPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - throw new UnsupportedOperationException( - Messages.get( - "ServiceContainerPluginFactory.serviceContainerRequired", new Object[] {"LimitlessConnectionPlugin"})); - } - - @Override - public ConnectionPlugin getInstance(final ServiceContainer serviceContainer, final Properties props) { - return new LimitlessConnectionPlugin(serviceContainer, props); + return new LimitlessConnectionPlugin(pluginService, props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java index 51361e052..3be0e7c03 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java @@ -31,7 +31,6 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.telemetry.TelemetryContext; @@ -49,11 +48,12 @@ public class LimitlessRouterMonitor implements AutoCloseable, Runnable { protected final SlidingExpirationCacheWithCleanupThread> limitlessRouterCache; protected final String limitlessRouterCacheKey; protected final @NonNull Properties props; - protected final PluginService pluginService; + protected final @NonNull PluginService pluginService; protected final LimitlessQueryHelper queryHelper; protected final TelemetryFactory telemetryFactory; protected Connection monitoringConn = null; + // TODO: remove and submit monitors to MonitorService instead private final ExecutorService threadPool = Executors.newFixedThreadPool(1, runnableTarget -> { final Thread monitoringThread = new Thread(runnableTarget); monitoringThread.setDaemon(true); @@ -63,12 +63,13 @@ public class LimitlessRouterMonitor implements AutoCloseable, Runnable { private final AtomicBoolean stopped = new AtomicBoolean(false); public LimitlessRouterMonitor( - final @NonNull ServiceContainer serviceContainer, + final @NonNull PluginService pluginService, final @NonNull HostSpec hostSpec, final @NonNull SlidingExpirationCacheWithCleanupThread> limitlessRouterCache, final @NonNull String limitlessRouterCacheKey, final @NonNull Properties props, final int intervalMs) { + this.pluginService = pluginService; this.hostSpec = hostSpec; this.limitlessRouterCache = limitlessRouterCache; this.limitlessRouterCacheKey = limitlessRouterCacheKey; @@ -85,8 +86,7 @@ public LimitlessRouterMonitor( this.props.setProperty(LimitlessConnectionPlugin.WAIT_FOR_ROUTER_INFO.name, "false"); this.intervalMs = intervalMs; - this.pluginService = serviceContainer.getPluginService(); - this.telemetryFactory = serviceContainer.getTelemetryFactory(); + this.telemetryFactory = this.pluginService.getTelemetryFactory(); this.queryHelper = new LimitlessQueryHelper(this.pluginService); this.threadPool.submit(this); this.threadPool.shutdown(); // No more task are accepted by pool. diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java index b01d25afd..ef8a88364 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterServiceImpl.java @@ -33,7 +33,6 @@ import software.amazon.jdbc.RoundRobinHostSelector; import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.wrapper.HighestWeightHostSelector; @@ -51,7 +50,7 @@ public class LimitlessRouterServiceImpl implements LimitlessRouterService { protected final PluginService pluginService; protected final LimitlessQueryHelper queryHelper; protected final LimitlessRouterMonitorInitializer limitlessRouterMonitorInitializer; - + // TODO: remove and submit monitors to MonitorService instead protected static final SlidingExpirationCacheWithCleanupThread limitlessRouterMonitors = new SlidingExpirationCacheWithCleanupThread<>( limitlessRouterMonitor -> true, @@ -78,22 +77,22 @@ public class LimitlessRouterServiceImpl implements LimitlessRouterService { PropertyDefinition.registerPluginProperties(LimitlessRouterServiceImpl.class); } - public LimitlessRouterServiceImpl(final @NonNull ServiceContainer serviceContainer) { + public LimitlessRouterServiceImpl(final @NonNull PluginService pluginService) { this( - serviceContainer.getPluginService(), + pluginService, (hostSpec, routerCache, routerCacheKey, props, intervalMs) -> new LimitlessRouterMonitor( - serviceContainer, + pluginService, hostSpec, routerCache, routerCacheKey, props, intervalMs), - new LimitlessQueryHelper(serviceContainer.getPluginService())); + new LimitlessQueryHelper(pluginService)); } public LimitlessRouterServiceImpl( diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java index 460351be4..6e7f39312 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPlugin.java @@ -39,7 +39,6 @@ import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.RandomHostSelector; import software.amazon.jdbc.plugin.AbstractConnectionPlugin; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.storage.CacheMap; public class FastestResponseStrategyPlugin extends AbstractConnectionPlugin { @@ -81,21 +80,21 @@ public class FastestResponseStrategyPlugin extends AbstractConnectionPlugin { PropertyDefinition.registerPluginProperties("frt-"); } - public FastestResponseStrategyPlugin(final ServiceContainer serviceContainer, final @NonNull Properties properties) { - this(serviceContainer, + public FastestResponseStrategyPlugin(final PluginService pluginService, final @NonNull Properties properties) { + this(pluginService, properties, new HostResponseTimeServiceImpl( - serviceContainer, + pluginService, properties, RESPONSE_MEASUREMENT_INTERVAL_MILLIS.getInteger(properties))); } public FastestResponseStrategyPlugin( - final ServiceContainer serviceContainer, + final PluginService pluginService, final @NonNull Properties properties, final @NonNull HostResponseTimeService hostResponseTimeService) { - this.pluginService = serviceContainer.getPluginService(); + this.pluginService = pluginService; this.properties = properties; this.hostResponseTimeService = hostResponseTimeService; this.cacheExpirationNano = TimeUnit.MILLISECONDS.toNanos( diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPluginFactory.java index ceb6e9840..87a1d766b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/FastestResponseStrategyPluginFactory.java @@ -18,21 +18,13 @@ import java.util.Properties; import software.amazon.jdbc.ConnectionPlugin; +import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.ServiceContainerPluginFactory; -import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; -public class FastestResponseStrategyPluginFactory implements ServiceContainerPluginFactory { - @Override - public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - throw new UnsupportedOperationException( - Messages.get( - "ServiceContainerPluginFactory.serviceContainerRequired", new Object[] {"FastestResponseStrategyPlugin"})); - } +public class FastestResponseStrategyPluginFactory implements ConnectionPluginFactory { @Override - public ConnectionPlugin getInstance(final ServiceContainer serviceContainer, final Properties props) { - return new FastestResponseStrategyPlugin(serviceContainer, props); + public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { + return new FastestResponseStrategyPlugin(pluginService, props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/HostResponseTimeServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/HostResponseTimeServiceImpl.java index acfc3ac73..c46d31c8d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/HostResponseTimeServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/HostResponseTimeServiceImpl.java @@ -26,19 +26,20 @@ import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.storage.SlidingExpirationCacheWithCleanupThread; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryGauge; public class HostResponseTimeServiceImpl implements HostResponseTimeService { - private static final Logger LOGGER = Logger.getLogger(HostResponseTimeServiceImpl.class.getName()); + private static final Logger LOGGER = + Logger.getLogger(HostResponseTimeServiceImpl.class.getName()); protected static final long CACHE_EXPIRATION_NANO = TimeUnit.MINUTES.toNanos(10); protected static final long CACHE_CLEANUP_NANO = TimeUnit.MINUTES.toNanos(1); - protected static final ReentrantLock cacheLock = new ReentrantLock(); + // TODO: remove and submit monitors to MonitorService instead protected static final SlidingExpirationCacheWithCleanupThread monitoringNodes = new SlidingExpirationCacheWithCleanupThread<>( (monitor) -> true, @@ -50,24 +51,28 @@ public class HostResponseTimeServiceImpl implements HostResponseTimeService { } }, CACHE_CLEANUP_NANO); + protected static final ReentrantLock cacheLock = new ReentrantLock(); + + protected int intervalMs; + + protected List hosts = new ArrayList<>(); + protected final @NonNull PluginService pluginService; - protected final @NonNull ServiceContainer serviceContainer; protected final @NonNull Properties props; + protected final TelemetryFactory telemetryFactory; private final TelemetryGauge nodeCountGauge; - protected List hosts = new ArrayList<>(); - protected int intervalMs; - public HostResponseTimeServiceImpl( - final @NonNull ServiceContainer serviceContainer, + final @NonNull PluginService pluginService, final @NonNull Properties props, int intervalMs) { - this.serviceContainer = serviceContainer; + + this.pluginService = pluginService; this.props = props; this.intervalMs = intervalMs; - this.telemetryFactory = serviceContainer.getTelemetryFactory(); + this.telemetryFactory = this.pluginService.getTelemetryFactory(); this.nodeCountGauge = telemetryFactory.createGauge("frt.nodes.count", () -> (long) monitoringNodes.size()); @@ -98,7 +103,7 @@ public void setHosts(final @NonNull List hosts) { try { monitoringNodes.computeIfAbsent( hostSpec.getUrl(), - (key) -> new NodeResponseTimeMonitor(this.serviceContainer, hostSpec, this.props, this.intervalMs), + (key) -> new NodeResponseTimeMonitor(this.pluginService, hostSpec, this.props, this.intervalMs), CACHE_EXPIRATION_NANO); } finally { cacheLock.unlock(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java index e09f2145c..324bb2ec1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java @@ -32,7 +32,6 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -63,7 +62,7 @@ public class NodeResponseTimeMonitor implements AutoCloseable, Runnable { private Connection monitoringConn = null; - // TODO: remove threadPool and submit monitors to MonitorService instead + // TODO: remove and submit monitors to MonitorService instead private final ExecutorService threadPool = Executors.newFixedThreadPool(1, runnableTarget -> { final Thread monitoringThread = new Thread(runnableTarget); monitoringThread.setDaemon(true); @@ -71,16 +70,16 @@ public class NodeResponseTimeMonitor implements AutoCloseable, Runnable { }); public NodeResponseTimeMonitor( - final @NonNull ServiceContainer serviceContainer, + final @NonNull PluginService pluginService, final @NonNull HostSpec hostSpec, final @NonNull Properties props, int intervalMs) { - this.pluginService = serviceContainer.getPluginService(); + this.pluginService = pluginService; this.hostSpec = hostSpec; this.props = props; this.intervalMs = intervalMs; - this.telemetryFactory = serviceContainer.getTelemetryFactory(); + this.telemetryFactory = this.pluginService.getTelemetryFactory(); final String nodeId = StringUtils.isNullOrEmpty(this.hostSpec.getHostId()) ? this.hostSpec.getHost() diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java index 21fc16425..30df221c9 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java @@ -64,7 +64,6 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; -import software.amazon.jdbc.util.ServiceContainer; class HostMonitoringConnectionPluginTest { @@ -75,7 +74,6 @@ class HostMonitoringConnectionPluginTest { static final int FAILURE_DETECTION_INTERVAL = 100; static final int FAILURE_DETECTION_COUNT = 5; private static final Object[] EMPTY_ARGS = {}; - @Mock ServiceContainer mockServiceContainer; @Mock PluginService pluginService; @Mock Dialect mockDialect; @Mock Connection connection; @@ -96,7 +94,7 @@ class HostMonitoringConnectionPluginTest { /** * Generate different sets of method arguments where one argument is null to ensure {@link - * software.amazon.jdbc.plugin.efm.HostMonitoringConnectionPlugin#HostMonitoringConnectionPlugin(ServiceContainer, + * software.amazon.jdbc.plugin.efm.HostMonitoringConnectionPlugin#HostMonitoringConnectionPlugin(PluginService, * Properties)} can handle null arguments correctly. * * @return different sets of arguments. @@ -136,7 +134,6 @@ void initDefaultMockReturns() throws Exception { .thenReturn(context); when(context.getLock()).thenReturn(mockReentrantLock); - when(mockServiceContainer.getPluginService()).thenReturn(pluginService); when(pluginService.getCurrentConnection()).thenReturn(connection); when(pluginService.getCurrentHostSpec()).thenReturn(hostSpec); when(pluginService.getDialect()).thenReturn(mockDialect); @@ -158,7 +155,7 @@ void initDefaultMockReturns() throws Exception { } private void initializePlugin() { - plugin = new HostMonitoringConnectionPlugin(mockServiceContainer, properties, supplier, rdsUtils); + plugin = new HostMonitoringConnectionPlugin(pluginService, properties, supplier, rdsUtils); } @Test diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java index e51f09397..558e2afba 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java @@ -49,17 +49,13 @@ import org.mockito.MockitoAnnotations; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; class MonitorImplTest { - @Mock ServiceContainer serviceContainer; @Mock PluginService pluginService; - @Mock ConnectionService connectionService; @Mock Connection connection; @Mock HostSpec hostSpec; @Mock Properties properties; @@ -93,16 +89,15 @@ void init() throws SQLException { .thenReturn(LONG_INTERVAL_MILLIS); when(booleanProperty.getStringValue()).thenReturn(Boolean.TRUE.toString()); when(longProperty.getValue()).thenReturn(SHORT_INTERVAL_MILLIS); - when(serviceContainer.getPluginService()).thenReturn(pluginService); - when(serviceContainer.getTelemetryFactory()).thenReturn(telemetryFactory); when(pluginService.forceConnect(any(HostSpec.class), any(Properties.class))).thenReturn(connection); + when(pluginService.getTelemetryFactory()).thenReturn(telemetryFactory); when(telemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(telemetryContext); when(telemetryFactory.openTelemetryContext(eq(null), any())).thenReturn(telemetryContext); when(telemetryFactory.createCounter(anyString())).thenReturn(telemetryCounter); when(executorServiceInitializer.createExecutorService()).thenReturn(executorService); threadContainer = MonitorThreadContainer.getInstance(executorServiceInitializer); - monitor = spy(new MonitorImpl(serviceContainer, hostSpec, properties, 0L, threadContainer)); + monitor = spy(new MonitorImpl(pluginService, hostSpec, properties, 0L, threadContainer)); } @AfterEach @@ -180,7 +175,7 @@ void test_8_runWithoutContext() { MonitorThreadContainer.releaseInstance(); } - @RepeatedTest(10) + @RepeatedTest(1000) void test_9_runWithContext() { final Map monitorMap = threadContainer.getMonitorMap(); final Map> taskMap = threadContainer.getTasksMap(); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorServiceImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorServiceImplTest.java index 36bf9e476..ce70f0f12 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorServiceImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorServiceImplTest.java @@ -46,7 +46,6 @@ import software.amazon.jdbc.HostSpecBuilder; import software.amazon.jdbc.PluginService; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -66,7 +65,6 @@ class MonitorServiceImplTest { @Mock private Future task; @Mock private HostSpec hostSpec; @Mock private JdbcConnection connection; - @Mock private ServiceContainer serviceContainer; @Mock private PluginService pluginService; @Mock private TelemetryFactory telemetryFactory; @Mock private TelemetryCounter telemetryCounter; @@ -83,8 +81,7 @@ void init() { closeable = MockitoAnnotations.openMocks(this); contextCaptor = ArgumentCaptor.forClass(MonitorConnectionContext.class); - when(serviceContainer.getPluginService()).thenReturn(pluginService); - when(serviceContainer.getTelemetryFactory()).thenReturn(telemetryFactory); + when(pluginService.getTelemetryFactory()).thenReturn(telemetryFactory); when(telemetryFactory.createCounter(anyString())).thenReturn(telemetryCounter); when(monitorInitializer.createMonitor( any(HostSpec.class), any(Properties.class), any(MonitorThreadContainer.class))) @@ -95,7 +92,7 @@ void init() { doReturn(task).when(executorService).submit(any(Monitor.class)); threadContainer = MonitorThreadContainer.getInstance(executorServiceInitializer); - monitorService = new MonitorServiceImpl(serviceContainer, monitorInitializer, executorServiceInitializer); + monitorService = new MonitorServiceImpl(pluginService, monitorInitializer, executorServiceInitializer); } @AfterEach diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedDefaultMonitorServiceTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedDefaultMonitorServiceTest.java index dbefbb50f..c408d1398 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedDefaultMonitorServiceTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedDefaultMonitorServiceTest.java @@ -56,7 +56,6 @@ import org.mockito.MockitoAnnotations; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -74,7 +73,6 @@ class MultiThreadedDefaultMonitorServiceTest { @Mock Monitor monitor; @Mock Properties properties; @Mock JdbcConnection connection; - @Mock ServiceContainer serviceContainer; @Mock PluginService pluginService; @Mock TelemetryCounter abortedConnectionsCounter; @@ -114,8 +112,7 @@ void init(TestInfo testInfo) { doNothing().when(monitor).stopMonitoring(stopMonitoringCaptor.capture()); when(properties.getProperty(any(String.class))) .thenReturn(String.valueOf(MONITOR_DISPOSE_TIME)); - when(serviceContainer.getPluginService()).thenReturn(pluginService); - when(serviceContainer.getTelemetryFactory()).thenReturn(telemetryFactory); + when(pluginService.getTelemetryFactory()).thenReturn(telemetryFactory); when(telemetryFactory.createCounter(anyString())).thenReturn(abortedConnectionsCounter); } @@ -413,7 +410,7 @@ private List generateContexts( private List generateServices(final int numServices) { final List services = new ArrayList<>(); for (int i = 0; i < numServices; i++) { - services.add(new MonitorServiceImpl(serviceContainer, monitorInitializer, executorServiceInitializer)); + services.add(new MonitorServiceImpl(pluginService, monitorInitializer, executorServiceInitializer)); } return services; } diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java index 5287f236a..31d58f7b0 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandlerTest.java @@ -54,14 +54,10 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; -import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.connection.ConnectionService; class ClusterAwareReaderFailoverHandlerTest { - @Mock ServiceContainer mockServiceContainer; @Mock PluginService mockPluginService; - @Mock ConnectionService mockConnectionService; @Mock Connection mockConnection; private AutoCloseable closeable; @@ -84,7 +80,6 @@ class ClusterAwareReaderFailoverHandlerTest { @BeforeEach void setUp() { closeable = MockitoAnnotations.openMocks(this); - when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); } @AfterEach @@ -118,7 +113,7 @@ public void testFailover() throws SQLException { final ReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockServiceContainer, + mockPluginService, properties); final ReaderFailoverResult result = target.failover(hosts, hosts.get(currentHostIndex)); @@ -157,7 +152,7 @@ public void testFailover_timeout() throws SQLException { final ReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockServiceContainer, + mockPluginService, properties, 5000, 30000, @@ -180,7 +175,7 @@ public void testFailover_timeout() throws SQLException { public void testFailover_nullOrEmptyHostList() throws SQLException { final ClusterAwareReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockServiceContainer, + mockPluginService, properties); final HostSpec currentHost = new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("writer") .port(1234).build(); @@ -220,7 +215,7 @@ public void testGetReader_connectionSuccess() throws SQLException { final ReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockServiceContainer, + mockPluginService, properties); final ReaderFailoverResult result = target.getReaderConnection(hosts); @@ -249,7 +244,7 @@ public void testGetReader_connectionFailure() throws SQLException { final ReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockServiceContainer, + mockPluginService, properties); final ReaderFailoverResult result = target.getReaderConnection(hosts); @@ -282,7 +277,7 @@ public void testGetReader_connectionAttemptsTimeout() throws SQLException { final ClusterAwareReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockServiceContainer, + mockPluginService, properties, 60000, 1000, @@ -303,7 +298,7 @@ public void testGetHostTuplesByPriority() { final ClusterAwareReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockServiceContainer, + mockPluginService, properties); final List hostsByPriority = target.getHostsByPriority(originalHosts); @@ -345,7 +340,7 @@ public void testGetReaderTuplesByPriority() { final ClusterAwareReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockServiceContainer, + mockPluginService, properties); final List hostsByPriority = target.getReaderHostsByPriority(originalHosts); @@ -382,7 +377,7 @@ public void testHostFailoverStrictReaderEnabled() { when(mockPluginService.getDialect()).thenReturn(mockDialect); final ClusterAwareReaderFailoverHandler target = new ClusterAwareReaderFailoverHandler( - mockServiceContainer, + mockPluginService, properties, DEFAULT_FAILOVER_TIMEOUT, DEFAULT_READER_CONNECT_TIMEOUT, diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java index 5436b2e1f..515325f7c 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/ClusterAwareWriterFailoverHandlerTest.java @@ -50,11 +50,9 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; -import software.amazon.jdbc.util.ServiceContainer; class ClusterAwareWriterFailoverHandlerTest { - @Mock ServiceContainer mockServiceContainer; @Mock PluginService mockPluginService; @Mock Connection mockConnection; @Mock ReaderFailoverHandler mockReaderFailover; @@ -80,7 +78,6 @@ class ClusterAwareWriterFailoverHandlerTest { @BeforeEach void setUp() { closeable = MockitoAnnotations.openMocks(this); - when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); writer.addAlias("writer-host"); newWriterHost.addAlias("new-writer-host"); readerA.addAlias("reader-a-host"); @@ -107,7 +104,7 @@ public void testReconnectToWriter_taskBReaderException() throws SQLException { final ClusterAwareWriterFailoverHandler target = new ClusterAwareWriterFailoverHandler( - mockServiceContainer, + mockPluginService, mockReaderFailover, properties, 5000, @@ -151,7 +148,7 @@ public void testReconnectToWriter_SlowReaderA() throws SQLException { final ClusterAwareWriterFailoverHandler target = new ClusterAwareWriterFailoverHandler( - mockServiceContainer, + mockPluginService, mockReaderFailover, properties, 60000, @@ -196,7 +193,7 @@ public void testReconnectToWriter_taskBDefers() throws SQLException { final ClusterAwareWriterFailoverHandler target = new ClusterAwareWriterFailoverHandler( - mockServiceContainer, + mockPluginService, mockReaderFailover, properties, 60000, @@ -244,7 +241,7 @@ public void testConnectToReaderA_SlowWriter() throws SQLException { final ClusterAwareWriterFailoverHandler target = new ClusterAwareWriterFailoverHandler( - mockServiceContainer, + mockPluginService, mockReaderFailover, properties, 60000, @@ -293,7 +290,7 @@ public void testConnectToReaderA_taskADefers() throws SQLException { final ClusterAwareWriterFailoverHandler target = new ClusterAwareWriterFailoverHandler( - mockServiceContainer, + mockPluginService, mockReaderFailover, properties, 60000, @@ -347,7 +344,7 @@ public void testFailedToConnect_failoverTimeout() throws SQLException { final ClusterAwareWriterFailoverHandler target = new ClusterAwareWriterFailoverHandler( - mockServiceContainer, + mockPluginService, mockReaderFailover, properties, 5000, @@ -394,7 +391,7 @@ public void testFailedToConnect_taskAException_taskBWriterException() throws SQL final ClusterAwareWriterFailoverHandler target = new ClusterAwareWriterFailoverHandler( - mockServiceContainer, + mockPluginService, mockReaderFailover, properties, 5000, diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginTest.java index 15bcb8560..2123848d1 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/failover/FailoverConnectionPluginTest.java @@ -60,7 +60,6 @@ import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.hostlistprovider.AuroraHostListProvider; import software.amazon.jdbc.util.RdsUrlType; -import software.amazon.jdbc.util.ServiceContainer; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.telemetry.GaugeCallable; import software.amazon.jdbc.util.telemetry.TelemetryContext; @@ -79,7 +78,6 @@ class FailoverConnectionPluginTest { new HostSpecBuilder(new SimpleHostAvailabilityStrategy()) .host("reader1").port(1234).role(HostRole.READER).build()); - @Mock ServiceContainer mockServiceContainer; @Mock PluginService mockPluginService; @Mock Connection mockConnection; @Mock HostSpec mockHostSpec; @@ -109,9 +107,7 @@ void cleanUp() throws Exception { void init() throws SQLException { closeable = MockitoAnnotations.openMocks(this); - when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); when(mockPluginService.getHostListProvider()).thenReturn(mockHostListProvider); - when(mockPluginService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockHostListProvider.getRdsUrlType()).thenReturn(RdsUrlType.RDS_WRITER_CLUSTER); when(mockPluginService.getCurrentConnection()).thenReturn(mockConnection); when(mockPluginService.getCurrentHostSpec()).thenReturn(mockHostSpec); @@ -433,7 +429,7 @@ void test_execute_withDirectExecute() throws SQLException { } private void initializePlugin() { - plugin = new FailoverConnectionPlugin(mockServiceContainer, properties); + plugin = new FailoverConnectionPlugin(mockPluginService, properties); plugin.setWriterFailoverHandler(mockWriterFailoverHandler); plugin.setReaderFailoverHandler(mockReaderFailoverHandler); } diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginTest.java index aeaa5a1ad..411233100 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/limitless/LimitlessConnectionPluginTest.java @@ -45,7 +45,6 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.dialect.PgDialect; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; -import software.amazon.jdbc.util.ServiceContainer; public class LimitlessConnectionPluginTest { @@ -59,20 +58,21 @@ public class LimitlessConnectionPluginTest { private static final Dialect supportedDialect = new AuroraPgDialect(); @Mock JdbcCallable mockConnectFuncLambda; @Mock private Connection mockConnection; - @Mock private ServiceContainer mockServiceContainer; @Mock private PluginService mockPluginService; @Mock private HostListProvider mockHostListProvider; @Mock private LimitlessRouterService mockLimitlessRouterService; private static Properties props; + private static LimitlessConnectionPlugin plugin; + private AutoCloseable closeable; @BeforeEach public void init() throws SQLException { closeable = MockitoAnnotations.openMocks(this); props = new Properties(); + plugin = new LimitlessConnectionPlugin(mockPluginService, props, () -> mockLimitlessRouterService); - when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); when(mockPluginService.getHostListProvider()).thenReturn(mockHostListProvider); when(mockPluginService.getDialect()).thenReturn(supportedDialect); when(mockHostListProvider.getClusterId()).thenReturn(CLUSTER_ID); @@ -94,8 +94,6 @@ public Void answer(InvocationOnMock invocation) { } }).when(mockLimitlessRouterService).establishConnection(any()); - final LimitlessConnectionPlugin plugin = - new LimitlessConnectionPlugin(mockServiceContainer, props, () -> mockLimitlessRouterService); final Connection expectedConnection = mockConnection; final Connection actualConnection = plugin.connect(DRIVER_PROTOCOL, INPUT_HOST_SPEC, props, true, mockConnectFuncLambda); @@ -118,8 +116,6 @@ public Void answer(InvocationOnMock invocation) { } }).when(mockLimitlessRouterService).establishConnection(any()); - final LimitlessConnectionPlugin plugin = - new LimitlessConnectionPlugin(mockServiceContainer, props, () -> mockLimitlessRouterService); assertThrows( SQLException.class, () -> plugin.connect(DRIVER_PROTOCOL, INPUT_HOST_SPEC, props, true, mockConnectFuncLambda)); @@ -136,8 +132,6 @@ void testConnectGivenUnsupportedDialect() throws SQLException { final Dialect unsupportedDialect = new PgDialect(); when(mockPluginService.getDialect()).thenReturn(unsupportedDialect, unsupportedDialect); - final LimitlessConnectionPlugin plugin = - new LimitlessConnectionPlugin(mockServiceContainer, props, () -> mockLimitlessRouterService); assertThrows( UnsupportedOperationException.class, () -> plugin.connect(DRIVER_PROTOCOL, INPUT_HOST_SPEC, props, true, mockConnectFuncLambda)); @@ -154,8 +148,6 @@ void testConnectGivenSupportedDialectAfterRefresh() throws SQLException { final Dialect unsupportedDialect = new PgDialect(); when(mockPluginService.getDialect()).thenReturn(unsupportedDialect, supportedDialect); - final LimitlessConnectionPlugin plugin = - new LimitlessConnectionPlugin(mockServiceContainer, props, () -> mockLimitlessRouterService); final Connection expectedConnection = mockConnection; final Connection actualConnection = plugin.connect(DRIVER_PROTOCOL, INPUT_HOST_SPEC, props, true, mockConnectFuncLambda); From f795adc91f539918fa33414dad98f5e414d460a8 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 20 May 2025 13:33:44 -0700 Subject: [PATCH 115/149] Remove unnecessary CustomEndpointMonitorImpl constructor arg --- .../jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java | 3 --- .../jdbc/plugin/customendpoint/CustomEndpointPlugin.java | 1 - .../plugin/customendpoint/CustomEndpointMonitorImplTest.java | 1 - 3 files changed, 5 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index 6d3dcac4d..80bc14680 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -33,7 +33,6 @@ import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.monitoring.AbstractMonitor; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; @@ -63,7 +62,6 @@ public class CustomEndpointMonitorImpl extends AbstractMonitor implements Custom /** * Constructs a CustomEndpointMonitorImpl instance for the host specified by {@code customEndpointHostSpec}. * - * @param monitorService The monitorService used to submit this monitor. * @param storageService The storage service used to store the set of allowed/blocked hosts according to the * custom endpoint info. * @param telemetryFactory The telemetry factory used to create telemetry data. @@ -76,7 +74,6 @@ public class CustomEndpointMonitorImpl extends AbstractMonitor implements Custom * information. */ public CustomEndpointMonitorImpl( - CoreMonitorService monitorService, StorageService storageService, TelemetryFactory telemetryFactory, HostSpec customEndpointHostSpec, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java index 305c0855b..7218cd58c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java @@ -218,7 +218,6 @@ protected CustomEndpointMonitor createMonitorIfAbsent(Properties props) throws S this.pluginService.getDialect(), this.props, (connectionService, pluginService) -> new CustomEndpointMonitorImpl( - this.monitorService, this.storageService, this.serviceContainer.getTelemetryFactory(), this.customEndpointHostSpec, diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java index 5420416d3..455521d5c 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java @@ -114,7 +114,6 @@ void cleanUp() throws Exception { @Test public void testRun() throws InterruptedException { CustomEndpointMonitorImpl monitor = new CustomEndpointMonitorImpl( - mockMonitorService, mockStorageService, mockTelemetryFactory, host, From 6904adecaacd8652b34930bf1fec4faab5c56273 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 20 May 2025 13:50:14 -0700 Subject: [PATCH 116/149] Rename EFM monitor classes to distinguish from new monitor classes --- .../java/software/amazon/jdbc/Driver.java | 7 +- .../efm/ExecutorServiceInitializer.java | 2 +- .../efm/{Monitor.java => HostMonitor.java} | 10 +- ...java => HostMonitorConnectionContext.java} | 12 +-- ...{MonitorImpl.java => HostMonitorImpl.java} | 32 +++--- ...lizer.java => HostMonitorInitializer.java} | 6 +- ...orService.java => HostMonitorService.java} | 12 +-- ...eImpl.java => HostMonitorServiceImpl.java} | 42 ++++---- ...r.java => HostMonitorThreadContainer.java} | 48 ++++----- .../efm/HostMonitoringConnectionPlugin.java | 12 +-- .../efm2/{Monitor.java => HostMonitor.java} | 4 +- ...java => HostMonitorConnectionContext.java} | 4 +- ...{MonitorImpl.java => HostMonitorImpl.java} | 28 ++--- ...lizer.java => HostMonitorInitializer.java} | 6 +- ...orService.java => HostMonitorService.java} | 12 +-- ...eImpl.java => HostMonitorServiceImpl.java} | 32 +++--- .../efm2/HostMonitoringConnectionPlugin.java | 12 +-- .../tests/AdvancedPerformanceTest.java | 8 +- .../container/tests/PerformanceTest.java | 16 +-- ...HostHostMonitorConnectionContextTest.java} | 14 +-- ...va => HostHostMonitorServiceImplTest.java} | 44 ++++---- ...ImplTest.java => HostMonitorImplTest.java} | 44 ++++---- .../HostMonitoringConnectionPluginTest.java | 6 +- ...dedDefaultHostHostMonitorServiceTest.java} | 100 +++++++++--------- ...readedHostMonitorThreadContainerTest.java} | 10 +- 25 files changed, 262 insertions(+), 261 deletions(-) rename wrapper/src/main/java/software/amazon/jdbc/plugin/efm/{Monitor.java => HostMonitor.java} (73%) rename wrapper/src/main/java/software/amazon/jdbc/plugin/efm/{MonitorConnectionContext.java => HostMonitorConnectionContext.java} (96%) rename wrapper/src/main/java/software/amazon/jdbc/plugin/efm/{MonitorImpl.java => HostMonitorImpl.java} (93%) rename wrapper/src/main/java/software/amazon/jdbc/plugin/efm/{MonitorInitializer.java => HostMonitorInitializer.java} (77%) rename wrapper/src/main/java/software/amazon/jdbc/plugin/efm/{MonitorService.java => HostMonitorService.java} (81%) rename wrapper/src/main/java/software/amazon/jdbc/plugin/efm/{MonitorServiceImpl.java => HostMonitorServiceImpl.java} (77%) rename wrapper/src/main/java/software/amazon/jdbc/plugin/efm/{MonitorThreadContainer.java => HostMonitorThreadContainer.java} (70%) rename wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/{Monitor.java => HostMonitor.java} (87%) rename wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/{MonitorConnectionContext.java => HostMonitorConnectionContext.java} (94%) rename wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/{MonitorImpl.java => HostMonitorImpl.java} (93%) rename wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/{MonitorInitializer.java => HostMonitorInitializer.java} (88%) rename wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/{MonitorService.java => HostMonitorService.java} (78%) rename wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/{MonitorServiceImpl.java => HostMonitorServiceImpl.java} (86%) rename wrapper/src/test/java/software/amazon/jdbc/plugin/efm/{MonitorConnectionContextTest.java => HostHostMonitorConnectionContextTest.java} (94%) rename wrapper/src/test/java/software/amazon/jdbc/plugin/efm/{MonitorServiceImplTest.java => HostHostMonitorServiceImplTest.java} (85%) rename wrapper/src/test/java/software/amazon/jdbc/plugin/efm/{MonitorImplTest.java => HostMonitorImplTest.java} (85%) rename wrapper/src/test/java/software/amazon/jdbc/plugin/efm/{MultiThreadedDefaultMonitorServiceTest.java => MultiThreadedDefaultHostHostMonitorServiceTest.java} (77%) rename wrapper/src/test/java/software/amazon/jdbc/plugin/efm/{MultiThreadedMonitorThreadContainerTest.java => MultiThreadedHostMonitorThreadContainerTest.java} (83%) diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index a277bbfe8..8c6d828e0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -43,7 +43,8 @@ import software.amazon.jdbc.plugin.DataCacheConnectionPlugin; import software.amazon.jdbc.plugin.OpenedConnectionTracker; import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; -import software.amazon.jdbc.plugin.efm.MonitorThreadContainer; +import software.amazon.jdbc.plugin.efm.HostMonitorThreadContainer; +import software.amazon.jdbc.plugin.efm2.HostMonitorServiceImpl; import software.amazon.jdbc.plugin.federatedauth.FederatedAuthCacheHolder; import software.amazon.jdbc.plugin.federatedauth.OktaAuthCacheHolder; import software.amazon.jdbc.plugin.iam.IamAuthCacheHolder; @@ -425,8 +426,8 @@ public static void clearCaches() { public static void releaseResources() { CoreServicesContainer.getInstance().getMonitorService().stopAndRemoveAll(); - software.amazon.jdbc.plugin.efm2.MonitorServiceImpl.closeAllMonitors(); - MonitorThreadContainer.releaseInstance(); + HostMonitorServiceImpl.closeAllMonitors(); + HostMonitorThreadContainer.releaseInstance(); ConnectionProviderManager.releaseResources(); HikariPoolsHolder.closeAllPools(); HostResponseTimeServiceImpl.closeAllMonitors(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/ExecutorServiceInitializer.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/ExecutorServiceInitializer.java index ef9cda6e1..b35d6c459 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/ExecutorServiceInitializer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/ExecutorServiceInitializer.java @@ -20,7 +20,7 @@ /** * Interface for passing a specific {@link ExecutorService} to use by the {@link - * MonitorThreadContainer}. + * HostMonitorThreadContainer}. */ @FunctionalInterface public interface ExecutorServiceInitializer { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitor.java similarity index 73% rename from wrapper/src/main/java/software/amazon/jdbc/plugin/efm/Monitor.java rename to wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitor.java index f9e3eb1ca..c95b5a8cf 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitor.java @@ -20,17 +20,17 @@ * Interface for monitors. This class uses background threads to monitor servers with one or more * connections for more efficient failure detection during method execution. */ -public interface Monitor extends Runnable { +public interface HostMonitor extends Runnable { - void startMonitoring(MonitorConnectionContext context); + void startMonitoring(HostMonitorConnectionContext context); - void stopMonitoring(MonitorConnectionContext context); + void stopMonitoring(HostMonitorConnectionContext context); - /** Clear all {@link MonitorConnectionContext} associated with this {@link Monitor} instance. */ + /** Clear all {@link HostMonitorConnectionContext} associated with this {@link HostMonitor} instance. */ void clearContexts(); /** - * Whether this {@link Monitor} has stopped monitoring a particular server. + * Whether this {@link HostMonitor} has stopped monitoring a particular server. * * @return true if the monitoring has stopped; false otherwise. */ diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorConnectionContext.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorConnectionContext.java similarity index 96% rename from wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorConnectionContext.java rename to wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorConnectionContext.java index 195e60869..2c6799ac4 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorConnectionContext.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorConnectionContext.java @@ -30,9 +30,9 @@ * Monitoring context for each connection. This contains each connection's criteria for whether a * server should be considered unhealthy. The context is shared between the main thread and the monitor thread. */ -public class MonitorConnectionContext { +public class HostMonitorConnectionContext { - private static final Logger LOGGER = Logger.getLogger(MonitorConnectionContext.class.getName()); + private static final Logger LOGGER = Logger.getLogger(HostMonitorConnectionContext.class.getName()); private static final Executor ABORT_EXECUTOR = Executors.newSingleThreadExecutor(); private final TelemetryCounter abortedConnectionsCounter; @@ -41,7 +41,7 @@ public class MonitorConnectionContext { private final long failureDetectionTimeMillis; private final long failureDetectionCount; private final Connection connectionToAbort; - private final Monitor monitor; + private final HostMonitor monitor; private volatile boolean activeContext = true; private volatile boolean nodeUnhealthy = false; @@ -64,8 +64,8 @@ public class MonitorConnectionContext { * node as unhealthy. * @param abortedConnectionsCounter Aborted connection telemetry counter. */ - public MonitorConnectionContext( - final Monitor monitor, + public HostMonitorConnectionContext( + final HostMonitor monitor, final Connection connectionToAbort, final long failureDetectionTimeMillis, final long failureDetectionIntervalMillis, @@ -101,7 +101,7 @@ public long getExpectedActiveMonitoringStartTimeNano() { return this.expectedActiveMonitoringStartTimeNano; } - public Monitor getMonitor() { + public HostMonitor getMonitor() { return this.monitor; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorImpl.java similarity index 93% rename from wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java rename to wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorImpl.java index ac53b2ca1..64cc80306 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorImpl.java @@ -40,7 +40,7 @@ * This class uses a background thread to monitor a particular server with one or more active {@link * Connection}. */ -public class MonitorImpl implements Monitor { +public class HostMonitorImpl implements HostMonitor { static class ConnectionStatus { @@ -53,18 +53,18 @@ static class ConnectionStatus { } } - private static final Logger LOGGER = Logger.getLogger(MonitorImpl.class.getName()); + private static final Logger LOGGER = Logger.getLogger(HostMonitorImpl.class.getName()); private static final long THREAD_SLEEP_WHEN_INACTIVE_MILLIS = 100; private static final long MIN_CONNECTION_CHECK_TIMEOUT_MILLIS = 100; private static final String MONITORING_PROPERTY_PREFIX = "monitoring-"; - final Queue activeContexts = new ConcurrentLinkedQueue<>(); - private final Queue newContexts = new ConcurrentLinkedQueue<>(); + final Queue activeContexts = new ConcurrentLinkedQueue<>(); + private final Queue newContexts = new ConcurrentLinkedQueue<>(); private final PluginService pluginService; private final TelemetryFactory telemetryFactory; private final Properties properties; private final HostSpec hostSpec; - private final MonitorThreadContainer threadContainer; + private final HostMonitorThreadContainer threadContainer; private final long monitorDisposalTimeMillis; private volatile long contextLastUsedTimestampNano; private volatile boolean stopped = false; @@ -78,22 +78,22 @@ static class ConnectionStatus { * Store the monitoring configuration for a connection. * * @param pluginService A service for creating new connections. - * @param hostSpec The {@link HostSpec} of the server this {@link MonitorImpl} + * @param hostSpec The {@link HostSpec} of the server this {@link HostMonitorImpl} * instance is monitoring. * @param properties The {@link Properties} containing additional monitoring * configuration. * @param monitorDisposalTimeMillis Time in milliseconds before stopping the monitoring thread * where there are no active connection to the server this - * {@link MonitorImpl} instance is monitoring. - * @param threadContainer A reference to the {@link MonitorThreadContainer} implementation + * {@link HostMonitorImpl} instance is monitoring. + * @param threadContainer A reference to the {@link HostMonitorThreadContainer} implementation * that initialized this class. */ - public MonitorImpl( + public HostMonitorImpl( final @NonNull PluginService pluginService, @NonNull final HostSpec hostSpec, @NonNull final Properties properties, final long monitorDisposalTimeMillis, - @NonNull final MonitorThreadContainer threadContainer) { + @NonNull final HostMonitorThreadContainer threadContainer) { this.pluginService = pluginService; this.telemetryFactory = pluginService.getTelemetryFactory(); this.hostSpec = hostSpec; @@ -112,7 +112,7 @@ public MonitorImpl( } @Override - public void startMonitoring(final MonitorConnectionContext context) { + public void startMonitoring(final HostMonitorConnectionContext context) { if (this.stopped) { LOGGER.warning(() -> Messages.get("MonitorImpl.monitorIsStopped", new Object[] {this.hostSpec.getHost()})); } @@ -123,7 +123,7 @@ public void startMonitoring(final MonitorConnectionContext context) { } @Override - public void stopMonitoring(final MonitorConnectionContext context) { + public void stopMonitoring(final HostMonitorConnectionContext context) { if (context == null) { LOGGER.warning(() -> Messages.get("MonitorImpl.contextNullWarning")); return; @@ -151,8 +151,8 @@ public void run() { try { // process new contexts - MonitorConnectionContext newMonitorContext; - MonitorConnectionContext firstAddedNewMonitorContext = null; + HostMonitorConnectionContext newMonitorContext; + HostMonitorConnectionContext firstAddedNewMonitorContext = null; final long currentTimeNano = this.getCurrentTimeNano(); while ((newMonitorContext = this.newContexts.poll()) != null) { @@ -187,8 +187,8 @@ public void run() { final ConnectionStatus status = checkConnectionStatus(this.nodeCheckTimeoutMillis); long delayMillis = -1; - MonitorConnectionContext monitorContext; - MonitorConnectionContext firstAddedMonitorContext = null; + HostMonitorConnectionContext monitorContext; + HostMonitorConnectionContext firstAddedMonitorContext = null; while ((monitorContext = this.activeContexts.poll()) != null) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorInitializer.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorInitializer.java similarity index 77% rename from wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorInitializer.java rename to wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorInitializer.java index 5e25a66af..e920e69ba 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorInitializer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorInitializer.java @@ -19,8 +19,8 @@ import java.util.Properties; import software.amazon.jdbc.HostSpec; -/** Interface for initialize a new {@link MonitorImpl}. */ +/** Interface for initialize a new {@link HostMonitorImpl}. */ @FunctionalInterface -public interface MonitorInitializer { - Monitor createMonitor(HostSpec hostSpec, Properties properties, MonitorThreadContainer threadContainer); +public interface HostMonitorInitializer { + HostMonitor createMonitor(HostSpec hostSpec, Properties properties, HostMonitorThreadContainer threadContainer); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorService.java similarity index 81% rename from wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorService.java rename to wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorService.java index 6f034fd75..21d52fc67 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorService.java @@ -25,9 +25,9 @@ * Interface for monitor services. This class implements ways to start and stop monitoring servers * when connections are created. */ -public interface MonitorService { +public interface HostMonitorService { - MonitorConnectionContext startMonitoring( + HostMonitorConnectionContext startMonitoring( Connection connectionToAbort, Set nodeKeys, HostSpec hostSpec, @@ -37,12 +37,12 @@ MonitorConnectionContext startMonitoring( int failureDetectionCount); /** - * Stop monitoring for a connection represented by the given {@link MonitorConnectionContext}. - * Removes the context from the {@link MonitorImpl}. + * Stop monitoring for a connection represented by the given {@link HostMonitorConnectionContext}. + * Removes the context from the {@link HostMonitorImpl}. * - * @param context The {@link MonitorConnectionContext} representing a connection. + * @param context The {@link HostMonitorConnectionContext} representing a connection. */ - void stopMonitoring(MonitorConnectionContext context); + void stopMonitoring(HostMonitorConnectionContext context); /** * Stop monitoring the node for all connections represented by the given set of node keys. diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorServiceImpl.java similarity index 77% rename from wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorServiceImpl.java rename to wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorServiceImpl.java index 2763f8a34..8ad17e618 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorServiceImpl.java @@ -35,9 +35,9 @@ * This class handles the creation and clean up of monitoring threads to servers with one or more * active connections. */ -public class MonitorServiceImpl implements MonitorService { +public class HostMonitorServiceImpl implements HostMonitorService { - private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); + private static final Logger LOGGER = Logger.getLogger(HostMonitorServiceImpl.class.getName()); public static final AwsWrapperProperty MONITOR_DISPOSAL_TIME_MS = new AwsWrapperProperty( @@ -46,19 +46,19 @@ public class MonitorServiceImpl implements MonitorService { "Interval in milliseconds for a monitor to be considered inactive and to be disposed."); private final PluginService pluginService; - private MonitorThreadContainer threadContainer; + private HostMonitorThreadContainer threadContainer; - final MonitorInitializer monitorInitializer; + final HostMonitorInitializer monitorInitializer; private Set cachedMonitorNodeKeys = null; - private WeakReference cachedMonitor = null; + private WeakReference cachedMonitor = null; final TelemetryFactory telemetryFactory; final TelemetryCounter abortedConnectionsCounter; - public MonitorServiceImpl(final @NonNull PluginService pluginService) { + public HostMonitorServiceImpl(final @NonNull PluginService pluginService) { this( pluginService, (hostSpec, properties, monitorService) -> - new MonitorImpl( + new HostMonitorImpl( pluginService, hostSpec, properties, @@ -73,19 +73,19 @@ public MonitorServiceImpl(final @NonNull PluginService pluginService) { })); } - MonitorServiceImpl( + HostMonitorServiceImpl( final PluginService pluginService, - final MonitorInitializer monitorInitializer, + final HostMonitorInitializer monitorInitializer, final ExecutorServiceInitializer executorServiceInitializer) { this.pluginService = pluginService; this.telemetryFactory = pluginService.getTelemetryFactory(); this.abortedConnectionsCounter = telemetryFactory.createCounter("efm.connections.aborted"); this.monitorInitializer = monitorInitializer; - this.threadContainer = MonitorThreadContainer.getInstance(executorServiceInitializer); + this.threadContainer = HostMonitorThreadContainer.getInstance(executorServiceInitializer); } @Override - public MonitorConnectionContext startMonitoring( + public HostMonitorConnectionContext startMonitoring( final Connection connectionToAbort, final Set nodeKeys, final HostSpec hostSpec, @@ -100,7 +100,7 @@ public MonitorConnectionContext startMonitoring( new Object[] {hostSpec})); } - Monitor monitor = this.cachedMonitor == null ? null : this.cachedMonitor.get(); + HostMonitor monitor = this.cachedMonitor == null ? null : this.cachedMonitor.get(); if (monitor == null || monitor.isStopped() || this.cachedMonitorNodeKeys == null @@ -111,8 +111,8 @@ public MonitorConnectionContext startMonitoring( this.cachedMonitorNodeKeys = Collections.unmodifiableSet(nodeKeys); } - final MonitorConnectionContext context = - new MonitorConnectionContext( + final HostMonitorConnectionContext context = + new HostMonitorConnectionContext( monitor, connectionToAbort, failureDetectionTimeMillis, @@ -126,14 +126,14 @@ public MonitorConnectionContext startMonitoring( } @Override - public void stopMonitoring(@NonNull final MonitorConnectionContext context) { - final Monitor monitor = context.getMonitor(); + public void stopMonitoring(@NonNull final HostMonitorConnectionContext context) { + final HostMonitor monitor = context.getMonitor(); monitor.stopMonitoring(context); } @Override public void stopMonitoringForAllConnections(@NonNull final Set nodeKeys) { - Monitor monitor; + HostMonitor monitor; for (final String nodeKey : nodeKeys) { monitor = this.threadContainer.getMonitor(nodeKey); if (monitor != null) { @@ -149,19 +149,19 @@ public void releaseResources() { } /** - * Get or create a {@link MonitorImpl} for a server. + * Get or create a {@link HostMonitorImpl} for a server. * * @param nodeKeys All references to the server requiring monitoring. * @param hostSpec Information such as hostname of the server. * @param properties The user configuration for the current connection. - * @return A {@link MonitorImpl} object associated with a specific server. + * @return A {@link HostMonitorImpl} object associated with a specific server. */ - protected Monitor getMonitor(final Set nodeKeys, final HostSpec hostSpec, final Properties properties) { + protected HostMonitor getMonitor(final Set nodeKeys, final HostSpec hostSpec, final Properties properties) { return this.threadContainer.getOrCreateMonitor( nodeKeys, () -> monitorInitializer.createMonitor(hostSpec, properties, this.threadContainer)); } - MonitorThreadContainer getThreadContainer() { + HostMonitorThreadContainer getThreadContainer() { return this.threadContainer; } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorThreadContainer.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorThreadContainer.java similarity index 70% rename from wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorThreadContainer.java rename to wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorThreadContainer.java index 4ead2ebdb..e23ec2b26 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/MonitorThreadContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorThreadContainer.java @@ -32,27 +32,27 @@ * This singleton class keeps track of all the monitoring threads and handles the creation and clean * up of each monitoring thread. */ -public class MonitorThreadContainer { +public class HostMonitorThreadContainer { - private static MonitorThreadContainer singleton = null; - private final Map> tasksMap = new ConcurrentHashMap<>(); + private static HostMonitorThreadContainer singleton = null; + private final Map> tasksMap = new ConcurrentHashMap<>(); // TODO: remove monitorMap and threadPool and submit monitors to MonitorService instead - private final Map monitorMap = new ConcurrentHashMap<>(); + private final Map monitorMap = new ConcurrentHashMap<>(); private final ExecutorService threadPool; private static final ReentrantLock LOCK_OBJECT = new ReentrantLock(); private static final ReentrantLock MONITOR_LOCK_OBJECT = new ReentrantLock(); /** - * Create an instance of the {@link MonitorThreadContainer}. + * Create an instance of the {@link HostMonitorThreadContainer}. * - * @return a singleton instance of the {@link MonitorThreadContainer}. + * @return a singleton instance of the {@link HostMonitorThreadContainer}. */ - public static MonitorThreadContainer getInstance() { + public static HostMonitorThreadContainer getInstance() { return getInstance(Executors::newCachedThreadPool); } - static MonitorThreadContainer getInstance(final ExecutorServiceInitializer executorServiceInitializer) { - MonitorThreadContainer singletonToReturn = singleton; + static HostMonitorThreadContainer getInstance(final ExecutorServiceInitializer executorServiceInitializer) { + HostMonitorThreadContainer singletonToReturn = singleton; if (singletonToReturn != null) { return singletonToReturn; @@ -61,7 +61,7 @@ static MonitorThreadContainer getInstance(final ExecutorServiceInitializer execu LOCK_OBJECT.lock(); try { if (singleton == null) { - singleton = new MonitorThreadContainer(executorServiceInitializer); + singleton = new HostMonitorThreadContainer(executorServiceInitializer); } singletonToReturn = singleton; } finally { @@ -71,7 +71,7 @@ static MonitorThreadContainer getInstance(final ExecutorServiceInitializer execu } /** - * Release resources held in the {@link MonitorThreadContainer} and clear references to the + * Release resources held in the {@link HostMonitorThreadContainer} and clear references to the * container. */ public static void releaseInstance() { @@ -89,23 +89,23 @@ public static void releaseInstance() { } } - private MonitorThreadContainer(final ExecutorServiceInitializer executorServiceInitializer) { + private HostMonitorThreadContainer(final ExecutorServiceInitializer executorServiceInitializer) { this.threadPool = executorServiceInitializer.createExecutorService(); } - public Map getMonitorMap() { + public Map getMonitorMap() { return monitorMap; } - public Map> getTasksMap() { + public Map> getTasksMap() { return tasksMap; } - Monitor getMonitor(final String node) { + HostMonitor getMonitor(final String node) { return monitorMap.get(node); } - Monitor getOrCreateMonitor(final Set nodeKeys, final Supplier monitorSupplier) { + HostMonitor getOrCreateMonitor(final Set nodeKeys, final Supplier monitorSupplier) { if (nodeKeys.isEmpty()) { throw new IllegalArgumentException(Messages.get("MonitorThreadContainer.emptyNodeKeys")); } @@ -113,7 +113,7 @@ Monitor getOrCreateMonitor(final Set nodeKeys, final Supplier m MONITOR_LOCK_OBJECT.lock(); try { - Monitor monitor = null; + HostMonitor monitor = null; String anyNodeKey = null; for (final String nodeKey : nodeKeys) { monitor = monitorMap.get(nodeKey); @@ -127,7 +127,7 @@ Monitor getOrCreateMonitor(final Set nodeKeys, final Supplier m monitor = monitorMap.computeIfAbsent( anyNodeKey, k -> { - final Monitor newMonitor = monitorSupplier.get(); + final HostMonitor newMonitor = monitorSupplier.get(); addTask(newMonitor); return newMonitor; }); @@ -140,28 +140,28 @@ Monitor getOrCreateMonitor(final Set nodeKeys, final Supplier m } } - private void populateMonitorMap(final Set nodeKeys, final Monitor monitor) { + private void populateMonitorMap(final Set nodeKeys, final HostMonitor monitor) { for (final String nodeKey : nodeKeys) { monitorMap.putIfAbsent(nodeKey, monitor); } } - void addTask(final Monitor monitor) { + void addTask(final HostMonitor monitor) { tasksMap.computeIfAbsent(monitor, k -> threadPool.submit(monitor)); } /** - * Remove references to the given {@link MonitorImpl} object and stop the background monitoring + * Remove references to the given {@link HostMonitorImpl} object and stop the background monitoring * thread. * - * @param monitor The {@link MonitorImpl} representing a monitoring thread. + * @param monitor The {@link HostMonitorImpl} representing a monitoring thread. */ - public void releaseResource(final Monitor monitor) { + public void releaseResource(final HostMonitor monitor) { if (monitor == null) { return; } - final List monitorList = Collections.singletonList(monitor); + final List monitorList = Collections.singletonList(monitor); MONITOR_LOCK_OBJECT.lock(); try { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPlugin.java index 9a04ea849..2d0592a6a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPlugin.java @@ -79,9 +79,9 @@ public class HostMonitoringConnectionPlugin extends AbstractConnectionPlugin Collections.unmodifiableSet(new HashSet<>(Collections.singletonList("*"))); protected @NonNull Properties properties; - private final @NonNull Supplier monitorServiceSupplier; + private final @NonNull Supplier monitorServiceSupplier; private final @NonNull PluginService pluginService; - private MonitorService monitorService; + private HostMonitorService monitorService; private final RdsUtils rdsHelper; private HostSpec monitoringHostSpec; @@ -99,13 +99,13 @@ public class HostMonitoringConnectionPlugin extends AbstractConnectionPlugin */ public HostMonitoringConnectionPlugin( final @NonNull PluginService pluginService, final @NonNull Properties properties) { - this(pluginService, properties, () -> new MonitorServiceImpl(pluginService), new RdsUtils()); + this(pluginService, properties, () -> new HostMonitorServiceImpl(pluginService), new RdsUtils()); } HostMonitoringConnectionPlugin( final @NonNull PluginService pluginService, final @NonNull Properties properties, - final @NonNull Supplier monitorServiceSupplier, + final @NonNull Supplier monitorServiceSupplier, final RdsUtils rdsHelper) { this.pluginService = pluginService; this.properties = properties; @@ -119,7 +119,7 @@ public Set getSubscribedMethods() { } /** - * Executes the given SQL function with {@link MonitorImpl} if connection monitoring is enabled. + * Executes the given SQL function with {@link HostMonitorImpl} if connection monitoring is enabled. * Otherwise, executes the SQL function directly. */ @Override @@ -147,7 +147,7 @@ public T execute( initMonitorService(); T result; - MonitorConnectionContext monitorContext = null; + HostMonitorConnectionContext monitorContext = null; try { LOGGER.finest( diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitor.java similarity index 87% rename from wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/Monitor.java rename to wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitor.java index 5689db2ce..8c0300ccd 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitor.java @@ -20,9 +20,9 @@ * Interface for monitors. This class uses background threads to monitor servers with one or more * connections for more efficient failure detection during method execution. */ -public interface Monitor extends AutoCloseable, Runnable { +public interface HostMonitor extends AutoCloseable, Runnable { - void startMonitoring(MonitorConnectionContext context); + void startMonitoring(HostMonitorConnectionContext context); boolean canDispose(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorConnectionContext.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorConnectionContext.java similarity index 94% rename from wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorConnectionContext.java rename to wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorConnectionContext.java index 806047147..c8c25da8d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorConnectionContext.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorConnectionContext.java @@ -25,7 +25,7 @@ * Monitoring context for each connection. This contains each connection's criteria for whether a * server should be considered unhealthy. The context is shared between the main thread and the monitor thread. */ -public class MonitorConnectionContext { +public class HostMonitorConnectionContext { private final AtomicReference> connectionToAbortRef; private final AtomicBoolean nodeUnhealthy = new AtomicBoolean(false); @@ -35,7 +35,7 @@ public class MonitorConnectionContext { * * @param connectionToAbort A reference to the connection associated with this context that will be aborted. */ - public MonitorConnectionContext(final Connection connectionToAbort) { + public HostMonitorConnectionContext(final Connection connectionToAbort) { this.connectionToAbortRef = new AtomicReference<>(new WeakReference<>(connectionToAbort)); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java similarity index 93% rename from wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java rename to wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java index 31bca34b0..5f4a05f15 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java @@ -50,16 +50,16 @@ * This class uses a background thread to monitor a particular server with one or more active {@link * Connection}. */ -public class MonitorImpl implements Monitor { +public class HostMonitorImpl implements HostMonitor { - private static final Logger LOGGER = Logger.getLogger(MonitorImpl.class.getName()); + private static final Logger LOGGER = Logger.getLogger(HostMonitorImpl.class.getName()); private static final long THREAD_SLEEP_NANO = TimeUnit.MILLISECONDS.toNanos(100); private static final String MONITORING_PROPERTY_PREFIX = "monitoring-"; protected static final Executor ABORT_EXECUTOR = Executors.newSingleThreadExecutor(); - private final Queue> activeContexts = new ConcurrentLinkedQueue<>(); - private final Map>> newContexts = + private final Queue> activeContexts = new ConcurrentLinkedQueue<>(); + private final Map>> newContexts = new ConcurrentHashMap<>(); private final PluginService pluginService; private final TelemetryFactory telemetryFactory; @@ -92,7 +92,7 @@ public class MonitorImpl implements Monitor { * Store the monitoring configuration for a connection. * * @param pluginService A service for creating new connections. - * @param hostSpec The {@link HostSpec} of the server this {@link MonitorImpl} + * @param hostSpec The {@link HostSpec} of the server this {@link HostMonitorImpl} * instance is monitoring. * @param properties The {@link Properties} containing additional monitoring * configuration. @@ -101,7 +101,7 @@ public class MonitorImpl implements Monitor { * @param failureDetectionCount A failure detection count. * @param abortedConnectionsCounter Aborted connection telemetry counter. */ - public MonitorImpl( + public HostMonitorImpl( final @NonNull PluginService pluginService, final @NonNull HostSpec hostSpec, final @NonNull Properties properties, @@ -163,7 +163,7 @@ protected long getActiveContextSize() { } @Override - public void startMonitoring(final MonitorConnectionContext context) { + public void startMonitoring(final HostMonitorConnectionContext context) { if (this.stopped.get()) { LOGGER.warning(() -> Messages.get("MonitorImpl.monitorIsStopped", new Object[] {this.hostSpec.getHost()})); } @@ -172,7 +172,7 @@ public void startMonitoring(final MonitorConnectionContext context) { long startMonitoringTimeNano = this.truncateNanoToSeconds( currentTimeNano + this.failureDetectionTimeNano); - Queue> queue = + Queue> queue = this.newContexts.computeIfAbsent( startMonitoringTimeNano, (key) -> new ConcurrentLinkedQueue<>()); @@ -209,14 +209,14 @@ public void newContextRun() { // Get entries with key (that is a time in nanos) less or equal than current time. .filter(entry -> entry.getKey() < currentTimeNano) .forEach(entry -> { - final Queue> queue = entry.getValue(); + final Queue> queue = entry.getValue(); processedKeys.add(entry.getKey()); // Each value of found entry is a queue of monitoring contexts awaiting active monitoring. // Add all contexts to an active monitoring contexts queue. // Ignore disposed contexts. - WeakReference contextWeakRef; + WeakReference contextWeakRef; while ((contextWeakRef = queue.poll()) != null) { - MonitorConnectionContext context = contextWeakRef.get(); + HostMonitorConnectionContext context = contextWeakRef.get(); if (context != null && context.isActive()) { this.activeContexts.add(contextWeakRef); } @@ -270,15 +270,15 @@ public void run() { this.pluginService.setAvailability(this.hostSpec.asAliases(), HostAvailability.NOT_AVAILABLE); } - final List> tmpActiveContexts = new ArrayList<>(); - WeakReference monitorContextWeakRef; + final List> tmpActiveContexts = new ArrayList<>(); + WeakReference monitorContextWeakRef; while ((monitorContextWeakRef = this.activeContexts.poll()) != null) { if (this.stopped.get()) { break; } - MonitorConnectionContext monitorContext = monitorContextWeakRef.get(); + HostMonitorConnectionContext monitorContext = monitorContextWeakRef.get(); if (monitorContext == null) { continue; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorInitializer.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorInitializer.java similarity index 88% rename from wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorInitializer.java rename to wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorInitializer.java index 9027ccc5a..f38d004c3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorInitializer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorInitializer.java @@ -20,10 +20,10 @@ import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.util.telemetry.TelemetryCounter; -/** Interface for initialize a new {@link MonitorImpl}. */ +/** Interface for initialize a new {@link HostMonitorImpl}. */ @FunctionalInterface -public interface MonitorInitializer { - Monitor createMonitor( +public interface HostMonitorInitializer { + HostMonitor createMonitor( HostSpec hostSpec, Properties properties, final int failureDetectionTimeMillis, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorService.java similarity index 78% rename from wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorService.java rename to wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorService.java index 20512dc4c..35a934057 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorService.java @@ -24,9 +24,9 @@ * Interface for monitor services. This class implements ways to start and stop monitoring servers * when connections are created. */ -public interface MonitorService { +public interface HostMonitorService { - MonitorConnectionContext startMonitoring( + HostMonitorConnectionContext startMonitoring( Connection connectionToAbort, HostSpec hostSpec, Properties properties, @@ -35,13 +35,13 @@ MonitorConnectionContext startMonitoring( int failureDetectionCount); /** - * Stop monitoring for a connection represented by the given {@link MonitorConnectionContext}. - * Removes the context from the {@link MonitorImpl}. + * Stop monitoring for a connection represented by the given {@link HostMonitorConnectionContext}. + * Removes the context from the {@link HostMonitorImpl}. * - * @param context The {@link MonitorConnectionContext} representing a connection. + * @param context The {@link HostMonitorConnectionContext} representing a connection. * @param connectionToAbort A connection to abort. */ - void stopMonitoring(MonitorConnectionContext context, Connection connectionToAbort); + void stopMonitoring(HostMonitorConnectionContext context, Connection connectionToAbort); void releaseResources(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorServiceImpl.java similarity index 86% rename from wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java rename to wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorServiceImpl.java index 556152ba0..ff649394d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorServiceImpl.java @@ -36,9 +36,9 @@ * This class handles the creation and clean up of monitoring threads to servers with one or more * active connections. */ -public class MonitorServiceImpl implements MonitorService { +public class HostMonitorServiceImpl implements HostMonitorService { - private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); + private static final Logger LOGGER = Logger.getLogger(HostMonitorServiceImpl.class.getName()); public static final AwsWrapperProperty MONITOR_DISPOSAL_TIME_MS = new AwsWrapperProperty( "monitorDisposalTime", @@ -49,9 +49,9 @@ public class MonitorServiceImpl implements MonitorService { protected static final Executor ABORT_EXECUTOR = Executors.newSingleThreadExecutor(); // TODO: remove and submit monitors to MonitorService instead - protected static final SlidingExpirationCacheWithCleanupThread monitors = + protected static final SlidingExpirationCacheWithCleanupThread monitors = new SlidingExpirationCacheWithCleanupThread<>( - Monitor::canDispose, + HostMonitor::canDispose, (monitor) -> { try { monitor.close(); @@ -62,11 +62,11 @@ public class MonitorServiceImpl implements MonitorService { CACHE_CLEANUP_NANO); protected final PluginService pluginService; - protected final MonitorInitializer monitorInitializer; + protected final HostMonitorInitializer monitorInitializer; protected final TelemetryFactory telemetryFactory; protected final TelemetryCounter abortedConnectionsCounter; - public MonitorServiceImpl(final @NonNull PluginService pluginService) { + public HostMonitorServiceImpl(final @NonNull PluginService pluginService) { this( pluginService, (hostSpec, @@ -75,7 +75,7 @@ public MonitorServiceImpl(final @NonNull PluginService pluginService) { failureDetectionIntervalMillis, failureDetectionCount, abortedConnectionsCounter) -> - new MonitorImpl( + new HostMonitorImpl( pluginService, hostSpec, properties, @@ -85,9 +85,9 @@ public MonitorServiceImpl(final @NonNull PluginService pluginService) { abortedConnectionsCounter)); } - MonitorServiceImpl( + HostMonitorServiceImpl( final @NonNull PluginService pluginService, - final @NonNull MonitorInitializer monitorInitializer) { + final @NonNull HostMonitorInitializer monitorInitializer) { this.pluginService = pluginService; this.telemetryFactory = pluginService.getTelemetryFactory(); this.abortedConnectionsCounter = telemetryFactory.createCounter("efm2.connections.aborted"); @@ -106,7 +106,7 @@ public static void closeAllMonitors() { } @Override - public MonitorConnectionContext startMonitoring( + public HostMonitorConnectionContext startMonitoring( final Connection connectionToAbort, final HostSpec hostSpec, final Properties properties, @@ -114,14 +114,14 @@ public MonitorConnectionContext startMonitoring( final int failureDetectionIntervalMillis, final int failureDetectionCount) { - final Monitor monitor = this.getMonitor( + final HostMonitor monitor = this.getMonitor( hostSpec, properties, failureDetectionTimeMillis, failureDetectionIntervalMillis, failureDetectionCount); - final MonitorConnectionContext context = new MonitorConnectionContext(connectionToAbort); + final HostMonitorConnectionContext context = new HostMonitorConnectionContext(connectionToAbort); monitor.startMonitoring(context); return context; @@ -129,7 +129,7 @@ public MonitorConnectionContext startMonitoring( @Override public void stopMonitoring( - @NonNull final MonitorConnectionContext context, + @NonNull final HostMonitorConnectionContext context, @NonNull Connection connectionToAbort) { if (context.shouldAbort()) { @@ -156,16 +156,16 @@ public void releaseResources() { } /** - * Get or create a {@link MonitorImpl} for a server. + * Get or create a {@link HostMonitorImpl} for a server. * * @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 MonitorImpl} object associated with a specific server. + * @return A {@link HostMonitorImpl} object associated with a specific server. */ - protected Monitor getMonitor( + protected HostMonitor getMonitor( final HostSpec hostSpec, final Properties properties, final int failureDetectionTimeMillis, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPlugin.java index c7d3d472c..b68ad1b6a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitoringConnectionPlugin.java @@ -78,9 +78,9 @@ public class HostMonitoringConnectionPlugin extends AbstractConnectionPlugin Collections.unmodifiableSet(new HashSet<>(Collections.singletonList("*"))); protected @NonNull Properties properties; - private final @NonNull Supplier monitorServiceSupplier; + private final @NonNull Supplier monitorServiceSupplier; private final @NonNull PluginService pluginService; - private MonitorService monitorService; + private HostMonitorService monitorService; private final RdsUtils rdsHelper; private HostSpec monitoringHostSpec; @@ -98,13 +98,13 @@ public class HostMonitoringConnectionPlugin extends AbstractConnectionPlugin */ public HostMonitoringConnectionPlugin( final @NonNull PluginService pluginService, final @NonNull Properties properties) { - this(pluginService, properties, () -> new MonitorServiceImpl(pluginService), new RdsUtils()); + this(pluginService, properties, () -> new HostMonitorServiceImpl(pluginService), new RdsUtils()); } HostMonitoringConnectionPlugin( final @NonNull PluginService pluginService, final @NonNull Properties properties, - final @NonNull Supplier monitorServiceSupplier, + final @NonNull Supplier monitorServiceSupplier, final RdsUtils rdsHelper) { this.pluginService = pluginService; this.properties = properties; @@ -118,7 +118,7 @@ public Set getSubscribedMethods() { } /** - * Executes the given SQL function with {@link MonitorImpl} if connection monitoring is enabled. + * Executes the given SQL function with {@link HostMonitorImpl} if connection monitoring is enabled. * Otherwise, executes the SQL function directly. */ @Override @@ -146,7 +146,7 @@ public T execute( initMonitorService(); T result; - MonitorConnectionContext monitorContext = null; + HostMonitorConnectionContext monitorContext = null; try { LOGGER.finest( diff --git a/wrapper/src/test/java/integration/container/tests/AdvancedPerformanceTest.java b/wrapper/src/test/java/integration/container/tests/AdvancedPerformanceTest.java index 22db0293d..4555f332c 100644 --- a/wrapper/src/test/java/integration/container/tests/AdvancedPerformanceTest.java +++ b/wrapper/src/test/java/integration/container/tests/AdvancedPerformanceTest.java @@ -65,8 +65,8 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.provider.Arguments; import software.amazon.jdbc.PropertyDefinition; -import software.amazon.jdbc.plugin.efm.MonitorThreadContainer; -import software.amazon.jdbc.plugin.efm2.MonitorServiceImpl; +import software.amazon.jdbc.plugin.efm.HostMonitorThreadContainer; +import software.amazon.jdbc.plugin.efm2.HostMonitorServiceImpl; import software.amazon.jdbc.plugin.failover.FailoverSuccessSQLException; import software.amazon.jdbc.util.StringUtils; @@ -687,8 +687,8 @@ private void ensureClusterHealthy() throws InterruptedException { TestAuroraHostListProvider.clearCache(); TestPluginServiceImpl.clearHostAvailabilityCache(); - MonitorThreadContainer.releaseInstance(); - MonitorServiceImpl.closeAllMonitors(); + HostMonitorThreadContainer.releaseInstance(); + HostMonitorServiceImpl.closeAllMonitors(); } private static Stream generateParams() { diff --git a/wrapper/src/test/java/integration/container/tests/PerformanceTest.java b/wrapper/src/test/java/integration/container/tests/PerformanceTest.java index 8df6c1669..2519cd041 100644 --- a/wrapper/src/test/java/integration/container/tests/PerformanceTest.java +++ b/wrapper/src/test/java/integration/container/tests/PerformanceTest.java @@ -64,8 +64,8 @@ import software.amazon.jdbc.hostlistprovider.RdsHostListProvider; import software.amazon.jdbc.hostlistprovider.monitoring.MonitoringRdsHostListProvider; import software.amazon.jdbc.plugin.OpenedConnectionTracker; -import software.amazon.jdbc.plugin.efm.MonitorThreadContainer; -import software.amazon.jdbc.plugin.efm2.MonitorServiceImpl; +import software.amazon.jdbc.plugin.efm.HostMonitorThreadContainer; +import software.amazon.jdbc.plugin.efm2.HostMonitorServiceImpl; import software.amazon.jdbc.plugin.failover.FailoverConnectionPlugin; import software.amazon.jdbc.util.StringUtils; @@ -145,8 +145,8 @@ public void test_FailureDetectionTime_EnhancedMonitoringEnabled(final String efm throws IOException { OpenedConnectionTracker.clearCache(); - MonitorThreadContainer.releaseInstance(); - MonitorServiceImpl.closeAllMonitors(); + HostMonitorThreadContainer.releaseInstance(); + HostMonitorServiceImpl.closeAllMonitors(); AuroraHostListProvider.clearAll(); MonitoringRdsHostListProvider.clearCache(); @@ -227,8 +227,8 @@ public void test_FailureDetectionTime_FailoverAndEnhancedMonitoringEnabled(final throws IOException { OpenedConnectionTracker.clearCache(); - MonitorThreadContainer.releaseInstance(); - MonitorServiceImpl.closeAllMonitors(); + HostMonitorThreadContainer.releaseInstance(); + HostMonitorServiceImpl.closeAllMonitors(); AuroraHostListProvider.clearAll(); MonitoringRdsHostListProvider.clearCache(); @@ -315,8 +315,8 @@ public void test_FailoverTime_SocketTimeout() throws IOException { private void test_FailoverTime_SocketTimeout(final String plugins) throws IOException { OpenedConnectionTracker.clearCache(); - MonitorThreadContainer.releaseInstance(); - MonitorServiceImpl.closeAllMonitors(); + HostMonitorThreadContainer.releaseInstance(); + HostMonitorServiceImpl.closeAllMonitors(); AuroraHostListProvider.clearAll(); MonitoringRdsHostListProvider.clearCache(); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorConnectionContextTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostHostMonitorConnectionContextTest.java similarity index 94% rename from wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorConnectionContextTest.java rename to wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostHostMonitorConnectionContextTest.java index e307394ba..7066bbe3c 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorConnectionContextTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostHostMonitorConnectionContextTest.java @@ -35,24 +35,24 @@ import org.mockito.MockitoAnnotations; import software.amazon.jdbc.util.telemetry.TelemetryCounter; -class MonitorConnectionContextTest { +class HostHostMonitorConnectionContextTest { private static final long FAILURE_DETECTION_TIME_MILLIS = 10; private static final long FAILURE_DETECTION_INTERVAL_MILLIS = 100; private static final long FAILURE_DETECTION_COUNT = 3; private static final long VALIDATION_INTERVAL_MILLIS = 50; - private MonitorConnectionContext context; + private HostMonitorConnectionContext context; private AutoCloseable closeable; @Mock Connection connectionToAbort; - @Mock Monitor monitor; + @Mock HostMonitor monitor; @Mock TelemetryCounter abortedConnectionsCounter; @BeforeEach void init() { closeable = MockitoAnnotations.openMocks(this); context = - new MonitorConnectionContext( + new HostMonitorConnectionContext( monitor, null, FAILURE_DETECTION_TIME_MILLIS, @@ -134,7 +134,7 @@ void test_updateConnectionStatus_inactiveContext(boolean isValid) { final long currentTime = System.nanoTime(); final long statusCheckStartTime = System.nanoTime() - FAILURE_DETECTION_TIME_MILLIS; - final MonitorConnectionContext spyContext = spy(context); + final HostMonitorConnectionContext spyContext = spy(context); spyContext.updateConnectionStatus("test-node", statusCheckStartTime, currentTime, isValid); @@ -148,7 +148,7 @@ void test_updateConnectionStatus() { final long statusCheckStartTime = System.nanoTime() - 1000; context.setInactive(); - final MonitorConnectionContext spyContext = spy(context); + final HostMonitorConnectionContext spyContext = spy(context); spyContext.updateConnectionStatus("test-node", statusCheckStartTime, currentTime, true); @@ -159,7 +159,7 @@ void test_updateConnectionStatus() { @Test void test_abortConnection_ignoresSqlException() throws SQLException { context = - new MonitorConnectionContext( + new HostMonitorConnectionContext( monitor, connectionToAbort, FAILURE_DETECTION_TIME_MILLIS, diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorServiceImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostHostMonitorServiceImplTest.java similarity index 85% rename from wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorServiceImplTest.java rename to wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostHostMonitorServiceImplTest.java index ce70f0f12..e89279279 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorServiceImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostHostMonitorServiceImplTest.java @@ -49,7 +49,7 @@ import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -class MonitorServiceImplTest { +class HostHostMonitorServiceImplTest { private static final Set NODE_KEYS = new HashSet<>(Collections.singletonList("any.node.domain")); @@ -57,10 +57,10 @@ class MonitorServiceImplTest { private static final int FAILURE_DETECTION_INTERVAL_MILLIS = 100; private static final int FAILURE_DETECTION_COUNT = 3; - @Mock private MonitorInitializer monitorInitializer; + @Mock private HostMonitorInitializer monitorInitializer; @Mock private ExecutorServiceInitializer executorServiceInitializer; - @Mock private Monitor monitorA; - @Mock private Monitor monitorB; + @Mock private HostMonitor monitorA; + @Mock private HostMonitor monitorB; @Mock private ExecutorService executorService; @Mock private Future task; @Mock private HostSpec hostSpec; @@ -71,28 +71,28 @@ class MonitorServiceImplTest { private Properties properties; private AutoCloseable closeable; - private MonitorServiceImpl monitorService; - private MonitorThreadContainer threadContainer; - private ArgumentCaptor contextCaptor; + private HostMonitorServiceImpl monitorService; + private HostMonitorThreadContainer threadContainer; + private ArgumentCaptor contextCaptor; @BeforeEach void init() { properties = new Properties(); closeable = MockitoAnnotations.openMocks(this); - contextCaptor = ArgumentCaptor.forClass(MonitorConnectionContext.class); + contextCaptor = ArgumentCaptor.forClass(HostMonitorConnectionContext.class); when(pluginService.getTelemetryFactory()).thenReturn(telemetryFactory); when(telemetryFactory.createCounter(anyString())).thenReturn(telemetryCounter); when(monitorInitializer.createMonitor( - any(HostSpec.class), any(Properties.class), any(MonitorThreadContainer.class))) + any(HostSpec.class), any(Properties.class), any(HostMonitorThreadContainer.class))) .thenReturn(monitorA, monitorB); when(executorServiceInitializer.createExecutorService()).thenReturn(executorService); - doReturn(task).when(executorService).submit(any(Monitor.class)); + doReturn(task).when(executorService).submit(any(HostMonitor.class)); - threadContainer = MonitorThreadContainer.getInstance(executorServiceInitializer); - monitorService = new MonitorServiceImpl(pluginService, monitorInitializer, executorServiceInitializer); + threadContainer = HostMonitorThreadContainer.getInstance(executorServiceInitializer); + monitorService = new HostMonitorServiceImpl(pluginService, monitorInitializer, executorServiceInitializer); } @AfterEach @@ -142,7 +142,7 @@ void test_startMonitoringCalledMultipleTimes() { void test_stopMonitoringWithInterruptedThread() { doNothing().when(monitorA).stopMonitoring(contextCaptor.capture()); - final MonitorConnectionContext context = + final HostMonitorConnectionContext context = monitorService.startMonitoring( connection, NODE_KEYS, @@ -162,7 +162,7 @@ void test_stopMonitoringWithInterruptedThread() { void test_stopMonitoringCalledTwice() { doNothing().when(monitorA).stopMonitoring(contextCaptor.capture()); - final MonitorConnectionContext context = + final HostMonitorConnectionContext context = monitorService.startMonitoring( connection, NODE_KEYS, @@ -213,11 +213,11 @@ void test_getMonitorCalledWithMultipleNodesInKeys() { final Set nodeKeysTwo = new HashSet<>(); nodeKeysTwo.add("nodeTwo.domain"); - final Monitor monitorOne = monitorService.getMonitor(nodeKeys, hostSpec, properties); + final HostMonitor monitorOne = monitorService.getMonitor(nodeKeys, hostSpec, properties); assertNotNull(monitorOne); // Should get the same monitor as before as contain the same key "nodeTwo.domain" - final Monitor monitorOneSame = monitorService.getMonitor(nodeKeysTwo, hostSpec, properties); + final HostMonitor monitorOneSame = monitorService.getMonitor(nodeKeysTwo, hostSpec, properties); assertNotNull(monitorOneSame); assertEquals(monitorOne, monitorOneSame); @@ -230,16 +230,16 @@ void test_getMonitorCalledWithDifferentNodeKeys() { final Set nodeKeys = new HashSet<>(); nodeKeys.add("nodeNEW.domain"); - final Monitor monitorOne = monitorService.getMonitor(nodeKeys, hostSpec, properties); + final HostMonitor monitorOne = monitorService.getMonitor(nodeKeys, hostSpec, properties); assertNotNull(monitorOne); // Ensuring monitor is the same one and not creating a new one - final Monitor monitorOneDupe = monitorService.getMonitor(nodeKeys, hostSpec, properties); + final HostMonitor monitorOneDupe = monitorService.getMonitor(nodeKeys, hostSpec, properties); assertEquals(monitorOne, monitorOneDupe); // Ensuring monitors are not the same as they have different keys // "any.node.domain" compared to "nodeNEW.domain" - final Monitor monitorTwo = monitorService.getMonitor(NODE_KEYS, hostSpec, properties); + final HostMonitor monitorTwo = monitorService.getMonitor(NODE_KEYS, hostSpec, properties); assertNotNull(monitorTwo); assertNotEquals(monitorOne, monitorTwo); } @@ -256,16 +256,16 @@ void test_getMonitorCalledWithSameKeysInDifferentNodeKeys() { final Set nodeKeysThree = new HashSet<>(); nodeKeysThree.add("nodeB"); - final Monitor monitorOne = monitorService.getMonitor(nodeKeys, hostSpec, properties); + final HostMonitor monitorOne = monitorService.getMonitor(nodeKeys, hostSpec, properties); assertNotNull(monitorOne); // Add a new key using the same monitor // Adding "nodeB" as a new key using the same monitor as "nodeA" - final Monitor monitorOneDupe = monitorService.getMonitor(nodeKeysTwo, hostSpec, properties); + final HostMonitor monitorOneDupe = monitorService.getMonitor(nodeKeysTwo, hostSpec, properties); assertEquals(monitorOne, monitorOneDupe); // Using new keyset but same node, "nodeB" should return same monitor - final Monitor monitorOneDupeAgain = + final HostMonitor monitorOneDupeAgain = monitorService.getMonitor(nodeKeysThree, hostSpec, properties); assertEquals(monitorOne, monitorOneDupeAgain); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitorImplTest.java similarity index 85% rename from wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java rename to wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitorImplTest.java index 558e2afba..5f7c537fc 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitorImplTest.java @@ -53,20 +53,20 @@ import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -class MonitorImplTest { +class HostMonitorImplTest { @Mock PluginService pluginService; @Mock Connection connection; @Mock HostSpec hostSpec; @Mock Properties properties; - @Mock MonitorConnectionContext contextWithShortInterval; - @Mock MonitorConnectionContext contextWithLongInterval; + @Mock HostMonitorConnectionContext contextWithShortInterval; + @Mock HostMonitorConnectionContext contextWithLongInterval; @Mock BooleanProperty booleanProperty; @Mock LongProperty longProperty; @Mock ExecutorServiceInitializer executorServiceInitializer; @Mock ExecutorService executorService; @Mock Future futureResult; - @Mock MonitorServiceImpl monitorService; + @Mock HostMonitorServiceImpl monitorService; @Mock TelemetryFactory telemetryFactory; @Mock TelemetryContext telemetryContext; @Mock TelemetryCounter telemetryCounter; @@ -76,8 +76,8 @@ class MonitorImplTest { private static final long LONG_INTERVAL_MILLIS = 300; private AutoCloseable closeable; - private MonitorImpl monitor; - private MonitorThreadContainer threadContainer; + private HostMonitorImpl monitor; + private HostMonitorThreadContainer threadContainer; @BeforeEach void init() throws SQLException { @@ -95,21 +95,21 @@ void init() throws SQLException { when(telemetryFactory.openTelemetryContext(eq(null), any())).thenReturn(telemetryContext); when(telemetryFactory.createCounter(anyString())).thenReturn(telemetryCounter); when(executorServiceInitializer.createExecutorService()).thenReturn(executorService); - threadContainer = MonitorThreadContainer.getInstance(executorServiceInitializer); + threadContainer = HostMonitorThreadContainer.getInstance(executorServiceInitializer); - monitor = spy(new MonitorImpl(pluginService, hostSpec, properties, 0L, threadContainer)); + monitor = spy(new HostMonitorImpl(pluginService, hostSpec, properties, 0L, threadContainer)); } @AfterEach void cleanUp() throws Exception { monitorService.releaseResources(); - MonitorThreadContainer.releaseInstance(); + HostMonitorThreadContainer.releaseInstance(); closeable.close(); } @Test void test_5_isConnectionHealthyWithNoExistingConnection() throws SQLException { - final MonitorImpl.ConnectionStatus status = + final HostMonitorImpl.ConnectionStatus status = monitor.checkConnectionStatus(SHORT_INTERVAL_MILLIS); verify(pluginService).forceConnect(any(HostSpec.class), any(Properties.class)); @@ -125,11 +125,11 @@ void test_6_isConnectionHealthyWithExistingConnection() throws SQLException { // Start up a monitoring connection. monitor.checkConnectionStatus(SHORT_INTERVAL_MILLIS); - final MonitorImpl.ConnectionStatus status1 = + final HostMonitorImpl.ConnectionStatus status1 = monitor.checkConnectionStatus(SHORT_INTERVAL_MILLIS); assertTrue(status1.isValid); - final MonitorImpl.ConnectionStatus status2 = + final HostMonitorImpl.ConnectionStatus status2 = monitor.checkConnectionStatus(SHORT_INTERVAL_MILLIS); assertFalse(status2.isValid); @@ -146,7 +146,7 @@ void test_7_isConnectionHealthyWithSQLException() throws SQLException { assertDoesNotThrow( () -> { - final MonitorImpl.ConnectionStatus status = + final HostMonitorImpl.ConnectionStatus status = monitor.checkConnectionStatus(SHORT_INTERVAL_MILLIS); assertFalse(status.isValid); assertTrue(status.elapsedTimeNano >= 0); @@ -155,8 +155,8 @@ void test_7_isConnectionHealthyWithSQLException() throws SQLException { @Test void test_8_runWithoutContext() { - final Map monitorMap = threadContainer.getMonitorMap(); - final Map> taskMap = threadContainer.getTasksMap(); + final Map monitorMap = threadContainer.getMonitorMap(); + final Map> taskMap = threadContainer.getTasksMap(); // Put monitor into container map final String nodeKey = "monitorA"; @@ -172,13 +172,13 @@ void test_8_runWithoutContext() { assertNull(taskMap.get(monitor)); // Clean-up - MonitorThreadContainer.releaseInstance(); + HostMonitorThreadContainer.releaseInstance(); } @RepeatedTest(1000) void test_9_runWithContext() { - final Map monitorMap = threadContainer.getMonitorMap(); - final Map> taskMap = threadContainer.getTasksMap(); + final Map monitorMap = threadContainer.getMonitorMap(); + final Map> taskMap = threadContainer.getTasksMap(); // Put monitor into container map final String nodeKey = "monitorA"; @@ -210,7 +210,7 @@ void test_9_runWithContext() { assertNull(taskMap.get(monitor)); // Clean-up - MonitorThreadContainer.releaseInstance(); + HostMonitorThreadContainer.releaseInstance(); } @Test @@ -219,8 +219,8 @@ void test_10_ensureStoppedMonitorIsRemovedFromMap() throws InterruptedException when(contextWithShortInterval.getExpectedActiveMonitoringStartTimeNano()).thenReturn(999999999999999L); doThrow(new InterruptedException("Test")).when(monitor).sleep(anyLong()); monitor.activeContexts.add(contextWithShortInterval); - final Map monitorMap = threadContainer.getMonitorMap(); - final Map> taskMap = threadContainer.getTasksMap(); + final Map monitorMap = threadContainer.getMonitorMap(); + final Map> taskMap = threadContainer.getTasksMap(); // Put monitor into container map final String nodeKey = "monitorA"; @@ -238,6 +238,6 @@ void test_10_ensureStoppedMonitorIsRemovedFromMap() throws InterruptedException assertNull(taskMap.get(monitor)); // Clean-up - MonitorThreadContainer.releaseInstance(); + HostMonitorThreadContainer.releaseInstance(); } } diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java index 30df221c9..98ad0bae3 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/HostMonitoringConnectionPluginTest.java @@ -83,11 +83,11 @@ class HostMonitoringConnectionPluginTest { Properties properties = new Properties(); @Mock HostSpec hostSpec; @Mock HostSpec hostSpec2; - @Mock Supplier supplier; + @Mock Supplier supplier; @Mock RdsUtils rdsUtils; - @Mock MonitorConnectionContext context; + @Mock HostMonitorConnectionContext context; @Mock ReentrantLock mockReentrantLock; - @Mock MonitorService monitorService; + @Mock HostMonitorService monitorService; @Mock JdbcCallable sqlFunction; private HostMonitoringConnectionPlugin plugin; private AutoCloseable closeable; diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedDefaultMonitorServiceTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedDefaultHostHostMonitorServiceTest.java similarity index 77% rename from wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedDefaultMonitorServiceTest.java rename to wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedDefaultHostHostMonitorServiceTest.java index c408d1398..b4e76d3ac 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedDefaultMonitorServiceTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedDefaultHostHostMonitorServiceTest.java @@ -60,17 +60,17 @@ import software.amazon.jdbc.util.telemetry.TelemetryFactory; /** - * Multithreaded tests for {@link MultiThreadedDefaultMonitorServiceTest}. Repeats each testcase + * Multithreaded tests for {@link MultiThreadedDefaultHostHostMonitorServiceTest}. Repeats each testcase * multiple times. Use a cyclic barrier to ensure threads start at the same time. */ -class MultiThreadedDefaultMonitorServiceTest { +class MultiThreadedDefaultHostHostMonitorServiceTest { - @Mock MonitorInitializer monitorInitializer; + @Mock HostMonitorInitializer monitorInitializer; @Mock ExecutorServiceInitializer executorServiceInitializer; @Mock ExecutorService service; @Mock Future taskA; @Mock HostSpec hostSpec; - @Mock Monitor monitor; + @Mock HostMonitor monitor; @Mock Properties properties; @Mock JdbcConnection connection; @Mock PluginService pluginService; @@ -90,24 +90,24 @@ class MultiThreadedDefaultMonitorServiceTest { "Test thread interrupted due to an unexpected exception."; private AutoCloseable closeable; - private ArgumentCaptor startMonitoringCaptor; - private ArgumentCaptor stopMonitoringCaptor; - private MonitorThreadContainer monitorThreadContainer; + private ArgumentCaptor startMonitoringCaptor; + private ArgumentCaptor stopMonitoringCaptor; + private HostMonitorThreadContainer monitorThreadContainer; @BeforeEach void init(TestInfo testInfo) { closeable = MockitoAnnotations.openMocks(this); - startMonitoringCaptor = ArgumentCaptor.forClass(MonitorConnectionContext.class); - stopMonitoringCaptor = ArgumentCaptor.forClass(MonitorConnectionContext.class); - monitorThreadContainer = MonitorThreadContainer.getInstance(); + startMonitoringCaptor = ArgumentCaptor.forClass(HostMonitorConnectionContext.class); + stopMonitoringCaptor = ArgumentCaptor.forClass(HostMonitorConnectionContext.class); + monitorThreadContainer = HostMonitorThreadContainer.getInstance(); CONCURRENT_TEST_MAP.computeIfAbsent(testInfo.getDisplayName(), k -> new AtomicBoolean(false)); when(monitorInitializer.createMonitor( - any(HostSpec.class), any(Properties.class), any(MonitorThreadContainer.class))) + any(HostSpec.class), any(Properties.class), any(HostMonitorThreadContainer.class))) .thenReturn(monitor); when(executorServiceInitializer.createExecutorService()).thenReturn(service); - doReturn(taskA).when(service).submit(any(Monitor.class)); + doReturn(taskA).when(service).submit(any(HostMonitor.class)); doNothing().when(monitor).startMonitoring(startMonitoringCaptor.capture()); doNothing().when(monitor).stopMonitoring(stopMonitoringCaptor.capture()); when(properties.getProperty(any(String.class))) @@ -126,7 +126,7 @@ void cleanUp(TestInfo testInfo) throws Exception { concurrentCounter.set(0); closeable.close(); - MonitorThreadContainer.releaseInstance(); + HostMonitorThreadContainer.releaseInstance(); } /** Ensure each test case was executed concurrently at least once. */ @@ -144,13 +144,13 @@ void test_1_startMonitoring_multipleConnectionsToDifferentNodes() throws ExecutionException, InterruptedException { final int numConnections = 10; final List> nodeKeyList = generateNodeKeys(numConnections, true); - final List services = generateServices(numConnections); + final List services = generateServices(numConnections); try { - final List contexts = + final List contexts = runStartMonitor(numConnections, services, nodeKeyList); - final List capturedContexts = startMonitoringCaptor.getAllValues(); + final List capturedContexts = startMonitoringCaptor.getAllValues(); assertEquals(numConnections, services.get(0).getThreadContainer().getMonitorMap().size()); assertTrue( @@ -158,7 +158,7 @@ void test_1_startMonitoring_multipleConnectionsToDifferentNodes() && contexts.containsAll(capturedContexts) && capturedContexts.containsAll(contexts)); verify(monitorInitializer, times(numConnections)) - .createMonitor(eq(hostSpec), eq(properties), any(MonitorThreadContainer.class)); + .createMonitor(eq(hostSpec), eq(properties), any(HostMonitorThreadContainer.class)); } finally { releaseResources(services); } @@ -169,13 +169,13 @@ void test_2_startMonitoring_multipleConnectionsToOneNode() throws InterruptedException, ExecutionException { final int numConnections = 10; final List> nodeKeyList = generateNodeKeys(numConnections, false); - final List services = generateServices(numConnections); + final List services = generateServices(numConnections); try { - final List contexts = + final List contexts = runStartMonitor(numConnections, services, nodeKeyList); - final List capturedContexts = startMonitoringCaptor.getAllValues(); + final List capturedContexts = startMonitoringCaptor.getAllValues(); assertEquals(1, services.get(0).getThreadContainer().getMonitorMap().size()); assertTrue( @@ -184,7 +184,7 @@ void test_2_startMonitoring_multipleConnectionsToOneNode() && capturedContexts.containsAll(contexts)); verify(monitorInitializer) - .createMonitor(eq(hostSpec), eq(properties), any(MonitorThreadContainer.class)); + .createMonitor(eq(hostSpec), eq(properties), any(HostMonitorThreadContainer.class)); } finally { releaseResources(services); } @@ -194,13 +194,13 @@ void test_2_startMonitoring_multipleConnectionsToOneNode() void test_3_stopMonitoring_multipleConnectionsToDifferentNodes() throws ExecutionException, InterruptedException { final int numConnections = 10; - final List contexts = generateContexts(numConnections, true); - final List services = generateServices(numConnections); + final List contexts = generateContexts(numConnections, true); + final List services = generateServices(numConnections); try { runStopMonitor(numConnections, services, contexts); - final List capturedContexts = stopMonitoringCaptor.getAllValues(); + final List capturedContexts = stopMonitoringCaptor.getAllValues(); assertTrue( (contexts.size() == capturedContexts.size()) && contexts.containsAll(capturedContexts) @@ -214,13 +214,13 @@ void test_3_stopMonitoring_multipleConnectionsToDifferentNodes() void test_4_stopMonitoring_multipleConnectionsToTheSameNode() throws ExecutionException, InterruptedException { final int numConnections = 10; - final List contexts = generateContexts(numConnections, false); - final List services = generateServices(numConnections); + final List contexts = generateContexts(numConnections, false); + final List services = generateServices(numConnections); try { runStopMonitor(numConnections, services, contexts); - final List capturedContexts = stopMonitoringCaptor.getAllValues(); + final List capturedContexts = stopMonitoringCaptor.getAllValues(); assertTrue( (contexts.size() == capturedContexts.size()) && contexts.containsAll(capturedContexts) @@ -231,7 +231,7 @@ void test_4_stopMonitoring_multipleConnectionsToTheSameNode() } /** - * Run {@link MonitorServiceImpl#startMonitoring(Connection, Set, HostSpec, Properties, int, int, + * Run {@link HostMonitorServiceImpl#startMonitoring(Connection, Set, HostSpec, Properties, int, int, * int)} concurrently in multiple threads. A {@link CountDownLatch} is used to ensure all threads * start at the same time. * @@ -242,16 +242,16 @@ void test_4_stopMonitoring_multipleConnectionsToTheSameNode() * @throws InterruptedException if a thread has been interrupted. * @throws ExecutionException if an exception occurred within a thread. */ - private List runStartMonitor( + private List runStartMonitor( final int numThreads, - final List services, + final List services, final List> nodeKeysList) throws InterruptedException, ExecutionException { final CountDownLatch latch = new CountDownLatch(1); - final List> threads = new ArrayList<>(); + final List> threads = new ArrayList<>(); for (int i = 0; i < numThreads; i++) { - final MonitorServiceImpl service = services.get(i); + final HostMonitorServiceImpl service = services.get(i); final Set nodeKeys = nodeKeysList.get(i); threads.add( @@ -270,7 +270,7 @@ private List runStartMonitor( concurrentCounter.getAndIncrement(); } - final MonitorConnectionContext context = + final HostMonitorConnectionContext context = service.startMonitoring( connection, nodeKeys, @@ -288,8 +288,8 @@ private List runStartMonitor( // Start all threads. latch.countDown(); - final List contexts = new ArrayList<>(); - for (final CompletableFuture thread : threads) { + final List contexts = new ArrayList<>(); + for (final CompletableFuture thread : threads) { contexts.add(thread.get()); } @@ -297,7 +297,7 @@ private List runStartMonitor( } /** - * Run {@link MonitorServiceImpl#stopMonitoring(MonitorConnectionContext)} concurrently in + * Run {@link HostMonitorServiceImpl#stopMonitoring(HostMonitorConnectionContext)} concurrently in * multiple threads. A {@link CountDownLatch} is used to ensure all threads start at the same * time. * @@ -309,15 +309,15 @@ private List runStartMonitor( */ private void runStopMonitor( final int numThreads, - final List services, - final List contexts) + final List services, + final List contexts) throws ExecutionException, InterruptedException { final CountDownLatch latch = new CountDownLatch(1); final List> threads = new ArrayList<>(); for (int i = 0; i < numThreads; i++) { - final MonitorServiceImpl service = services.get(i); - final MonitorConnectionContext context = contexts.get(i); + final HostMonitorServiceImpl service = services.get(i); + final HostMonitorConnectionContext context = contexts.get(i); threads.add( CompletableFuture.runAsync( @@ -380,16 +380,16 @@ private List> generateNodeKeys(final int numNodeKeys, final boolean * node keys. * @return the generated contexts. */ - private List generateContexts( + private List generateContexts( final int numContexts, final boolean diffContext) { final List> nodeKeysList = generateNodeKeys(numContexts, diffContext); - final List contexts = new ArrayList<>(); + final List contexts = new ArrayList<>(); nodeKeysList.forEach( nodeKeys -> { monitorThreadContainer.getOrCreateMonitor(nodeKeys, () -> monitor); contexts.add( - new MonitorConnectionContext( + new HostMonitorConnectionContext( monitor, null, FAILURE_DETECTION_TIME, @@ -402,15 +402,15 @@ private List generateContexts( } /** - * Create multiple {@link MonitorServiceImpl} objects. + * Create multiple {@link HostMonitorServiceImpl} objects. * * @param numServices The number of monitor services to create. * @return a list of monitor services. */ - private List generateServices(final int numServices) { - final List services = new ArrayList<>(); + private List generateServices(final int numServices) { + final List services = new ArrayList<>(); for (int i = 0; i < numServices; i++) { - services.add(new MonitorServiceImpl(pluginService, monitorInitializer, executorServiceInitializer)); + services.add(new HostMonitorServiceImpl(pluginService, monitorInitializer, executorServiceInitializer)); } return services; } @@ -418,10 +418,10 @@ private List generateServices(final int numServices) { /** * Release any resources used by the given services. * - * @param services The {@link MonitorServiceImpl} services to clean. + * @param services The {@link HostMonitorServiceImpl} services to clean. */ - private void releaseResources(final List services) { - for (final MonitorServiceImpl defaultMonitorService : services) { + private void releaseResources(final List services) { + for (final HostMonitorServiceImpl defaultMonitorService : services) { defaultMonitorService.releaseResources(); } } diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedMonitorThreadContainerTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedHostMonitorThreadContainerTest.java similarity index 83% rename from wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedMonitorThreadContainerTest.java rename to wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedHostMonitorThreadContainerTest.java index d8f06798b..38d0f3191 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedMonitorThreadContainerTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/MultiThreadedHostMonitorThreadContainerTest.java @@ -30,7 +30,7 @@ import org.mockito.MockitoAnnotations; @Disabled -public class MultiThreadedMonitorThreadContainerTest { +public class MultiThreadedHostMonitorThreadContainerTest { @Mock ExecutorServiceInitializer mockExecutorServiceInitializer; @Mock ExecutorService mockExecutorService; @@ -46,17 +46,17 @@ void init() { @AfterEach void cleanup() throws Exception { closeable.close(); - MonitorThreadContainer.releaseInstance(); + HostMonitorThreadContainer.releaseInstance(); } @RepeatedTest(value = 1000, name = "MonitorThreadContainer ThreadPoolExecutor is not closed prematurely") void testThreadPoolExecutorNotClosedPrematurely() throws InterruptedException { - MonitorThreadContainer.getInstance(mockExecutorServiceInitializer); + HostMonitorThreadContainer.getInstance(mockExecutorServiceInitializer); ExecutorService executorService = Executors.newCachedThreadPool(); - executorService.execute(() -> MonitorThreadContainer.getInstance(mockExecutorServiceInitializer)); + executorService.execute(() -> HostMonitorThreadContainer.getInstance(mockExecutorServiceInitializer)); Thread.sleep(3); - executorService.execute(MonitorThreadContainer::releaseInstance); + executorService.execute(HostMonitorThreadContainer::releaseInstance); executorService.shutdown(); verify(mockExecutorService, times(0)).shutdownNow(); From ba92785598da4a6a9edad22ac918e89df2e73f17 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 20 May 2025 15:54:53 -0700 Subject: [PATCH 117/149] PR suggestions --- .../amazon/jdbc/PartialPluginService.java | 4 ++-- .../monitoring/ClusterTopologyMonitorImpl.java | 3 ++- .../customendpoint/CustomEndpointMonitorImpl.java | 3 ++- .../ClusterAwareReaderFailoverHandler.java | 15 ++++++++++----- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java index efe128d06..2b6cf1a43 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java @@ -194,9 +194,9 @@ public String getOriginalUrl() { } @Override - @Deprecated public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHosts) { - this.storageService.set(this.initialConnectionHostSpec.getHost(), allowedAndBlockedHosts); + throw new UnsupportedOperationException( + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"setAllowedAndBlockedHosts"})); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index fa0052818..2e9e7b3aa 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -67,6 +67,7 @@ public class ClusterTopologyMonitorImpl extends AbstractMonitor implements Clust protected static final String MONITORING_PROPERTY_PREFIX = "topology-monitoring-"; protected static final Executor networkTimeoutExecutor = new SynchronousExecutor(); protected static final RdsUtils rdsHelper = new RdsUtils(); + protected static final long monitorTerminationTimeoutSec = 30; protected static final int defaultTopologyQueryTimeoutMs = 1000; protected static final int closeConnectionNetworkTimeoutMs = 500; @@ -123,7 +124,7 @@ public ClusterTopologyMonitorImpl( final String topologyQuery, final String writerTopologyQuery, final String nodeIdQuery) { - super(30); + super(monitorTerminationTimeoutSec); this.clusterId = clusterId; this.storageService = storageService; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index 80bc14680..0b31eb1fa 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -49,6 +49,7 @@ public class CustomEndpointMonitorImpl extends AbstractMonitor implements Custom // Keys are custom endpoint URLs, values are information objects for the associated custom endpoint. protected static final CacheMap customEndpointInfoCache = new CacheMap<>(); protected static final long CUSTOM_ENDPOINT_INFO_EXPIRATION_NANO = TimeUnit.MINUTES.toNanos(5); + protected static final long MONITOR_TERMINATION_TIMEOUT_SEC = 30; protected final RdsClient rdsClient; protected final HostSpec customEndpointHostSpec; @@ -81,7 +82,7 @@ public CustomEndpointMonitorImpl( Region region, long refreshRateNano, BiFunction rdsClientFunc) { - super(30); + super(MONITOR_TERMINATION_TIMEOUT_SEC); this.storageService = storageService; this.customEndpointHostSpec = customEndpointHostSpec; this.endpointIdentifier = endpointIdentifier; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java index 37043cacd..28f89324e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/failover/ClusterAwareReaderFailoverHandler.java @@ -276,9 +276,14 @@ public List getReaderHostsByPriority(final List hosts) { hostsByPriority.addAll(downHostList); final int numOfReaders = activeReaders.size() + downHostList.size(); - final boolean enableWriterInTaskB = - this.pluginService.getDialect().getFailoverRestrictions().contains(FailoverRestriction.ENABLE_WRITER_IN_TASK_B); - if (writerHost != null && (numOfReaders == 0 || enableWriterInTaskB)) { + if (writerHost == null) { + return hostsByPriority; + } + + boolean shouldIncludeWriter = numOfReaders == 0 + || this.pluginService.getDialect().getFailoverRestrictions() + .contains(FailoverRestriction.ENABLE_WRITER_IN_TASK_B); + if (shouldIncludeWriter) { hostsByPriority.add(writerHost); } @@ -402,7 +407,7 @@ public ReaderFailoverResult call() { LOGGER.fine( Messages.get( "ClusterAwareReaderFailoverHandler.readerRequired", - new Object[]{ this.newHost.getUrl(), role })); + new Object[] {this.newHost.getUrl(), role})); try { conn.close(); @@ -413,7 +418,7 @@ public ReaderFailoverResult call() { return FAILED_READER_FAILOVER_RESULT; } } catch (SQLException e) { - LOGGER.fine(Messages.get("ClusterAwareReaderFailoverHandler.errorGettingHostRole", new Object[]{e})); + LOGGER.fine(Messages.get("ClusterAwareReaderFailoverHandler.errorGettingHostRole", new Object[] {e})); try { conn.close(); From 79bbb4f433188795106cbd75672035f75b3edc4e Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 20 May 2025 16:30:28 -0700 Subject: [PATCH 118/149] Rename ServiceContainer to CompleteServicesContainer --- .../ConnectionPluginManagerBenchmarks.java | 14 ++--- .../jdbc/ConnectionPluginChainBuilder.java | 10 ++-- .../amazon/jdbc/ConnectionPluginFactory.java | 4 +- .../amazon/jdbc/ConnectionPluginManager.java | 16 +++--- .../java/software/amazon/jdbc/Driver.java | 9 ++-- .../amazon/jdbc/PartialPluginService.java | 16 +++--- .../amazon/jdbc/PluginServiceImpl.java | 22 ++++---- .../jdbc/ServiceContainerPluginFactory.java | 8 +-- .../jdbc/dialect/AuroraMysqlDialect.java | 8 +-- .../dialect/HostListProviderSupplier.java | 4 +- .../amazon/jdbc/ds/AwsWrapperDataSource.java | 9 ++-- .../AuroraHostListProvider.java | 6 +-- .../hostlistprovider/RdsHostListProvider.java | 8 +-- .../RdsMultiAzDbClusterListProvider.java | 6 +-- .../MonitoringRdsHostListProvider.java | 16 +++--- .../MonitoringRdsMultiAzHostListProvider.java | 6 +-- .../customendpoint/CustomEndpointPlugin.java | 30 +++++------ .../CustomEndpointPluginFactory.java | 6 +-- ...er.java => CompleteServicesContainer.java} | 2 +- ...ava => CompleteServicesContainerImpl.java} | 6 +-- .../connection/ConnectionServiceImpl.java | 19 +++---- .../jdbc/wrapper/ConnectionWrapper.java | 40 +++++++------- .../aurora/TestAuroraHostListProvider.java | 6 +-- .../aurora/TestPluginServiceImpl.java | 6 +-- .../ConnectionPluginChainBuilderTests.java | 14 ++--- .../jdbc/ConnectionPluginManagerTests.java | 12 ++--- .../amazon/jdbc/DialectDetectionTests.java | 12 ++--- .../amazon/jdbc/PluginServiceImplTests.java | 54 +++++++++---------- .../RdsHostListProviderTest.java | 10 ++-- .../RdsMultiAzDbClusterListProviderTest.java | 10 ++-- ...AwsSecretsManagerConnectionPluginTest.java | 12 ++--- .../CustomEndpointPluginTest.java | 10 ++-- .../dev/DeveloperConnectionPluginTest.java | 30 +++++------ 33 files changed, 222 insertions(+), 219 deletions(-) rename wrapper/src/main/java/software/amazon/jdbc/util/{ServiceContainer.java => CompleteServicesContainer.java} (97%) rename wrapper/src/main/java/software/amazon/jdbc/util/{ServiceContainerImpl.java => CompleteServicesContainerImpl.java} (96%) diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java index 3e1ea4a3e..81839d672 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java @@ -67,7 +67,7 @@ import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.profile.ConfigurationProfileBuilder; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; import software.amazon.jdbc.util.telemetry.GaugeCallable; import software.amazon.jdbc.util.telemetry.TelemetryContext; @@ -94,7 +94,7 @@ public class ConnectionPluginManagerBenchmarks { @Mock ConnectionProvider mockConnectionProvider; @Mock ConnectionWrapper mockConnectionWrapper; - @Mock ServiceContainer mockServiceContainer; + @Mock CompleteServicesContainer mockServicesContainer; @Mock PluginService mockPluginService; @Mock PluginManagerService mockPluginManagerService; @Mock TelemetryFactory mockTelemetryFactory; @@ -139,7 +139,7 @@ public void setUpIteration() throws Exception { when(mockResultSet.getString(eq(FIELD_SESSION_ID))).thenReturn(WRITER_SESSION_ID); when(mockResultSet.getString(eq(FIELD_SERVER_ID))) .thenReturn("myInstance1.domain.com", "myInstance2.domain.com", "myInstance3.domain.com"); - when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); + when(mockServicesContainer.getPluginService()).thenReturn(mockPluginService); when(mockPluginService.getCurrentConnection()).thenReturn(mockConnection); when(mockPluginService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); @@ -165,11 +165,11 @@ public void setUpIteration() throws Exception { null, mockConnectionWrapper, telemetryFactory); - pluginManager.init(mockServiceContainer, propertiesWithPlugins, mockPluginManagerService, configurationProfile); + pluginManager.init(mockServicesContainer, propertiesWithPlugins, mockPluginManagerService, configurationProfile); pluginManagerWithNoPlugins = new ConnectionPluginManager(mockConnectionProvider, null, mockConnectionWrapper, telemetryFactory); - pluginManagerWithNoPlugins.init(mockServiceContainer, propertiesWithoutPlugins, mockPluginManagerService, null); + pluginManagerWithNoPlugins.init(mockServicesContainer, propertiesWithoutPlugins, mockPluginManagerService, null); } @TearDown(Level.Iteration) @@ -181,7 +181,7 @@ public void tearDownIteration() throws Exception { public ConnectionPluginManager initConnectionPluginManagerWithNoPlugins() throws SQLException { final ConnectionPluginManager manager = new ConnectionPluginManager(mockConnectionProvider, null, mockConnectionWrapper, mockTelemetryFactory); - manager.init(mockServiceContainer, propertiesWithoutPlugins, mockPluginManagerService, configurationProfile); + manager.init(mockServicesContainer, propertiesWithoutPlugins, mockPluginManagerService, configurationProfile); return manager; } @@ -189,7 +189,7 @@ public ConnectionPluginManager initConnectionPluginManagerWithNoPlugins() throws public ConnectionPluginManager initConnectionPluginManagerWithPlugins() throws SQLException { final ConnectionPluginManager manager = new ConnectionPluginManager(mockConnectionProvider, null, mockConnectionWrapper, mockTelemetryFactory); - manager.init(mockServiceContainer, propertiesWithPlugins, mockPluginManagerService, configurationProfile); + manager.init(mockServicesContainer, propertiesWithPlugins, mockPluginManagerService, configurationProfile); return manager; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java index dbb22e6d3..2dc1504be 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java @@ -48,7 +48,7 @@ import software.amazon.jdbc.plugin.strategy.fastestresponse.FastestResponseStrategyPluginFactory; import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; @@ -134,7 +134,7 @@ public PluginFactoryInfo(final Class factory, } public List getPlugins( - final ServiceContainer serviceContainer, + final CompleteServicesContainer servicesContainer, final ConnectionProvider defaultConnProvider, final ConnectionProvider effectiveConnProvider, final PluginManagerService pluginManagerService, @@ -191,9 +191,9 @@ public List getPlugins( for (final ConnectionPluginFactory factory : factories) { if (factory instanceof ServiceContainerPluginFactory) { ServiceContainerPluginFactory serviceContainerPluginFactory = (ServiceContainerPluginFactory) factory; - plugins.add(serviceContainerPluginFactory.getInstance(serviceContainer, props)); + plugins.add(serviceContainerPluginFactory.getInstance(servicesContainer, props)); } else { - plugins.add(factory.getInstance(serviceContainer.getPluginService(), props)); + plugins.add(factory.getInstance(servicesContainer.getPluginService(), props)); } } @@ -206,7 +206,7 @@ public List getPlugins( // add default connection plugin to the tail final ConnectionPlugin defaultPlugin = new DefaultConnectionPlugin( - serviceContainer.getPluginService(), + servicesContainer.getPluginService(), defaultConnProvider, effectiveConnProvider, pluginManagerService); diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginFactory.java index 614547de4..2e7cbd896 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginFactory.java @@ -17,14 +17,14 @@ package software.amazon.jdbc; import java.util.Properties; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; /** * Interface for connection plugin factories. This class implements ways to initialize a connection * plugin. * * @apiNote Consider using {@link ServiceContainerPluginFactory} for new implementations as it provides access to all - * services in the {@link ServiceContainer}. + * services in the {@link CompleteServicesContainer}. */ public interface ConnectionPluginFactory { diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java index 47ac3cffd..ed3b308e7 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java @@ -51,7 +51,7 @@ import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.util.AsynchronousMethodsHelper; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.SqlMethodAnalyzer; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.WrapperUtils; @@ -111,7 +111,7 @@ public class ConnectionPluginManager implements CanReleaseResources, Wrapper { protected final @NonNull ConnectionProvider defaultConnProvider; protected final @Nullable ConnectionProvider effectiveConnProvider; protected final ConnectionWrapper connectionWrapper; - protected ServiceContainer serviceContainer; + protected CompleteServicesContainer servicesContainer; protected PluginService pluginService; protected TelemetryFactory telemetryFactory; @@ -182,27 +182,27 @@ public boolean isHeldByCurrentThread() { *

The {@link DefaultConnectionPlugin} will always be initialized and attached as the last * connection plugin in the chain. * - * @param serviceContainer the service container for the services required by this class. + * @param servicesContainer the service container for the services required by this class. * @param props the configuration of the connection * @param pluginManagerService a reference to a plugin manager service * @param configurationProfile a profile configuration defined by the user * @throws SQLException if errors occurred during the execution */ public void init( - final ServiceContainer serviceContainer, + final CompleteServicesContainer servicesContainer, final Properties props, final PluginManagerService pluginManagerService, @Nullable ConfigurationProfile configurationProfile) throws SQLException { this.props = props; - this.serviceContainer = serviceContainer; - this.pluginService = serviceContainer.getPluginService(); - this.telemetryFactory = serviceContainer.getTelemetryFactory(); + this.servicesContainer = servicesContainer; + this.pluginService = servicesContainer.getPluginService(); + this.telemetryFactory = servicesContainer.getTelemetryFactory(); ConnectionPluginChainBuilder pluginChainBuilder = new ConnectionPluginChainBuilder(); this.plugins = pluginChainBuilder.getPlugins( - this.serviceContainer, + this.servicesContainer, this.defaultConnProvider, this.effectiveConnProvider, pluginManagerService, diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index 8c6d828e0..49adea4e3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -63,8 +63,8 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; -import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.ServiceContainerImpl; +import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.CompleteServicesContainerImpl; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; @@ -232,10 +232,11 @@ public Connection connect(final String url, final Properties info) throws SQLExc effectiveConnectionProvider = configurationProfile.getConnectionProvider(); } - ServiceContainer serviceContainer = new ServiceContainerImpl(storageService, monitorService, telemetryFactory); + CompleteServicesContainer + servicesContainer = new CompleteServicesContainerImpl(storageService, monitorService, telemetryFactory); return new ConnectionWrapper( - serviceContainer, + servicesContainer, props, driverUrl, defaultConnectionProvider, diff --git a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java index 2b6cf1a43..9dc9e33b3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java @@ -47,7 +47,7 @@ import software.amazon.jdbc.states.SessionStateServiceImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.storage.StorageService; @@ -66,7 +66,7 @@ public class PartialPluginService implements PluginService, CanReleaseResources, protected static final long DEFAULT_HOST_AVAILABILITY_CACHE_EXPIRE_NANO = TimeUnit.MINUTES.toNanos(5); protected static final CacheMap hostAvailabilityExpiringCache = new CacheMap<>(); - protected final ServiceContainer serviceContainer; + protected final CompleteServicesContainer servicesContainer; protected final StorageService storageService; protected final ConnectionPluginManager pluginManager; protected final Properties props; @@ -90,14 +90,14 @@ public class PartialPluginService implements PluginService, CanReleaseResources, protected final ReentrantLock connectionSwitchLock = new ReentrantLock(); public PartialPluginService( - @NonNull final ServiceContainer serviceContainer, + @NonNull final CompleteServicesContainer servicesContainer, @NonNull final Properties props, @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, @NonNull final TargetDriverDialect targetDriverDialect, @NonNull final Dialect dbDialect) { this( - serviceContainer, + servicesContainer, new ExceptionManager(), props, originalUrl, @@ -109,7 +109,7 @@ public PartialPluginService( } public PartialPluginService( - @NonNull final ServiceContainer serviceContainer, + @NonNull final CompleteServicesContainer servicesContainer, @NonNull final ExceptionManager exceptionManager, @NonNull final Properties props, @NonNull final String originalUrl, @@ -118,9 +118,9 @@ public PartialPluginService( @NonNull final Dialect dbDialect, @Nullable final ConfigurationProfile configurationProfile, @Nullable final SessionStateService sessionStateService) { - this.serviceContainer = serviceContainer; - this.storageService = serviceContainer.getStorageService(); - this.pluginManager = serviceContainer.getConnectionPluginManager(); + this.servicesContainer = servicesContainer; + this.storageService = servicesContainer.getStorageService(); + this.pluginManager = servicesContainer.getConnectionPluginManager(); this.props = props; this.originalUrl = originalUrl; this.driverProtocol = targetDriverProtocol; diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index 8fccf35fe..147dddb2d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -51,7 +51,7 @@ import software.amazon.jdbc.states.SessionStateServiceImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.CacheMap; import software.amazon.jdbc.util.storage.StorageService; @@ -64,7 +64,7 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources, protected static final long DEFAULT_HOST_AVAILABILITY_CACHE_EXPIRE_NANO = TimeUnit.MINUTES.toNanos(5); protected static final CacheMap hostAvailabilityExpiringCache = new CacheMap<>(); - protected final ServiceContainer serviceContainer; + protected final CompleteServicesContainer servicesContainer; protected final StorageService storageService; protected final ConnectionPluginManager pluginManager; private final Properties props; @@ -89,7 +89,7 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources, protected final ReentrantLock connectionSwitchLock = new ReentrantLock(); public PluginServiceImpl( - @NonNull final ServiceContainer serviceContainer, + @NonNull final CompleteServicesContainer servicesContainer, @NonNull final Properties props, @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, @@ -97,7 +97,7 @@ public PluginServiceImpl( throws SQLException { this( - serviceContainer, + servicesContainer, new ExceptionManager(), props, originalUrl, @@ -109,14 +109,14 @@ public PluginServiceImpl( } public PluginServiceImpl( - @NonNull final ServiceContainer serviceContainer, + @NonNull final CompleteServicesContainer servicesContainer, @NonNull final Properties props, @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, @NonNull final TargetDriverDialect targetDriverDialect, @Nullable final ConfigurationProfile configurationProfile) throws SQLException { this( - serviceContainer, + servicesContainer, new ExceptionManager(), props, originalUrl, @@ -128,7 +128,7 @@ public PluginServiceImpl( } public PluginServiceImpl( - @NonNull final ServiceContainer serviceContainer, + @NonNull final CompleteServicesContainer servicesContainer, @NonNull final ExceptionManager exceptionManager, @NonNull final Properties props, @NonNull final String originalUrl, @@ -137,9 +137,9 @@ public PluginServiceImpl( @NonNull final TargetDriverDialect targetDriverDialect, @Nullable final ConfigurationProfile configurationProfile, @Nullable final SessionStateService sessionStateService) throws SQLException { - this.serviceContainer = serviceContainer; - this.storageService = serviceContainer.getStorageService(); - this.pluginManager = serviceContainer.getConnectionPluginManager(); + this.servicesContainer = servicesContainer; + this.storageService = servicesContainer.getStorageService(); + this.pluginManager = servicesContainer.getConnectionPluginManager(); this.props = props; this.originalUrl = originalUrl; this.driverProtocol = targetDriverProtocol; @@ -728,7 +728,7 @@ public void updateDialect(final @NonNull Connection connection) throws SQLExcept } final HostListProviderSupplier supplier = this.dialect.getHostListProvider(); - this.setHostListProvider(supplier.getProvider(this.props, this.originalUrl, this.serviceContainer)); + this.setHostListProvider(supplier.getProvider(this.props, this.originalUrl, this.servicesContainer)); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java index 8282dff88..a4c9a387a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java @@ -17,22 +17,22 @@ package software.amazon.jdbc; import java.util.Properties; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; /** * A factory for plugins that utilizes a ServiceContainer. This interface extends {@link ConnectionPluginFactory} to * provide additional flexibility in plugin instantiation while maintaining backward compatibility. * - *

Implementations of this interface can access all services in the {@link ServiceContainer} when creating connection + *

Implementations of this interface can access all services in the {@link CompleteServicesContainer} when creating connection * plugins, rather than being limited to just the {@link PluginService}

*/ public interface ServiceContainerPluginFactory extends ConnectionPluginFactory { /** * Get an instance of a {@link ConnectionPlugin}. * - * @param serviceContainer the service container containing the services to be used by the {@link ConnectionPlugin}. + * @param servicesContainer the service container containing the services to be used by the {@link ConnectionPlugin}. * @param props to be used by the {@link ConnectionPlugin}. * @return an instance of a {@link ConnectionPlugin}. */ - ConnectionPlugin getInstance(ServiceContainer serviceContainer, Properties props); + ConnectionPlugin getInstance(CompleteServicesContainer servicesContainer, Properties props); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java index 425dca624..1dab6319f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraMysqlDialect.java @@ -82,15 +82,15 @@ public boolean isDialect(final Connection connection) { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, serviceContainer) -> { - final PluginService pluginService = serviceContainer.getPluginService(); + return (properties, initialUrl, servicesContainer) -> { + final PluginService pluginService = servicesContainer.getPluginService(); final FailoverConnectionPlugin failover2Plugin = pluginService.getPlugin(FailoverConnectionPlugin.class); if (failover2Plugin != null) { return new MonitoringRdsHostListProvider( properties, initialUrl, - serviceContainer, + servicesContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, @@ -99,7 +99,7 @@ public HostListProviderSupplier getHostListProvider() { return new AuroraHostListProvider( properties, initialUrl, - serviceContainer, + servicesContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY); diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java index a3d9a8d22..aab6e544d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java @@ -19,12 +19,12 @@ import java.util.Properties; import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.HostListProvider; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; @FunctionalInterface public interface HostListProviderSupplier { @NonNull HostListProvider getProvider( final @NonNull Properties properties, final String initialUrl, - final @NonNull ServiceContainer serviceContainer); + final @NonNull CompleteServicesContainer servicesContainer); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index 30128d8c2..394baef4e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -50,8 +50,8 @@ import software.amazon.jdbc.util.CoreServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; -import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.ServiceContainerImpl; +import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.CompleteServicesContainerImpl; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; @@ -269,9 +269,10 @@ ConnectionWrapper createConnectionWrapper( final @NonNull TargetDriverDialect targetDriverDialect, final @Nullable ConfigurationProfile configurationProfile, final TelemetryFactory telemetryFactory) throws SQLException { - ServiceContainer serviceContainer = new ServiceContainerImpl(storageService, monitorService, telemetryFactory); + CompleteServicesContainer + servicesContainer = new CompleteServicesContainerImpl(storageService, monitorService, telemetryFactory); return new ConnectionWrapper( - serviceContainer, + servicesContainer, props, url, defaultProvider, diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java index 38b7324f9..2bbe9b177 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java @@ -19,7 +19,7 @@ import java.util.Properties; import java.util.logging.Logger; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; public class AuroraHostListProvider extends RdsHostListProvider { @@ -29,13 +29,13 @@ public class AuroraHostListProvider extends RdsHostListProvider { public AuroraHostListProvider( final Properties properties, final String originalUrl, - final ServiceContainer serviceContainer, + final CompleteServicesContainer servicesContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery) { super(properties, originalUrl, - serviceContainer, + servicesContainer, topologyQuery, nodeIdQuery, isReaderQuery); diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index 34ce4cbb2..dede28112 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -53,7 +53,7 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SynchronousExecutor; import software.amazon.jdbc.util.Utils; @@ -129,12 +129,12 @@ public class RdsHostListProvider implements DynamicHostListProvider { public RdsHostListProvider( final Properties properties, final String originalUrl, - final ServiceContainer serviceContainer, + final CompleteServicesContainer servicesContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery) { - this.hostListProviderService = serviceContainer.getHostListProviderService(); - this.storageService = serviceContainer.getStorageService(); + this.hostListProviderService = servicesContainer.getHostListProviderService(); + this.storageService = servicesContainer.getStorageService(); this.properties = properties; this.originalUrl = originalUrl; this.topologyQuery = topologyQuery; diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java index e10c11722..3d045132c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java @@ -32,7 +32,7 @@ import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; public class RdsMultiAzDbClusterListProvider extends RdsHostListProvider { private final String fetchWriterNodeQuery; @@ -42,7 +42,7 @@ public class RdsMultiAzDbClusterListProvider extends RdsHostListProvider { public RdsMultiAzDbClusterListProvider( final Properties properties, final String originalUrl, - final ServiceContainer serviceContainer, + final CompleteServicesContainer servicesContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery, @@ -51,7 +51,7 @@ public RdsMultiAzDbClusterListProvider( ) { super(properties, originalUrl, - serviceContainer, + servicesContainer, topologyQuery, nodeIdQuery, isReaderQuery); diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index 28e5d52f0..614c2a3df 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -30,7 +30,7 @@ import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.cleanup.CanReleaseResources; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.Topology; @@ -50,7 +50,7 @@ public class MonitoringRdsHostListProvider extends RdsHostListProvider PropertyDefinition.registerPluginProperties(MonitoringRdsHostListProvider.class); } - protected final ServiceContainer serviceContainer; + protected final CompleteServicesContainer servicesContainer; protected final CoreMonitorService monitorService; protected final PluginService pluginService; protected final long highRefreshRateNano; @@ -59,15 +59,15 @@ public class MonitoringRdsHostListProvider extends RdsHostListProvider public MonitoringRdsHostListProvider( final Properties properties, final String originalUrl, - final ServiceContainer serviceContainer, + final CompleteServicesContainer servicesContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery, final String writerTopologyQuery) { - super(properties, originalUrl, serviceContainer, topologyQuery, nodeIdQuery, isReaderQuery); - this.serviceContainer = serviceContainer; - this.monitorService = serviceContainer.getMonitorService(); - this.pluginService = serviceContainer.getPluginService(); + super(properties, originalUrl, servicesContainer, topologyQuery, nodeIdQuery, isReaderQuery); + this.servicesContainer = servicesContainer; + this.monitorService = servicesContainer.getMonitorService(); + this.pluginService = servicesContainer.getPluginService(); this.writerTopologyQuery = writerTopologyQuery; this.highRefreshRateNano = TimeUnit.MILLISECONDS.toNanos( CLUSTER_TOPOLOGY_HIGH_REFRESH_RATE_MS.getLong(this.properties)); @@ -100,7 +100,7 @@ protected ClusterTopologyMonitor initMonitor() throws SQLException { this.initialHostSpec, this.properties, monitorPluginService, - this.serviceContainer.getHostListProviderService(), + this.servicesContainer.getHostListProviderService(), this.clusterInstanceTemplate, this.refreshRateNano, this.highRefreshRateNano, diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java index 65e364d46..ba49cf86c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java @@ -19,7 +19,7 @@ import java.sql.SQLException; import java.util.Properties; import java.util.logging.Logger; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; public class MonitoringRdsMultiAzHostListProvider extends MonitoringRdsHostListProvider { @@ -31,7 +31,7 @@ public class MonitoringRdsMultiAzHostListProvider extends MonitoringRdsHostListP public MonitoringRdsMultiAzHostListProvider( final Properties properties, final String originalUrl, - final ServiceContainer serviceContainer, + final CompleteServicesContainer servicesContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery, @@ -40,7 +40,7 @@ public MonitoringRdsMultiAzHostListProvider( super( properties, originalUrl, - serviceContainer, + servicesContainer, topologyQuery, nodeIdQuery, isReaderQuery, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java index 7218cd58c..95cf8bd3d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java @@ -37,7 +37,7 @@ import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUtils; import software.amazon.jdbc.util.RegionUtils; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SubscribedMethodHelper; import software.amazon.jdbc.util.WrapperUtils; @@ -90,7 +90,7 @@ public class CustomEndpointPlugin extends AbstractConnectionPlugin { PropertyDefinition.registerPluginProperties(CustomEndpointPlugin.class); } - protected final ServiceContainer serviceContainer; + protected final CompleteServicesContainer servicesContainer; protected final PluginService pluginService; protected final StorageService storageService; protected final CoreMonitorService monitorService; @@ -110,12 +110,12 @@ public class CustomEndpointPlugin extends AbstractConnectionPlugin { /** * Constructs a new CustomEndpointPlugin instance. * - * @param serviceContainer The service container for the services required by this class. + * @param servicesContainer The service container for the services required by this class. * @param props The properties that the custom endpoint plugin should use. */ - public CustomEndpointPlugin(final ServiceContainer serviceContainer, final Properties props) { + public CustomEndpointPlugin(final CompleteServicesContainer servicesContainer, final Properties props) { this( - serviceContainer, + servicesContainer, props, (hostSpec, region) -> RdsClient.builder() @@ -127,19 +127,19 @@ public CustomEndpointPlugin(final ServiceContainer serviceContainer, final Prope /** * Constructs a new CustomEndpointPlugin instance. * - * @param serviceContainer The service container for the services required by this class. + * @param servicesContainer The service container for the services required by this class. * @param props The properties that the custom endpoint plugin should use. * @param rdsClientFunc The function to call to obtain an {@link RdsClient} instance. */ public CustomEndpointPlugin( - final ServiceContainer serviceContainer, + final CompleteServicesContainer servicesContainer, final Properties props, final BiFunction rdsClientFunc) { - this.serviceContainer = serviceContainer; - this.pluginService = serviceContainer.getPluginService(); - this.storageService = serviceContainer.getStorageService(); - this.monitorService = serviceContainer.getMonitorService(); - this.telemetryFactory = serviceContainer.getTelemetryFactory(); + this.servicesContainer = servicesContainer; + this.pluginService = servicesContainer.getPluginService(); + this.storageService = servicesContainer.getStorageService(); + this.monitorService = servicesContainer.getMonitorService(); + this.telemetryFactory = servicesContainer.getTelemetryFactory(); this.props = props; this.rdsClientFunc = rdsClientFunc; @@ -148,7 +148,7 @@ public CustomEndpointPlugin( this.waitOnCachedInfoDurationMs = WAIT_FOR_CUSTOM_ENDPOINT_INFO_TIMEOUT_MS.getInteger(this.props); this.idleMonitorExpirationMs = CUSTOM_ENDPOINT_MONITOR_IDLE_EXPIRATION_MS.getInteger(this.props); - TelemetryFactory telemetryFactory = serviceContainer.getTelemetryFactory(); + TelemetryFactory telemetryFactory = servicesContainer.getTelemetryFactory(); this.waitForInfoCounter = telemetryFactory.createCounter(TELEMETRY_WAIT_FOR_INFO_COUNTER); } @@ -207,7 +207,7 @@ public Connection connect( * @return {@link CustomEndpointMonitor} */ protected CustomEndpointMonitor createMonitorIfAbsent(Properties props) throws SQLException { - return this.serviceContainer.getMonitorService().runIfAbsent( + return this.servicesContainer.getMonitorService().runIfAbsent( CustomEndpointMonitorImpl.class, this.customEndpointHostSpec.getUrl(), this.storageService, @@ -219,7 +219,7 @@ protected CustomEndpointMonitor createMonitorIfAbsent(Properties props) throws S this.props, (connectionService, pluginService) -> new CustomEndpointMonitorImpl( this.storageService, - this.serviceContainer.getTelemetryFactory(), + this.servicesContainer.getTelemetryFactory(), this.customEndpointHostSpec, this.customEndpointId, this.region, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java index 15fcc6b20..bbc92a0e5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java @@ -22,7 +22,7 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.ServiceContainerPluginFactory; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; public class CustomEndpointPluginFactory implements ServiceContainerPluginFactory { @Override @@ -33,13 +33,13 @@ public ConnectionPlugin getInstance(final PluginService pluginService, final Pro } @Override - public ConnectionPlugin getInstance(final ServiceContainer serviceContainer, final Properties props) { + public ConnectionPlugin getInstance(final CompleteServicesContainer servicesContainer, final Properties props) { try { Class.forName("software.amazon.awssdk.services.rds.RdsClient"); } catch (final ClassNotFoundException e) { throw new RuntimeException(Messages.get("CustomEndpointPluginFactory.awsSdkNotInClasspath")); } - return new CustomEndpointPlugin(serviceContainer, props); + return new CustomEndpointPlugin(servicesContainer, props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainer.java similarity index 97% rename from wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java rename to wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainer.java index 57015b82c..1f2ae5fff 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainer.java @@ -24,7 +24,7 @@ import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -public interface ServiceContainer { +public interface CompleteServicesContainer { StorageService getStorageService(); CoreMonitorService getMonitorService(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainerImpl.java similarity index 96% rename from wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java rename to wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainerImpl.java index 36c7eb411..57d9721ea 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ServiceContainerImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainerImpl.java @@ -24,7 +24,7 @@ import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -public class ServiceContainerImpl implements ServiceContainer { +public class CompleteServicesContainerImpl implements CompleteServicesContainer { private StorageService storageService; private CoreMonitorService monitorService; private TelemetryFactory telemetryFactory; @@ -33,7 +33,7 @@ public class ServiceContainerImpl implements ServiceContainer { private PluginService pluginService; private PluginManagerService pluginManagerService; - public ServiceContainerImpl( + public CompleteServicesContainerImpl( StorageService storageService, CoreMonitorService monitorService, TelemetryFactory telemetryFactory, @@ -48,7 +48,7 @@ public ServiceContainerImpl( this.pluginManagerService = pluginManagerService; } - public ServiceContainerImpl( + public CompleteServicesContainerImpl( StorageService storageService, CoreMonitorService monitorService, TelemetryFactory telemetryFactory) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index 0b59222e1..1ce592515 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -26,8 +26,8 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.ServiceContainerImpl; +import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.CompleteServicesContainerImpl; import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -49,16 +49,17 @@ public ConnectionServiceImpl( Properties props) throws SQLException { this.targetDriverProtocol = targetDriverProtocol; - ServiceContainer serviceContainer = new ServiceContainerImpl(storageService, monitorService, telemetryFactory); + CompleteServicesContainer + servicesContainer = new CompleteServicesContainerImpl(storageService, monitorService, telemetryFactory); this.pluginManager = new ConnectionPluginManager( connectionProvider, null, null, telemetryFactory); - serviceContainer.setConnectionPluginManager(this.pluginManager); + servicesContainer.setConnectionPluginManager(this.pluginManager); PartialPluginService partialPluginService = new PartialPluginService( - serviceContainer, + servicesContainer, props, originalUrl, this.targetDriverProtocol, @@ -67,11 +68,11 @@ public ConnectionServiceImpl( ); this.pluginService = partialPluginService; - serviceContainer.setHostListProviderService(partialPluginService); - serviceContainer.setPluginService(partialPluginService); - serviceContainer.setPluginManagerService(partialPluginService); + servicesContainer.setHostListProviderService(partialPluginService); + servicesContainer.setPluginService(partialPluginService); + servicesContainer.setPluginManagerService(partialPluginService); - this.pluginManager.init(serviceContainer, props, partialPluginService, null); + this.pluginManager.init(servicesContainer, props, partialPluginService, null); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index 1672dcb62..93cf221ce 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -52,8 +52,8 @@ import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.ServiceContainerImpl; +import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.CompleteServicesContainerImpl; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; @@ -81,7 +81,7 @@ public class ConnectionWrapper implements Connection, CanReleaseResources { protected final ConnectionUrlParser connectionUrlParser = new ConnectionUrlParser(); public ConnectionWrapper( - @NonNull final ServiceContainer serviceContainer, + @NonNull final CompleteServicesContainer servicesContainer, @NonNull final Properties props, @NonNull final String url, @NonNull final ConnectionProvider defaultConnectionProvider, @@ -103,20 +103,20 @@ public ConnectionWrapper( defaultConnectionProvider, effectiveConnectionProvider, this, - serviceContainer.getTelemetryFactory()); - serviceContainer.setConnectionPluginManager(pluginManager); + servicesContainer.getTelemetryFactory()); + servicesContainer.setConnectionPluginManager(pluginManager); final PluginServiceImpl pluginService = new PluginServiceImpl( - serviceContainer, + servicesContainer, props, url, this.targetDriverProtocol, driverDialect, this.configurationProfile); - serviceContainer.setHostListProviderService(pluginService); - serviceContainer.setPluginService(pluginService); - serviceContainer.setPluginManagerService(pluginService); + servicesContainer.setHostListProviderService(pluginService); + servicesContainer.setPluginService(pluginService); + servicesContainer.setPluginManagerService(pluginService); - init(props, serviceContainer, defaultConnectionProvider, driverDialect); + init(props, servicesContainer, defaultConnectionProvider, driverDialect); if (PropertyDefinition.LOG_UNCLOSED_CONNECTIONS.getBoolean(props)) { this.openConnectionStacktrace = new Throwable(Messages.get("ConnectionWrapper.unclosedConnectionInstantiated")); @@ -143,7 +143,7 @@ protected ConnectionWrapper( throw new IllegalArgumentException("url"); } - ServiceContainer serviceContainer = new ServiceContainerImpl( + CompleteServicesContainer servicesContainer = new CompleteServicesContainerImpl( storageService, monitorService, telemetryFactory, @@ -153,24 +153,24 @@ protected ConnectionWrapper( pluginManagerService ); - init(props, serviceContainer, defaultConnectionProvider, driverDialect); + init(props, servicesContainer, defaultConnectionProvider, driverDialect); } protected void init(final Properties props, - final ServiceContainer serviceContainer, + final CompleteServicesContainer servicesContainer, final ConnectionProvider defaultConnectionProvider, final TargetDriverDialect driverDialect) throws SQLException { - this.pluginManager = serviceContainer.getConnectionPluginManager(); - this.telemetryFactory = serviceContainer.getTelemetryFactory(); - this.pluginService = serviceContainer.getPluginService(); - this.hostListProviderService = serviceContainer.getHostListProviderService(); - this.pluginManagerService = serviceContainer.getPluginManagerService(); + this.pluginManager = servicesContainer.getConnectionPluginManager(); + this.telemetryFactory = servicesContainer.getTelemetryFactory(); + this.pluginService = servicesContainer.getPluginService(); + this.hostListProviderService = servicesContainer.getHostListProviderService(); + this.pluginManagerService = servicesContainer.getPluginManagerService(); - this.pluginManager.init(serviceContainer, props, pluginManagerService, this.configurationProfile); + this.pluginManager.init(servicesContainer, props, pluginManagerService, this.configurationProfile); final HostListProviderSupplier supplier = this.pluginService.getDialect().getHostListProvider(); if (supplier != null) { - final HostListProvider provider = supplier.getProvider(props, this.originalUrl, serviceContainer); + final HostListProvider provider = supplier.getProvider(props, this.originalUrl, servicesContainer); hostListProviderService.setHostListProvider(provider); } diff --git a/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java b/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java index bb7352f0d..1f055d1cc 100644 --- a/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java +++ b/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java @@ -18,12 +18,12 @@ import java.util.Properties; import software.amazon.jdbc.hostlistprovider.AuroraHostListProvider; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; public class TestAuroraHostListProvider extends AuroraHostListProvider { - public TestAuroraHostListProvider(ServiceContainer serviceContainer, Properties properties, String originalUrl) { - super(properties, originalUrl, serviceContainer, "", "", ""); + public TestAuroraHostListProvider(CompleteServicesContainer servicesContainer, Properties properties, String originalUrl) { + super(properties, originalUrl, servicesContainer, "", "", ""); } public static void clearCache() { diff --git a/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java b/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java index f8666d265..de24f8919 100644 --- a/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java +++ b/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java @@ -21,19 +21,19 @@ import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.PluginServiceImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; public class TestPluginServiceImpl extends PluginServiceImpl { public TestPluginServiceImpl( - @NonNull ServiceContainer serviceContainer, + @NonNull CompleteServicesContainer servicesContainer, @NonNull Properties props, @NonNull String originalUrl, String targetDriverProtocol, @NonNull final TargetDriverDialect targetDriverDialect) throws SQLException { - super(serviceContainer, props, originalUrl, targetDriverProtocol, targetDriverDialect); + super(servicesContainer, props, originalUrl, targetDriverProtocol, targetDriverDialect); } public static void clearHostAvailabilityCache() { diff --git a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java index 006ce474f..c3739e85d 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java @@ -39,14 +39,14 @@ import software.amazon.jdbc.plugin.efm2.HostMonitoringConnectionPlugin; import software.amazon.jdbc.plugin.failover.FailoverConnectionPlugin; import software.amazon.jdbc.plugin.iam.IamAuthConnectionPlugin; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class ConnectionPluginChainBuilderTests { @Mock ConnectionProvider mockConnectionProvider; - @Mock ServiceContainer mockServiceContainer; + @Mock CompleteServicesContainer mockServicesContainer; @Mock PluginService mockPluginService; @Mock PluginManagerService mockPluginManagerService; @Mock TelemetryFactory mockTelemetryFactory; @@ -62,8 +62,8 @@ void afterEach() throws Exception { @BeforeEach void beforeEach() { closeable = MockitoAnnotations.openMocks(this); - when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); - when(mockServiceContainer.getTelemetryFactory()).thenReturn(mockTelemetryFactory); + when(mockServicesContainer.getPluginService()).thenReturn(mockPluginService); + when(mockServicesContainer.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockPluginService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockTelemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(mockTelemetryContext); when(mockTelemetryFactory.openTelemetryContext(eq(null), any())).thenReturn(mockTelemetryContext); @@ -76,7 +76,7 @@ public void testSortPlugins() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "iam,efm2,failover"); List result = builder.getPlugins( - mockServiceContainer, + mockServicesContainer, mockConnectionProvider, null, mockPluginManagerService, @@ -99,7 +99,7 @@ public void testPreservePluginOrder() throws SQLException { props.put(PropertyDefinition.AUTO_SORT_PLUGIN_ORDER.name, "false"); List result = builder.getPlugins( - mockServiceContainer, + mockServicesContainer, mockConnectionProvider, null, mockPluginManagerService, @@ -121,7 +121,7 @@ public void testSortPluginsWithStickToPrior() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "dev,iam,executionTime,connectTime,efm2,failover"); List result = builder.getPlugins( - mockServiceContainer, + mockServicesContainer, mockConnectionProvider, null, mockPluginManagerService, diff --git a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java index 4c6e6b6c1..00ebb2cc8 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java @@ -62,7 +62,7 @@ import software.amazon.jdbc.plugin.failover.FailoverConnectionPlugin; import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.profile.ConfigurationProfileBuilder; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.WrapperUtils; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -77,7 +77,7 @@ public class ConnectionPluginManagerTests { @Mock ConnectionWrapper mockConnectionWrapper; @Mock TelemetryFactory mockTelemetryFactory; @Mock TelemetryContext mockTelemetryContext; - @Mock ServiceContainer mockServiceContainer; + @Mock CompleteServicesContainer mockServicesContainer; @Mock PluginService mockPluginService; @Mock PluginManagerService mockPluginManagerService; ConfigurationProfile configurationProfile = ConfigurationProfileBuilder.get().withName("test").build(); @@ -92,7 +92,7 @@ void cleanUp() throws Exception { @BeforeEach void init() { closeable = MockitoAnnotations.openMocks(this); - when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); + when(mockServicesContainer.getPluginService()).thenReturn(mockPluginService); when(mockPluginService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockTelemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(mockTelemetryContext); when(mockTelemetryFactory.openTelemetryContext(eq(null), any())).thenReturn(mockTelemetryContext); @@ -612,7 +612,7 @@ public void testDefaultPlugins() throws SQLException { null, mockConnectionWrapper, mockTelemetryFactory)); - target.init(mockServiceContainer, testProperties, mockPluginManagerService, configurationProfile); + target.init(mockServicesContainer, testProperties, mockPluginManagerService, configurationProfile); assertEquals(4, target.plugins.size()); assertEquals(AuroraConnectionTrackerPlugin.class, target.plugins.get(0).getClass()); @@ -631,7 +631,7 @@ public void testNoWrapperPlugins() throws SQLException { null, mockConnectionWrapper, mockTelemetryFactory)); - target.init(mockServiceContainer, testProperties, mockPluginManagerService, configurationProfile); + target.init(mockServicesContainer, testProperties, mockPluginManagerService, configurationProfile); assertEquals(1, target.plugins.size()); } @@ -646,7 +646,7 @@ public void testOverridingDefaultPluginsWithPluginCodes() throws SQLException { null, mockConnectionWrapper, mockTelemetryFactory)); - target.init(mockServiceContainer, testProperties, mockPluginManagerService, configurationProfile); + target.init(mockServicesContainer, testProperties, mockPluginManagerService, configurationProfile); assertEquals(2, target.plugins.size()); assertEquals(LogQueryConnectionPlugin.class, target.plugins.get(0).getClass()); diff --git a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java index 7a6301036..5a999066b 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java @@ -51,7 +51,7 @@ import software.amazon.jdbc.dialect.RdsPgDialect; import software.amazon.jdbc.exceptions.ExceptionManager; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; public class DialectDetectionTests { private static final String LOCALHOST = "localhost"; @@ -63,7 +63,7 @@ public class DialectDetectionTests { private final DialectManager dialectManager = new DialectManager(null); private final Properties props = new Properties(); private AutoCloseable closeable; - @Mock private ServiceContainer mockServiceContainer; + @Mock private CompleteServicesContainer mockServicesContainer; @Mock private HostListProvider mockHostListProvider; @Mock private Connection mockConnection; @Mock private Statement mockStatement; @@ -77,7 +77,7 @@ public class DialectDetectionTests { @BeforeEach void setUp() throws SQLException { closeable = MockitoAnnotations.openMocks(this); - when(this.mockServiceContainer.getConnectionPluginManager()).thenReturn(mockPluginManager); + when(this.mockServicesContainer.getConnectionPluginManager()).thenReturn(mockPluginManager); when(this.mockConnection.createStatement()).thenReturn(this.mockStatement); when(this.mockHost.getUrl()).thenReturn("url"); when(this.mockFailResultSet.next()).thenReturn(false); @@ -93,7 +93,7 @@ void cleanUp() throws Exception { PluginServiceImpl getPluginService(String protocol) throws SQLException { return spy( new PluginServiceImpl( - mockServiceContainer, + mockServicesContainer, new ExceptionManager(), props, protocol + DialectDetectionTests.LOCALHOST, @@ -169,7 +169,7 @@ void testUpdateDialectMysqlToAurora() throws SQLException { when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'aurora_version'")).thenReturn(mockSuccessResultSet); when(mockSuccessResultSet.next()).thenReturn(true, false); final PluginServiceImpl target = getPluginService(MYSQL_PROTOCOL); - when(mockServiceContainer.getPluginService()).thenReturn(target); + when(mockServicesContainer.getPluginService()).thenReturn(target); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(AuroraMysqlDialect.class, target.dialect.getClass()); @@ -272,7 +272,7 @@ void testUpdateDialectMariaToMysqlAurora() throws SQLException { when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'aurora_version'")).thenReturn(mockSuccessResultSet); when(mockSuccessResultSet.next()).thenReturn(true, false); final PluginServiceImpl target = getPluginService(MARIA_PROTOCOL); - when(mockServiceContainer.getPluginService()).thenReturn(target); + when(mockServicesContainer.getPluginService()).thenReturn(target); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(AuroraMysqlDialect.class, target.dialect.getClass()); diff --git a/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java b/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java index 690977f36..6d948c5a1 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java @@ -66,7 +66,7 @@ import software.amazon.jdbc.profile.ConfigurationProfileBuilder; import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.TestStorageServiceImpl; @@ -79,7 +79,7 @@ public class PluginServiceImplTests { private StorageService storageService; private AutoCloseable closeable; - @Mock ServiceContainer serviceContainer; + @Mock CompleteServicesContainer servicesContainer; @Mock EventPublisher mockEventPublisher; @Mock ConnectionPluginManager pluginManager; @Mock Connection newConnection; @@ -102,8 +102,8 @@ void setUp() throws SQLException { when(oldConnection.isClosed()).thenReturn(false); when(newConnection.createStatement()).thenReturn(statement); when(statement.executeQuery(any())).thenReturn(resultSet); - when(serviceContainer.getConnectionPluginManager()).thenReturn(pluginManager); - when(serviceContainer.getStorageService()).thenReturn(storageService); + when(servicesContainer.getConnectionPluginManager()).thenReturn(pluginManager); + when(servicesContainer.getStorageService()).thenReturn(storageService); storageService = new TestStorageServiceImpl(mockEventPublisher); PluginServiceImpl.hostAvailabilityExpiringCache.clear(); } @@ -122,7 +122,7 @@ public void testOldConnectionNoSuggestion() throws SQLException { PluginServiceImpl target = spy(new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -151,7 +151,7 @@ public void testOldConnectionDisposeSuggestion() throws SQLException { PluginServiceImpl target = spy(new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -180,7 +180,7 @@ public void testOldConnectionPreserveSuggestion() throws SQLException { PluginServiceImpl target = spy(new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -213,7 +213,7 @@ public void testOldConnectionMixedSuggestion() throws SQLException { PluginServiceImpl target = spy(new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -243,7 +243,7 @@ public void testChangesNewConnectionNewHostNewPortNewRoleNewAvailability() throw PluginServiceImpl target = spy(new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -282,7 +282,7 @@ public void testChangesNewConnectionNewRoleNewAvailability() throws SQLException PluginServiceImpl target = spy(new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -321,7 +321,7 @@ public void testChangesNewConnection() throws SQLException { PluginServiceImpl target = spy(new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -360,7 +360,7 @@ public void testChangesNoChanges() throws SQLException { PluginServiceImpl target = spy(new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -391,7 +391,7 @@ public void testSetNodeListAdded() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -425,7 +425,7 @@ public void testSetNodeListDeleted() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -462,7 +462,7 @@ public void testSetNodeListChanged() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -499,7 +499,7 @@ public void testSetNodeListNoChanges() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -525,7 +525,7 @@ public void testNodeAvailabilityNotChanged() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -554,7 +554,7 @@ public void testNodeAvailabilityChanged_WentDown() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -590,7 +590,7 @@ public void testNodeAvailabilityChanged_WentUp() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -637,7 +637,7 @@ public void testNodeAvailabilityChanged_WentUp_ByAlias() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -682,7 +682,7 @@ public void testNodeAvailabilityChanged_WentUp_MultipleHostsByAlias() throws SQL PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -760,7 +760,7 @@ void testRefreshHostList_withCachedHostAvailability() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -817,7 +817,7 @@ void testForceRefreshHostList_withCachedHostAvailability() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -842,7 +842,7 @@ void testForceRefreshHostList_withCachedHostAvailability() throws SQLException { void testIdentifyConnectionWithNoAliases() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -863,7 +863,7 @@ void testIdentifyConnectionWithAliases() throws SQLException { .build(); PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -891,7 +891,7 @@ void testFillAliasesNonEmptyAliases() throws SQLException { PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, @@ -913,7 +913,7 @@ void testFillAliasesWithInstanceEndpoint(Dialect dialect, String[] expectedInsta final HostSpec empty = new HostSpecBuilder(new SimpleHostAvailabilityStrategy()).host("foo").build(); PluginServiceImpl target = spy( new PluginServiceImpl( - serviceContainer, + servicesContainer, new ExceptionManager(), PROPERTIES, URL, diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java index e957bb2f6..c3b69120e 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java @@ -64,7 +64,7 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider.FetchTopologyResult; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.TestStorageServiceImpl; @@ -77,7 +77,7 @@ class RdsHostListProviderTest { @Mock private Connection mockConnection; @Mock private Statement mockStatement; @Mock private ResultSet mockResultSet; - @Mock private ServiceContainer mockServiceContainer; + @Mock private CompleteServicesContainer mockServicesContainer; @Mock private PluginService mockPluginService; @Mock private HostListProviderService mockHostListProviderService; @Mock private EventPublisher mockEventPublisher; @@ -95,8 +95,8 @@ class RdsHostListProviderTest { void setUp() throws SQLException { closeable = MockitoAnnotations.openMocks(this); storageService = new TestStorageServiceImpl(mockEventPublisher); - when(mockServiceContainer.getHostListProviderService()).thenReturn(mockHostListProviderService); - when(mockServiceContainer.getStorageService()).thenReturn(storageService); + when(mockServicesContainer.getHostListProviderService()).thenReturn(mockHostListProviderService); + when(mockServicesContainer.getStorageService()).thenReturn(storageService); when(mockPluginService.getCurrentConnection()).thenReturn(mockConnection); when(mockPluginService.connect(any(HostSpec.class), any(Properties.class))).thenReturn(mockConnection); when(mockPluginService.getCurrentHostSpec()).thenReturn(currentHostSpec); @@ -119,7 +119,7 @@ private RdsHostListProvider getRdsHostListProvider(String originalUrl) throws SQ RdsHostListProvider provider = new RdsHostListProvider( new Properties(), originalUrl, - mockServiceContainer, + mockServicesContainer, "foo", "bar", "baz"); provider.init(); return provider; diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java index 5264cd7cd..94c7536c2 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java @@ -58,7 +58,7 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider.FetchTopologyResult; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.TestStorageServiceImpl; @@ -71,7 +71,7 @@ class RdsMultiAzDbClusterListProviderTest { @Mock private Connection mockConnection; @Mock private Statement mockStatement; @Mock private ResultSet mockResultSet; - @Mock private ServiceContainer mockServiceContainer; + @Mock private CompleteServicesContainer mockServicesContainer; @Mock private PluginService mockPluginService; @Mock private HostListProviderService mockHostListProviderService; @Mock private EventPublisher mockEventPublisher; @@ -89,8 +89,8 @@ class RdsMultiAzDbClusterListProviderTest { void setUp() throws SQLException { closeable = MockitoAnnotations.openMocks(this); storageService = new TestStorageServiceImpl(mockEventPublisher); - when(mockServiceContainer.getHostListProviderService()).thenReturn(mockHostListProviderService); - when(mockServiceContainer.getStorageService()).thenReturn(storageService); + when(mockServicesContainer.getHostListProviderService()).thenReturn(mockHostListProviderService); + when(mockServicesContainer.getStorageService()).thenReturn(storageService); when(mockPluginService.getCurrentConnection()).thenReturn(mockConnection); when(mockPluginService.connect(any(HostSpec.class), any(Properties.class))).thenReturn(mockConnection); when(mockPluginService.getCurrentHostSpec()).thenReturn(currentHostSpec); @@ -112,7 +112,7 @@ private RdsMultiAzDbClusterListProvider getRdsMazDbClusterHostListProvider(Strin RdsMultiAzDbClusterListProvider provider = new RdsMultiAzDbClusterListProvider( new Properties(), originalUrl, - mockServiceContainer, + mockServicesContainer, "foo", "bar", "baz", diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java index 3ff241dda..942edf007 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java @@ -72,7 +72,7 @@ import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Pair; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.telemetry.GaugeCallable; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; @@ -109,7 +109,7 @@ public class AwsSecretsManagerConnectionPluginTest { private AutoCloseable closeable; - @Mock ServiceContainer mockServiceContainer; + @Mock CompleteServicesContainer mockServicesContainer; @Mock SecretsManagerClient mockSecretsManagerClient; @Mock GetSecretValueRequest mockGetValueRequest; @Mock JdbcCallable connectFunc; @@ -136,7 +136,7 @@ public void init() throws SQLException { when(mockDialectManager.getDialect(anyString(), anyString(), any(Properties.class))) .thenReturn(mockTopologyAwareDialect); - when(mockServiceContainer.getConnectionPluginManager()).thenReturn(mockConnectionPluginManager); + when(mockServicesContainer.getConnectionPluginManager()).thenReturn(mockConnectionPluginManager); when(mockService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockConnectionPluginManager.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockTelemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(mockTelemetryContext); @@ -280,7 +280,7 @@ public void testConnectWithNewSecretsAfterTryingWithCachedSecrets( private @NotNull PluginServiceImpl getPluginService(String protocol) throws SQLException { return new PluginServiceImpl( - mockServiceContainer, + mockServicesContainer, new ExceptionManager(), TEST_PROPS, "url", @@ -453,7 +453,7 @@ public void testConnectViaARN(final String arn, final Region expectedRegionParse SECRET_ID_PROPERTY.set(props, arn); this.plugin = spy(new AwsSecretsManagerConnectionPlugin( - new PluginServiceImpl(mockServiceContainer, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect), + new PluginServiceImpl(mockServicesContainer, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect), props, (host, r) -> mockSecretsManagerClient, (id) -> mockGetValueRequest)); @@ -473,7 +473,7 @@ public void testConnectionWithRegionParameterAndARN(final String arn, final Regi REGION_PROPERTY.set(props, expectedRegion.toString()); this.plugin = spy(new AwsSecretsManagerConnectionPlugin( - new PluginServiceImpl(mockServiceContainer, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect), + new PluginServiceImpl(mockServicesContainer, props, "url", TEST_PG_PROTOCOL, mockTargetDriverDialect), props, (host, r) -> mockSecretsManagerClient, (id) -> mockGetValueRequest)); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java index c96ffe227..631559949 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java @@ -45,7 +45,7 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.hostavailability.HostAvailabilityStrategy; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; -import software.amazon.jdbc.util.ServiceContainer; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -60,7 +60,7 @@ public class CustomEndpointPluginTest { private final HostSpec writerClusterHost = hostSpecBuilder.host(writerClusterUrl).build(); private final HostSpec host = hostSpecBuilder.host(customEndpointUrl).build(); - @Mock private ServiceContainer mockServiceContainer; + @Mock private CompleteServicesContainer mockServicesContainer; @Mock private PluginService mockPluginService; @Mock private BiFunction mockRdsClientFunc; @Mock private TelemetryFactory mockTelemetryFactory; @@ -74,8 +74,8 @@ public class CustomEndpointPluginTest { public void init() throws SQLException { closeable = MockitoAnnotations.openMocks(this); - when(mockServiceContainer.getPluginService()).thenReturn(mockPluginService); - when(mockServiceContainer.getTelemetryFactory()).thenReturn(mockTelemetryFactory); + when(mockServicesContainer.getPluginService()).thenReturn(mockPluginService); + when(mockServicesContainer.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockTelemetryFactory.createCounter(any(String.class))).thenReturn(mockTelemetryCounter); when(mockMonitor.hasCustomEndpointInfo()).thenReturn(true); } @@ -87,7 +87,7 @@ void cleanUp() throws Exception { } private CustomEndpointPlugin getSpyPlugin() throws SQLException { - CustomEndpointPlugin plugin = new CustomEndpointPlugin(mockServiceContainer, props, mockRdsClientFunc); + CustomEndpointPlugin plugin = new CustomEndpointPlugin(mockServicesContainer, props, mockRdsClientFunc); CustomEndpointPlugin spyPlugin = spy(plugin); doReturn(mockMonitor).when(spyPlugin).createMonitorIfAbsent(any(Properties.class)); return spyPlugin; diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java index 8654c7bee..f0cb2a7d5 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java @@ -42,8 +42,8 @@ import software.amazon.jdbc.dialect.DialectCodes; import software.amazon.jdbc.dialect.DialectManager; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.ServiceContainer; -import software.amazon.jdbc.util.ServiceContainerImpl; +import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.CompleteServicesContainerImpl; import software.amazon.jdbc.util.monitoring.CoreMonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryContext; @@ -52,7 +52,7 @@ @SuppressWarnings({"resource"}) public class DeveloperConnectionPluginTest { - private ServiceContainer serviceContainer; + private CompleteServicesContainer servicesContainer; @Mock StorageService mockStorageService; @Mock CoreMonitorService mockMonitorService; @Mock ConnectionProvider mockConnectionProvider; @@ -73,7 +73,7 @@ void cleanUp() throws Exception { @BeforeEach void init() throws SQLException { closeable = MockitoAnnotations.openMocks(this); - serviceContainer = new ServiceContainerImpl(mockStorageService, mockMonitorService, mockTelemetryFactory); + servicesContainer = new CompleteServicesContainerImpl(mockStorageService, mockMonitorService, mockTelemetryFactory); when(mockConnectionProvider.connect(any(), any(), any(), any(), any())).thenReturn(mockConnection); when(mockConnectCallback.getExceptionToRaise(any(), any(), any(), anyBoolean())).thenReturn(null); @@ -91,7 +91,7 @@ public void test_RaiseException() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "dev"); props.put(DialectManager.DIALECT.name, DialectCodes.PG); try (ConnectionWrapper wrapper = new ConnectionWrapper( - serviceContainer, + servicesContainer, props, "any-protocol://any-host/", mockConnectionProvider, @@ -120,7 +120,7 @@ public void test_RaiseExceptionForMethodName() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "dev"); props.put(DialectManager.DIALECT.name, DialectCodes.PG); try (ConnectionWrapper wrapper = new ConnectionWrapper( - serviceContainer, + servicesContainer, props, "any-protocol://any-host/", mockConnectionProvider, @@ -149,7 +149,7 @@ public void test_RaiseExceptionForAnyMethodName() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "dev"); props.put(DialectManager.DIALECT.name, DialectCodes.PG); try (ConnectionWrapper wrapper = new ConnectionWrapper( - serviceContainer, + servicesContainer, props, "any-protocol://any-host/", mockConnectionProvider, @@ -178,7 +178,7 @@ public void test_RaiseExceptionForWrongMethodName() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "dev"); props.put(DialectManager.DIALECT.name, DialectCodes.PG); try (ConnectionWrapper wrapper = new ConnectionWrapper( - serviceContainer, + servicesContainer, props, "any-protocol://any-host/", mockConnectionProvider, @@ -209,7 +209,7 @@ public void test_RaiseExpectedExceptionClass() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "dev"); props.put(DialectManager.DIALECT.name, DialectCodes.PG); try (ConnectionWrapper wrapper = new ConnectionWrapper( - serviceContainer, + servicesContainer, props, "any-protocol://any-host/", mockConnectionProvider, @@ -238,7 +238,7 @@ public void test_RaiseUnexpectedExceptionClass() throws SQLException { props.put(PropertyDefinition.PLUGINS.name, "dev"); props.put(DialectManager.DIALECT.name, DialectCodes.PG); try (ConnectionWrapper wrapper = new ConnectionWrapper( - serviceContainer, + servicesContainer, props, "any-protocol://any-host/", mockConnectionProvider, @@ -277,7 +277,7 @@ public void test_RaiseExceptionOnConnect() { Throwable thrownException = assertThrows( SQLException.class, () -> new ConnectionWrapper( - serviceContainer, + servicesContainer, props, "any-protocol://any-host/", mockConnectionProvider, @@ -288,7 +288,7 @@ public void test_RaiseExceptionOnConnect() { assertDoesNotThrow( () -> new ConnectionWrapper( - serviceContainer, + servicesContainer, props, "any-protocol://any-host/", mockConnectionProvider, @@ -308,7 +308,7 @@ public void test_NoExceptionOnConnectWithCallback() { assertDoesNotThrow( () -> new ConnectionWrapper( - serviceContainer, + servicesContainer, props, "any-protocol://any-host/", mockConnectionProvider, @@ -333,7 +333,7 @@ public void test_RaiseExceptionOnConnectWithCallback() { Throwable thrownException = assertThrows( SQLException.class, () -> new ConnectionWrapper( - serviceContainer, + servicesContainer, props, "any-protocol://any-host/", mockConnectionProvider, @@ -344,7 +344,7 @@ public void test_RaiseExceptionOnConnectWithCallback() { assertDoesNotThrow( () -> new ConnectionWrapper( - serviceContainer, + servicesContainer, props, "any-protocol://any-host/", mockConnectionProvider, From 757b0497e4656f711de7330fe21e232aa977ed3a Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 20 May 2025 16:47:13 -0700 Subject: [PATCH 119/149] Add javadocs, remove CoreServicesContainer#getEventPublisher --- .../jdbc/util/CompleteServicesContainer.java | 7 +++++++ .../amazon/jdbc/util/CoreServicesContainer.java | 14 ++++++-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainer.java index 1f2ae5fff..6fe0e5374 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainer.java @@ -24,6 +24,13 @@ import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; +/** + * A container object used to hold and access the various services required by the driver. This class provides access to + * both connection-specific services required by plugins and monitors as well as core universal services such + * as {@link CoreMonitorService} and {@link StorageService}. + * + * @see CoreServicesContainer + */ public interface CompleteServicesContainer { StorageService getStorageService(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java index 75ea0bcdd..9dfeea3a3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java @@ -24,18 +24,20 @@ import software.amazon.jdbc.util.storage.StorageServiceImpl; /** - * A singleton container object used to instantiate core services. This class should be used instead of directly - * instantiating core services so that only one instance of each service is instantiated. + * A singleton container object used to instantiate and access core universal services. This class should be used + * instead of directly instantiating core services so that only one instance of each service is instantiated. + * + * @see CompleteServicesContainer for a container that holds both connection-specific services and core universal + * services. */ public class CoreServicesContainer { private static final CoreServicesContainer INSTANCE = new CoreServicesContainer(); - private final EventPublisher eventPublisher; private final StorageService storageService; private final CoreMonitorService monitorService; private CoreServicesContainer() { - this.eventPublisher = new BatchingEventPublisher(); + EventPublisher eventPublisher = new BatchingEventPublisher(); this.storageService = new StorageServiceImpl(eventPublisher); this.monitorService = new CoreMonitorServiceImpl(eventPublisher); } @@ -44,10 +46,6 @@ public static CoreServicesContainer getInstance() { return INSTANCE; } - public EventPublisher getEventPublisher() { - return eventPublisher; - } - public StorageService getStorageService() { return storageService; } From 1b1760069edf4fd8504c1f978d672ee4399a1c42 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 20 May 2025 17:23:45 -0700 Subject: [PATCH 120/149] Resolve storageService and monitorService on the fly from CompleteServicesContainer --- .../amazon/jdbc/PartialPluginService.java | 7 ++----- .../amazon/jdbc/PluginServiceImpl.java | 11 ++++------ .../hostlistprovider/RdsHostListProvider.java | 19 ++++++++--------- .../MonitoringRdsHostListProvider.java | 21 +++++++++++-------- .../MonitoringRdsMultiAzHostListProvider.java | 8 +++---- .../customendpoint/CustomEndpointPlugin.java | 12 +++-------- 6 files changed, 34 insertions(+), 44 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java index 9dc9e33b3..4f31bc10b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java @@ -46,11 +46,10 @@ import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.states.SessionStateServiceImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.CacheMap; -import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; /** @@ -67,7 +66,6 @@ public class PartialPluginService implements PluginService, CanReleaseResources, protected static final CacheMap hostAvailabilityExpiringCache = new CacheMap<>(); protected final CompleteServicesContainer servicesContainer; - protected final StorageService storageService; protected final ConnectionPluginManager pluginManager; protected final Properties props; protected volatile HostListProvider hostListProvider; @@ -119,7 +117,6 @@ public PartialPluginService( @Nullable final ConfigurationProfile configurationProfile, @Nullable final SessionStateService sessionStateService) { this.servicesContainer = servicesContainer; - this.storageService = servicesContainer.getStorageService(); this.pluginManager = servicesContainer.getConnectionPluginManager(); this.props = props; this.originalUrl = originalUrl; @@ -392,7 +389,7 @@ public List getAllHosts() { @Override public List getHosts() { - AllowedAndBlockedHosts hostPermissions = this.storageService.get( + AllowedAndBlockedHosts hostPermissions = this.servicesContainer.getStorageService().get( AllowedAndBlockedHosts.class, this.initialConnectionHostSpec.getUrl()); if (hostPermissions == null) { return this.allHosts; diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index 147dddb2d..e897969f7 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -50,11 +50,10 @@ import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.states.SessionStateServiceImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.CacheMap; -import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class PluginServiceImpl implements PluginService, CanReleaseResources, @@ -65,7 +64,6 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources, protected static final CacheMap hostAvailabilityExpiringCache = new CacheMap<>(); protected final CompleteServicesContainer servicesContainer; - protected final StorageService storageService; protected final ConnectionPluginManager pluginManager; private final Properties props; private final String originalUrl; @@ -138,7 +136,6 @@ public PluginServiceImpl( @Nullable final ConfigurationProfile configurationProfile, @Nullable final SessionStateService sessionStateService) throws SQLException { this.servicesContainer = servicesContainer; - this.storageService = servicesContainer.getStorageService(); this.pluginManager = servicesContainer.getConnectionPluginManager(); this.props = props; this.originalUrl = originalUrl; @@ -219,7 +216,7 @@ public String getOriginalUrl() { @Override @Deprecated public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHosts) { - this.storageService.set(this.initialConnectionHostSpec.getHost(), allowedAndBlockedHosts); + this.servicesContainer.getStorageService().set(this.initialConnectionHostSpec.getHost(), allowedAndBlockedHosts); } @Override @@ -413,8 +410,8 @@ public List getAllHosts() { @Override public List getHosts() { - AllowedAndBlockedHosts hostPermissions = this.storageService.get( - AllowedAndBlockedHosts.class, this.initialConnectionHostSpec.getUrl()); + AllowedAndBlockedHosts hostPermissions = this.servicesContainer.getStorageService() + .get(AllowedAndBlockedHosts.class, this.initialConnectionHostSpec.getUrl()); if (hostPermissions == null) { return this.allHosts; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index dede28112..e9a3d0cd0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -49,16 +49,15 @@ import software.amazon.jdbc.HostSpecBuilder; import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.hostavailability.HostAvailability; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; -import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SynchronousExecutor; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.CacheMap; -import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.Topology; public class RdsHostListProvider implements DynamicHostListProvider { @@ -96,7 +95,7 @@ public class RdsHostListProvider implements DynamicHostListProvider { protected static final CacheMap suggestedPrimaryClusterIdCache = new CacheMap<>(); protected static final CacheMap primaryClusterIdCache = new CacheMap<>(); - protected final StorageService storageService; + protected final CompleteServicesContainer servicesContainer; protected final HostListProviderService hostListProviderService; protected final String originalUrl; protected final String topologyQuery; @@ -133,10 +132,10 @@ public RdsHostListProvider( final String topologyQuery, final String nodeIdQuery, final String isReaderQuery) { - this.hostListProviderService = servicesContainer.getHostListProviderService(); - this.storageService = servicesContainer.getStorageService(); this.properties = properties; this.originalUrl = originalUrl; + this.servicesContainer = servicesContainer; + this.hostListProviderService = servicesContainer.getHostListProviderService(); this.topologyQuery = topologyQuery; this.nodeIdQuery = nodeIdQuery; this.isReaderQuery = isReaderQuery; @@ -267,7 +266,7 @@ protected FetchTopologyResult getTopology(final Connection conn, final boolean f final List hosts = queryForTopology(conn); if (!Utils.isNullOrEmpty(hosts)) { - storageService.set(this.clusterId, new Topology(hosts)); + this.servicesContainer.getStorageService().set(this.clusterId, new Topology(hosts)); if (needToSuggest) { this.suggestPrimaryCluster(hosts); } @@ -288,7 +287,7 @@ protected void clusterIdChanged(final String oldClusterId) throws SQLException { } protected ClusterSuggestedResult getSuggestedClusterId(final String url) { - Map entries = storageService.getEntries(Topology.class); + Map entries = this.servicesContainer.getStorageService().getEntries(Topology.class); if (entries == null) { return null; } @@ -325,7 +324,7 @@ protected void suggestPrimaryCluster(final @NonNull List primaryCluste primaryClusterHostUrls.add(hostSpec.getUrl()); } - Map entries = storageService.getEntries(Topology.class); + Map entries = this.servicesContainer.getStorageService().getEntries(Topology.class); if (entries == null) { return; } @@ -508,7 +507,7 @@ protected String getHostEndpoint(final String nodeName) { * cached topology is outdated, it returns null. */ public @Nullable List getStoredTopology() { - Topology topology = storageService.get(Topology.class, this.clusterId); + Topology topology = this.servicesContainer.getStorageService().get(Topology.class, this.clusterId); return topology == null ? null : topology.getHosts(); } @@ -524,7 +523,7 @@ public static void clearAll() { * Clear topology cache for the current cluster. */ public void clear() { - storageService.remove(Topology.class, this.clusterId); + this.servicesContainer.getStorageService().remove(Topology.class, this.clusterId); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index 614c2a3df..be4c6af30 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -33,6 +33,7 @@ import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.CoreMonitorService; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.Topology; public class MonitoringRdsHostListProvider extends RdsHostListProvider @@ -51,7 +52,6 @@ public class MonitoringRdsHostListProvider extends RdsHostListProvider } protected final CompleteServicesContainer servicesContainer; - protected final CoreMonitorService monitorService; protected final PluginService pluginService; protected final long highRefreshRateNano; protected final String writerTopologyQuery; @@ -66,7 +66,6 @@ public MonitoringRdsHostListProvider( final String writerTopologyQuery) { super(properties, originalUrl, servicesContainer, topologyQuery, nodeIdQuery, isReaderQuery); this.servicesContainer = servicesContainer; - this.monitorService = servicesContainer.getMonitorService(); this.pluginService = servicesContainer.getPluginService(); this.writerTopologyQuery = writerTopologyQuery; this.highRefreshRateNano = TimeUnit.MILLISECONDS.toNanos( @@ -83,10 +82,10 @@ protected void init() throws SQLException { } protected ClusterTopologyMonitor initMonitor() throws SQLException { - return monitorService.runIfAbsent( + return this.servicesContainer.getMonitorService().runIfAbsent( ClusterTopologyMonitorImpl.class, this.clusterId, - this.storageService, + this.servicesContainer.getStorageService(), this.pluginService.getTelemetryFactory(), this.originalUrl, this.pluginService.getDriverProtocol(), @@ -95,7 +94,7 @@ protected ClusterTopologyMonitor initMonitor() throws SQLException { this.properties, (ConnectionService connectionService, PluginService monitorPluginService) -> new ClusterTopologyMonitorImpl( this.clusterId, - this.storageService, + this.servicesContainer.getStorageService(), connectionService, this.initialHostSpec, this.properties, @@ -111,7 +110,8 @@ protected ClusterTopologyMonitor initMonitor() throws SQLException { @Override protected List queryForTopology(final Connection conn) throws SQLException { - ClusterTopologyMonitor monitor = monitorService.get(ClusterTopologyMonitorImpl.class, this.clusterId); + ClusterTopologyMonitor monitor = this.servicesContainer.getMonitorService() + .get(ClusterTopologyMonitorImpl.class, this.clusterId); if (monitor == null) { monitor = this.initMonitor(); } @@ -125,13 +125,14 @@ protected List queryForTopology(final Connection conn) throws SQLExcep @Override protected void clusterIdChanged(final String oldClusterId) throws SQLException { + CoreMonitorService monitorService = this.servicesContainer.getMonitorService(); final ClusterTopologyMonitorImpl existingMonitor = monitorService.get(ClusterTopologyMonitorImpl.class, oldClusterId); if (existingMonitor != null) { - monitorService.runIfAbsent( + this.servicesContainer.getMonitorService().runIfAbsent( ClusterTopologyMonitorImpl.class, this.clusterId, - this.storageService, + this.servicesContainer.getStorageService(), this.pluginService.getTelemetryFactory(), this.originalUrl, this.pluginService.getDriverProtocol(), @@ -144,6 +145,7 @@ protected void clusterIdChanged(final String oldClusterId) throws SQLException { monitorService.remove(ClusterTopologyMonitorImpl.class, oldClusterId); } + final StorageService storageService = this.servicesContainer.getStorageService(); final Topology existingTopology = storageService.get(Topology.class, oldClusterId); final List existingHosts = existingTopology == null ? null : existingTopology.getHosts(); if (existingHosts != null) { @@ -155,7 +157,8 @@ protected void clusterIdChanged(final String oldClusterId) throws SQLException { public List forceRefresh(final boolean shouldVerifyWriter, final long timeoutMs) throws SQLException, TimeoutException { - ClusterTopologyMonitor monitor = monitorService.get(ClusterTopologyMonitorImpl.class, this.clusterId); + ClusterTopologyMonitor monitor = + this.servicesContainer.getMonitorService().get(ClusterTopologyMonitorImpl.class, this.clusterId); if (monitor == null) { monitor = this.initMonitor(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java index ba49cf86c..968351859 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java @@ -51,9 +51,9 @@ public MonitoringRdsMultiAzHostListProvider( @Override protected ClusterTopologyMonitor initMonitor() throws SQLException { - return monitorService.runIfAbsent(MultiAzClusterTopologyMonitorImpl.class, + return this.servicesContainer.getMonitorService().runIfAbsent(MultiAzClusterTopologyMonitorImpl.class, this.clusterId, - this.storageService, + this.servicesContainer.getStorageService(), this.pluginService.getTelemetryFactory(), this.originalUrl, this.pluginService.getDriverProtocol(), @@ -62,8 +62,8 @@ protected ClusterTopologyMonitor initMonitor() throws SQLException { this.properties, (connectionService, pluginService) -> new MultiAzClusterTopologyMonitorImpl( this.clusterId, - this.storageService, - this.monitorService, + this.servicesContainer.getStorageService(), + this.servicesContainer.getMonitorService(), connectionService, this.initialHostSpec, this.properties, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java index 95cf8bd3d..da25707e4 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java @@ -34,15 +34,13 @@ import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.authentication.AwsCredentialsManager; import software.amazon.jdbc.plugin.AbstractConnectionPlugin; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUtils; import software.amazon.jdbc.util.RegionUtils; -import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SubscribedMethodHelper; import software.amazon.jdbc.util.WrapperUtils; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; -import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -92,8 +90,6 @@ public class CustomEndpointPlugin extends AbstractConnectionPlugin { protected final CompleteServicesContainer servicesContainer; protected final PluginService pluginService; - protected final StorageService storageService; - protected final CoreMonitorService monitorService; protected final TelemetryFactory telemetryFactory; protected final Properties props; protected final RdsUtils rdsUtils = new RdsUtils(); @@ -137,8 +133,6 @@ public CustomEndpointPlugin( final BiFunction rdsClientFunc) { this.servicesContainer = servicesContainer; this.pluginService = servicesContainer.getPluginService(); - this.storageService = servicesContainer.getStorageService(); - this.monitorService = servicesContainer.getMonitorService(); this.telemetryFactory = servicesContainer.getTelemetryFactory(); this.props = props; @@ -210,7 +204,7 @@ protected CustomEndpointMonitor createMonitorIfAbsent(Properties props) throws S return this.servicesContainer.getMonitorService().runIfAbsent( CustomEndpointMonitorImpl.class, this.customEndpointHostSpec.getUrl(), - this.storageService, + this.servicesContainer.getStorageService(), this.pluginService.getTelemetryFactory(), this.pluginService.getOriginalUrl(), this.pluginService.getDriverProtocol(), @@ -218,7 +212,7 @@ protected CustomEndpointMonitor createMonitorIfAbsent(Properties props) throws S this.pluginService.getDialect(), this.props, (connectionService, pluginService) -> new CustomEndpointMonitorImpl( - this.storageService, + this.servicesContainer.getStorageService(), this.servicesContainer.getTelemetryFactory(), this.customEndpointHostSpec, this.customEndpointId, From d63c1cd754014bb266aa129303f95269047e09f3 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 20 May 2025 17:24:50 -0700 Subject: [PATCH 121/149] Rename CoreMonitorService to MonitorService --- .../amazon/jdbc/benchmarks/PluginBenchmarks.java | 4 ++-- .../benchmarks/testplugin/TestConnectionWrapper.java | 4 ++-- .../src/main/java/software/amazon/jdbc/Driver.java | 4 ++-- .../amazon/jdbc/ds/AwsWrapperDataSource.java | 4 ++-- .../monitoring/MonitoringRdsHostListProvider.java | 4 ++-- .../MultiAzClusterTopologyMonitorImpl.java | 4 ++-- .../amazon/jdbc/util/CompleteServicesContainer.java | 8 ++++---- .../jdbc/util/CompleteServicesContainerImpl.java | 12 ++++++------ .../amazon/jdbc/util/CoreServicesContainer.java | 10 +++++----- .../jdbc/util/connection/ConnectionServiceImpl.java | 4 ++-- .../{CoreMonitorService.java => MonitorService.java} | 2 +- ...nitorServiceImpl.java => MonitorServiceImpl.java} | 8 ++++---- .../amazon/jdbc/wrapper/ConnectionWrapper.java | 4 ++-- .../CustomEndpointMonitorImplTest.java | 4 ++-- .../plugin/dev/DeveloperConnectionPluginTest.java | 4 ++-- ...viceImplTest.java => MonitorServiceImplTest.java} | 8 ++++---- 16 files changed, 44 insertions(+), 44 deletions(-) rename wrapper/src/main/java/software/amazon/jdbc/util/monitoring/{CoreMonitorService.java => MonitorService.java} (99%) rename wrapper/src/main/java/software/amazon/jdbc/util/monitoring/{CoreMonitorServiceImpl.java => MonitorServiceImpl.java} (97%) rename wrapper/src/test/java/software/amazon/jdbc/util/monitoring/{CoreMonitorServiceImplTest.java => MonitorServiceImplTest.java} (98%) diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java index 56059e905..eb7c13518 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/PluginBenchmarks.java @@ -63,7 +63,7 @@ import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.connection.ConnectionService; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.GaugeCallable; import software.amazon.jdbc.util.telemetry.TelemetryContext; @@ -92,7 +92,7 @@ public class PluginBenchmarks { .host(TEST_HOST).port(TEST_PORT).build(); @Mock private StorageService mockStorageService; - @Mock private CoreMonitorService mockMonitorService; + @Mock private MonitorService mockMonitorService; @Mock private ConnectionService mockConnectionService; @Mock private PluginService mockPluginService; @Mock private TargetDriverDialect mockTargetDriverDialect; diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java index 421c0c613..d0ec5c063 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/testplugin/TestConnectionWrapper.java @@ -26,7 +26,7 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.connection.ConnectionService; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.wrapper.ConnectionWrapper; @@ -45,7 +45,7 @@ public TestConnectionWrapper( @NonNull final HostListProviderService hostListProviderService, @NonNull final PluginManagerService pluginManagerService, @NonNull final StorageService storageService, - @NonNull final CoreMonitorService monitorService, + @NonNull final MonitorService monitorService, @NonNull final ConnectionService connectionService) throws SQLException { super( diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index 49adea4e3..0d668d9b1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -66,7 +66,7 @@ import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.CompleteServicesContainerImpl; import software.amazon.jdbc.util.StringUtils; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryContext; @@ -110,7 +110,7 @@ public class Driver implements java.sql.Driver { } private final StorageService storageService; - private final CoreMonitorService monitorService; + private final MonitorService monitorService; public Driver() { this(CoreServicesContainer.getInstance()); diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index 394baef4e..663617d20 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -55,7 +55,7 @@ import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryContext; @@ -73,7 +73,7 @@ public class AwsWrapperDataSource implements DataSource, Referenceable, Serializ private static final String SERVER_PORT = "serverPort"; private final StorageService storageService; - private final CoreMonitorService monitorService; + private final MonitorService monitorService; static { try { diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index be4c6af30..a6c9f478b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -32,7 +32,7 @@ import software.amazon.jdbc.hostlistprovider.RdsHostListProvider; import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.connection.ConnectionService; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.Topology; @@ -125,7 +125,7 @@ protected List queryForTopology(final Connection conn) throws SQLExcep @Override protected void clusterIdChanged(final String oldClusterId) throws SQLException { - CoreMonitorService monitorService = this.servicesContainer.getMonitorService(); + MonitorService monitorService = this.servicesContainer.getMonitorService(); final ClusterTopologyMonitorImpl existingMonitor = monitorService.get(ClusterTopologyMonitorImpl.class, oldClusterId); if (existingMonitor != null) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java index e93446ff6..e8a0d937d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java @@ -29,7 +29,7 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.connection.ConnectionService; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; public class MultiAzClusterTopologyMonitorImpl extends ClusterTopologyMonitorImpl { @@ -42,7 +42,7 @@ public class MultiAzClusterTopologyMonitorImpl extends ClusterTopologyMonitorImp public MultiAzClusterTopologyMonitorImpl( final String clusterId, final StorageService storageService, - final CoreMonitorService monitorService, + final MonitorService monitorService, final ConnectionService connectionService, final HostSpec initialHostSpec, final Properties properties, diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainer.java index 6fe0e5374..4f10b3c4a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainer.java @@ -20,21 +20,21 @@ import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; /** * A container object used to hold and access the various services required by the driver. This class provides access to * both connection-specific services required by plugins and monitors as well as core universal services such - * as {@link CoreMonitorService} and {@link StorageService}. + * as {@link MonitorService} and {@link StorageService}. * * @see CoreServicesContainer */ public interface CompleteServicesContainer { StorageService getStorageService(); - CoreMonitorService getMonitorService(); + MonitorService getMonitorService(); TelemetryFactory getTelemetryFactory(); @@ -46,7 +46,7 @@ public interface CompleteServicesContainer { PluginManagerService getPluginManagerService(); - void setMonitorService(CoreMonitorService monitorService); + void setMonitorService(MonitorService monitorService); void setStorageService(StorageService storageService); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainerImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainerImpl.java index 57d9721ea..808289576 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainerImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainerImpl.java @@ -20,13 +20,13 @@ import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.PluginManagerService; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class CompleteServicesContainerImpl implements CompleteServicesContainer { private StorageService storageService; - private CoreMonitorService monitorService; + private MonitorService monitorService; private TelemetryFactory telemetryFactory; private ConnectionPluginManager connectionPluginManager; private HostListProviderService hostListProviderService; @@ -35,7 +35,7 @@ public class CompleteServicesContainerImpl implements CompleteServicesContainer public CompleteServicesContainerImpl( StorageService storageService, - CoreMonitorService monitorService, + MonitorService monitorService, TelemetryFactory telemetryFactory, ConnectionPluginManager connectionPluginManager, HostListProviderService hostListProviderService, @@ -50,7 +50,7 @@ public CompleteServicesContainerImpl( public CompleteServicesContainerImpl( StorageService storageService, - CoreMonitorService monitorService, + MonitorService monitorService, TelemetryFactory telemetryFactory) { this.storageService = storageService; this.monitorService = monitorService; @@ -63,7 +63,7 @@ public StorageService getStorageService() { } @Override - public CoreMonitorService getMonitorService() { + public MonitorService getMonitorService() { return this.monitorService; } @@ -93,7 +93,7 @@ public PluginManagerService getPluginManagerService() { } @Override - public void setMonitorService(CoreMonitorService monitorService) { + public void setMonitorService(MonitorService monitorService) { this.monitorService = monitorService; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java index 9dfeea3a3..6407032a1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java @@ -18,8 +18,8 @@ import software.amazon.jdbc.util.events.BatchingEventPublisher; import software.amazon.jdbc.util.events.EventPublisher; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; -import software.amazon.jdbc.util.monitoring.CoreMonitorServiceImpl; +import software.amazon.jdbc.util.monitoring.MonitorService; +import software.amazon.jdbc.util.monitoring.MonitorServiceImpl; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.StorageServiceImpl; @@ -34,12 +34,12 @@ public class CoreServicesContainer { private static final CoreServicesContainer INSTANCE = new CoreServicesContainer(); private final StorageService storageService; - private final CoreMonitorService monitorService; + private final MonitorService monitorService; private CoreServicesContainer() { EventPublisher eventPublisher = new BatchingEventPublisher(); this.storageService = new StorageServiceImpl(eventPublisher); - this.monitorService = new CoreMonitorServiceImpl(eventPublisher); + this.monitorService = new MonitorServiceImpl(eventPublisher); } public static CoreServicesContainer getInstance() { @@ -50,7 +50,7 @@ public StorageService getStorageService() { return storageService; } - public CoreMonitorService getMonitorService() { + public MonitorService getMonitorService() { return monitorService; } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index 1ce592515..1ddd81408 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -28,7 +28,7 @@ import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.CompleteServicesContainerImpl; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -39,7 +39,7 @@ public class ConnectionServiceImpl implements ConnectionService { public ConnectionServiceImpl( StorageService storageService, - CoreMonitorService monitorService, + MonitorService monitorService, TelemetryFactory telemetryFactory, ConnectionProvider connectionProvider, String originalUrl, diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/CoreMonitorService.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java similarity index 99% rename from wrapper/src/main/java/software/amazon/jdbc/util/monitoring/CoreMonitorService.java rename to wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java index d5fa8df67..41df2c567 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/CoreMonitorService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorService.java @@ -25,7 +25,7 @@ import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -public interface CoreMonitorService { +public interface MonitorService { /** * Registers a new monitor type with the monitor service. This method needs to be called before adding new types of * monitors to the monitor service, so that the monitor service knows when to dispose of a monitor. Expected monitor diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java similarity index 97% rename from wrapper/src/main/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImpl.java rename to wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index bf9c6856b..4a4eeb2c2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -52,8 +52,8 @@ import software.amazon.jdbc.util.storage.Topology; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -public class CoreMonitorServiceImpl implements CoreMonitorService, EventSubscriber { - private static final Logger LOGGER = Logger.getLogger(CoreMonitorServiceImpl.class.getName()); +public class MonitorServiceImpl implements MonitorService, EventSubscriber { + private static final Logger LOGGER = Logger.getLogger(MonitorServiceImpl.class.getName()); protected static final long DEFAULT_CLEANUP_INTERVAL_NANOS = TimeUnit.MINUTES.toNanos(1); protected static final Map, Supplier> defaultSuppliers; @@ -84,7 +84,7 @@ public class CoreMonitorServiceImpl implements CoreMonitorService, EventSubscrib })); - public CoreMonitorServiceImpl(EventPublisher publisher) { + public MonitorServiceImpl(EventPublisher publisher) { this(DEFAULT_CLEANUP_INTERVAL_NANOS, publisher); } @@ -96,7 +96,7 @@ public CoreMonitorServiceImpl(EventPublisher publisher) { * nanoseconds. * @param publisher the publisher to subscribe to for data access events. */ - public CoreMonitorServiceImpl(long cleanupIntervalNanos, EventPublisher publisher) { + public MonitorServiceImpl(long cleanupIntervalNanos, EventPublisher publisher) { this.publisher = publisher; this.publisher.subscribe(this, new HashSet<>(Collections.singletonList(DataAccessEvent.class))); initCleanupThread(cleanupIntervalNanos); diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index 93cf221ce..29d345113 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -58,7 +58,7 @@ import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; import software.amazon.jdbc.util.connection.ConnectionService; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -135,7 +135,7 @@ protected ConnectionWrapper( @NonNull final HostListProviderService hostListProviderService, @NonNull final PluginManagerService pluginManagerService, @NonNull final StorageService storageService, - @NonNull final CoreMonitorService monitorService, + @NonNull final MonitorService monitorService, @NonNull final ConnectionService connectionService) throws SQLException { diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java index 455521d5c..afa6570e0 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImplTest.java @@ -48,13 +48,13 @@ import software.amazon.jdbc.HostSpecBuilder; import software.amazon.jdbc.hostavailability.HostAvailabilityStrategy; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class CustomEndpointMonitorImplTest { - @Mock private CoreMonitorService mockMonitorService; + @Mock private MonitorService mockMonitorService; @Mock private StorageService mockStorageService; @Mock private BiFunction mockRdsClientFunc; @Mock private RdsClient mockRdsClient; diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java index f0cb2a7d5..d4552296d 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java @@ -44,7 +44,7 @@ import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.CompleteServicesContainerImpl; -import software.amazon.jdbc.util.monitoring.CoreMonitorService; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -54,7 +54,7 @@ public class DeveloperConnectionPluginTest { private CompleteServicesContainer servicesContainer; @Mock StorageService mockStorageService; - @Mock CoreMonitorService mockMonitorService; + @Mock MonitorService mockMonitorService; @Mock ConnectionProvider mockConnectionProvider; @Mock Connection mockConnection; @Mock ConnectionPluginManager mockConnectionPluginManager; diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java similarity index 98% rename from wrapper/src/test/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImplTest.java rename to wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java index c8fe3c7bc..cfe8d714c 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/CoreMonitorServiceImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java @@ -39,19 +39,19 @@ import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -class CoreMonitorServiceImplTest { +class MonitorServiceImplTest { @Mock StorageService storageService; @Mock TelemetryFactory telemetryFactory; @Mock TargetDriverDialect targetDriverDialect; @Mock Dialect dbDialect; @Mock EventPublisher publisher; - CoreMonitorServiceImpl monitorService; + MonitorServiceImpl monitorService; private AutoCloseable closeable; @BeforeEach void setUp() { closeable = MockitoAnnotations.openMocks(this); - monitorService = new CoreMonitorServiceImpl(publisher) { + monitorService = new MonitorServiceImpl(publisher) { @Override protected void initCleanupThread(long cleanupIntervalNanos) { // Do nothing @@ -281,7 +281,7 @@ public void testStopAndRemove() throws SQLException, InterruptedException { static class NoOpMonitor extends AbstractMonitor { protected NoOpMonitor( - CoreMonitorService monitorService, + MonitorService monitorService, long terminationTimeoutSec) { super(terminationTimeoutSec); } From 76a9342dc4da90f3b5e926b2fe364ceb54471d99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 21:51:57 +0000 Subject: [PATCH 122/149] chore(deps): bump software.amazon.awssdk:auth from 2.31.36 to 2.31.45 (#1406) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- wrapper/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wrapper/build.gradle.kts b/wrapper/build.gradle.kts index 005b93ef7..60a205341 100644 --- a/wrapper/build.gradle.kts +++ b/wrapper/build.gradle.kts @@ -30,7 +30,7 @@ dependencies { implementation("org.checkerframework:checker-qual:3.49.2") compileOnly("org.apache.httpcomponents:httpclient:4.5.14") compileOnly("software.amazon.awssdk:rds:2.31.41") - compileOnly("software.amazon.awssdk:auth:2.31.36") // Required for IAM (light implementation) + compileOnly("software.amazon.awssdk:auth:2.31.45") // Required for IAM (light implementation) compileOnly("software.amazon.awssdk:http-client-spi:2.31.17") // Required for IAM (light implementation) compileOnly("software.amazon.awssdk:sts:2.30.27") compileOnly("com.zaxxer:HikariCP:4.0.3") // Version 4.+ is compatible with Java 8 @@ -66,7 +66,7 @@ dependencies { testImplementation("org.springframework.boot:spring-boot-starter-jdbc:2.7.13") // 2.7.13 is the last version compatible with Java 8 testImplementation("org.mockito:mockito-inline:4.11.0") // 4.11.0 is the last version compatible with Java 8 testImplementation("software.amazon.awssdk:rds:2.31.41") - testImplementation("software.amazon.awssdk:auth:2.31.36") // Required for IAM (light implementation) + testImplementation("software.amazon.awssdk:auth:2.31.45") // Required for IAM (light implementation) testImplementation("software.amazon.awssdk:http-client-spi:2.31.17") // Required for IAM (light implementation) testImplementation("software.amazon.awssdk:ec2:2.31.36") testImplementation("software.amazon.awssdk:secretsmanager:2.31.12") From 7e5c0fbd2517821f8e05ef612a22a04852ecb86c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 22:44:38 +0000 Subject: [PATCH 123/149] chore(deps): bump org.jetbrains.kotlin:kotlin-stdlib from 2.1.20 to 2.1.21 (#1404) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- wrapper/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper/build.gradle.kts b/wrapper/build.gradle.kts index 60a205341..fe918f913 100644 --- a/wrapper/build.gradle.kts +++ b/wrapper/build.gradle.kts @@ -47,7 +47,7 @@ dependencies { compileOnly("io.opentelemetry:opentelemetry-sdk:1.50.0") compileOnly("io.opentelemetry:opentelemetry-sdk-metrics:1.50.0") compileOnly("org.jsoup:jsoup:1.20.1") - compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.1.20") + compileOnly("org.jetbrains.kotlin:kotlin-stdlib:2.1.21") testImplementation("org.junit.platform:junit-platform-commons:1.12.2") testImplementation("org.junit.platform:junit-platform-engine:1.12.2") From 4ad627c990c34f849d03ba61ec207ea7feeeccbe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 23:30:06 +0000 Subject: [PATCH 124/149] chore(deps): bump software.amazon.awssdk:rds from 2.31.41 to 2.31.46 (#1409) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- aws-advanced-jdbc-wrapper-bundle/build.gradle.kts | 2 +- examples/AWSDriverExample/build.gradle.kts | 2 +- examples/DBCPExample/build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- .../build.gradle.kts | 2 +- examples/SpringHibernateExample/build.gradle.kts | 2 +- examples/SpringWildflyExample/spring/build.gradle.kts | 2 +- wrapper/build.gradle.kts | 4 ++-- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/aws-advanced-jdbc-wrapper-bundle/build.gradle.kts b/aws-advanced-jdbc-wrapper-bundle/build.gradle.kts index 9e2c9f02d..9993f31d9 100644 --- a/aws-advanced-jdbc-wrapper-bundle/build.gradle.kts +++ b/aws-advanced-jdbc-wrapper-bundle/build.gradle.kts @@ -25,7 +25,7 @@ repositories { dependencies { implementation("org.apache.httpcomponents:httpclient:4.5.14") - implementation("software.amazon.awssdk:rds:2.31.41") + implementation("software.amazon.awssdk:rds:2.31.46") implementation("software.amazon.awssdk:sts:2.30.27") implementation(project(":aws-advanced-jdbc-wrapper")) } diff --git a/examples/AWSDriverExample/build.gradle.kts b/examples/AWSDriverExample/build.gradle.kts index 577398ecd..6b1aacbce 100644 --- a/examples/AWSDriverExample/build.gradle.kts +++ b/examples/AWSDriverExample/build.gradle.kts @@ -18,7 +18,7 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-jdbc:2.7.13") // 2.7.13 is the last version compatible with Java 8 implementation("org.postgresql:postgresql:42.7.5") implementation("com.mysql:mysql-connector-j:9.2.0") - implementation("software.amazon.awssdk:rds:2.31.41") + implementation("software.amazon.awssdk:rds:2.31.46") implementation("software.amazon.awssdk:secretsmanager:2.31.12") implementation("software.amazon.awssdk:sts:2.30.27") implementation("com.fasterxml.jackson.core:jackson-databind:2.19.0") diff --git a/examples/DBCPExample/build.gradle.kts b/examples/DBCPExample/build.gradle.kts index f493d60f0..a8d79c3e9 100644 --- a/examples/DBCPExample/build.gradle.kts +++ b/examples/DBCPExample/build.gradle.kts @@ -19,5 +19,5 @@ dependencies { implementation("com.mysql:mysql-connector-j:9.2.0") implementation(project(":aws-advanced-jdbc-wrapper")) implementation("org.apache.commons:commons-dbcp2:2.13.0") - implementation("software.amazon.awssdk:rds:2.31.41") + implementation("software.amazon.awssdk:rds:2.31.46") } diff --git a/examples/SpringHibernateBalancedReaderOneDataSourceExample/build.gradle.kts b/examples/SpringHibernateBalancedReaderOneDataSourceExample/build.gradle.kts index f0b68800b..1096e0252 100644 --- a/examples/SpringHibernateBalancedReaderOneDataSourceExample/build.gradle.kts +++ b/examples/SpringHibernateBalancedReaderOneDataSourceExample/build.gradle.kts @@ -23,6 +23,6 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.retry:spring-retry") implementation("org.postgresql:postgresql:42.7.5") - implementation("software.amazon.awssdk:rds:2.31.41") + implementation("software.amazon.awssdk:rds:2.31.46") implementation(project(":aws-advanced-jdbc-wrapper")) } diff --git a/examples/SpringHibernateBalancedReaderTwoDataSourceExample/build.gradle.kts b/examples/SpringHibernateBalancedReaderTwoDataSourceExample/build.gradle.kts index f0b68800b..1096e0252 100644 --- a/examples/SpringHibernateBalancedReaderTwoDataSourceExample/build.gradle.kts +++ b/examples/SpringHibernateBalancedReaderTwoDataSourceExample/build.gradle.kts @@ -23,6 +23,6 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.retry:spring-retry") implementation("org.postgresql:postgresql:42.7.5") - implementation("software.amazon.awssdk:rds:2.31.41") + implementation("software.amazon.awssdk:rds:2.31.46") implementation(project(":aws-advanced-jdbc-wrapper")) } diff --git a/examples/SpringHibernateExample/build.gradle.kts b/examples/SpringHibernateExample/build.gradle.kts index 7c6bad88f..431b6f55f 100644 --- a/examples/SpringHibernateExample/build.gradle.kts +++ b/examples/SpringHibernateExample/build.gradle.kts @@ -23,6 +23,6 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.postgresql:postgresql:42.7.5") - implementation("software.amazon.awssdk:rds:2.31.41") + implementation("software.amazon.awssdk:rds:2.31.46") implementation(project(":aws-advanced-jdbc-wrapper")) } diff --git a/examples/SpringWildflyExample/spring/build.gradle.kts b/examples/SpringWildflyExample/spring/build.gradle.kts index 2777e935b..6f8318443 100644 --- a/examples/SpringWildflyExample/spring/build.gradle.kts +++ b/examples/SpringWildflyExample/spring/build.gradle.kts @@ -24,6 +24,6 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-web") runtimeOnly("org.springframework.boot:spring-boot-devtools") implementation("org.postgresql:postgresql:42.7.5") - implementation("software.amazon.awssdk:rds:2.31.41") + implementation("software.amazon.awssdk:rds:2.31.46") implementation(project(":aws-advanced-jdbc-wrapper")) } diff --git a/wrapper/build.gradle.kts b/wrapper/build.gradle.kts index fe918f913..a717c90ba 100644 --- a/wrapper/build.gradle.kts +++ b/wrapper/build.gradle.kts @@ -29,7 +29,7 @@ plugins { dependencies { implementation("org.checkerframework:checker-qual:3.49.2") compileOnly("org.apache.httpcomponents:httpclient:4.5.14") - compileOnly("software.amazon.awssdk:rds:2.31.41") + compileOnly("software.amazon.awssdk:rds:2.31.46") compileOnly("software.amazon.awssdk:auth:2.31.45") // Required for IAM (light implementation) compileOnly("software.amazon.awssdk:http-client-spi:2.31.17") // Required for IAM (light implementation) compileOnly("software.amazon.awssdk:sts:2.30.27") @@ -65,7 +65,7 @@ dependencies { testImplementation("com.mchange:c3p0:0.11.0") testImplementation("org.springframework.boot:spring-boot-starter-jdbc:2.7.13") // 2.7.13 is the last version compatible with Java 8 testImplementation("org.mockito:mockito-inline:4.11.0") // 4.11.0 is the last version compatible with Java 8 - testImplementation("software.amazon.awssdk:rds:2.31.41") + testImplementation("software.amazon.awssdk:rds:2.31.46") testImplementation("software.amazon.awssdk:auth:2.31.45") // Required for IAM (light implementation) testImplementation("software.amazon.awssdk:http-client-spi:2.31.17") // Required for IAM (light implementation) testImplementation("software.amazon.awssdk:ec2:2.31.36") From 5349779e6e46951f9e0a17033a14b9235898a8ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 00:19:55 +0000 Subject: [PATCH 125/149] chore(deps): bump org.testcontainers:testcontainers from 1.20.6 to 1.21.0 (#1402) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- wrapper/build.gradle.kts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/wrapper/build.gradle.kts b/wrapper/build.gradle.kts index a717c90ba..bb30d1798 100644 --- a/wrapper/build.gradle.kts +++ b/wrapper/build.gradle.kts @@ -72,12 +72,12 @@ dependencies { testImplementation("software.amazon.awssdk:secretsmanager:2.31.12") testImplementation("software.amazon.awssdk:sts:2.30.27") // Note: all org.testcontainers dependencies should have the same version - testImplementation("org.testcontainers:testcontainers:1.20.6") + testImplementation("org.testcontainers:testcontainers:1.21.0") testImplementation("org.testcontainers:mysql:1.21.0") - testImplementation("org.testcontainers:postgresql:1.20.6") - testImplementation("org.testcontainers:mariadb:1.20.6") + testImplementation("org.testcontainers:postgresql:1.21.0") + testImplementation("org.testcontainers:mariadb:1.21.0") testImplementation("org.testcontainers:junit-jupiter:1.21.0") - testImplementation("org.testcontainers:toxiproxy:1.20.6") + testImplementation("org.testcontainers:toxiproxy:1.21.0") testImplementation("eu.rekawek.toxiproxy:toxiproxy-java:2.1.7") testImplementation("org.apache.poi:poi-ooxml:5.4.1") testImplementation("org.slf4j:slf4j-simple:2.0.17") From eee05ff0c190528ffab1a673c9fa3c13f6735582 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 01:08:13 +0000 Subject: [PATCH 126/149] chore(deps): bump software.amazon.awssdk:sts from 2.30.27 to 2.31.46 (#1408) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- aws-advanced-jdbc-wrapper-bundle/build.gradle.kts | 2 +- examples/AWSDriverExample/build.gradle.kts | 2 +- wrapper/build.gradle.kts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/aws-advanced-jdbc-wrapper-bundle/build.gradle.kts b/aws-advanced-jdbc-wrapper-bundle/build.gradle.kts index 9993f31d9..c2ed995f8 100644 --- a/aws-advanced-jdbc-wrapper-bundle/build.gradle.kts +++ b/aws-advanced-jdbc-wrapper-bundle/build.gradle.kts @@ -26,7 +26,7 @@ repositories { dependencies { implementation("org.apache.httpcomponents:httpclient:4.5.14") implementation("software.amazon.awssdk:rds:2.31.46") - implementation("software.amazon.awssdk:sts:2.30.27") + implementation("software.amazon.awssdk:sts:2.31.46") implementation(project(":aws-advanced-jdbc-wrapper")) } diff --git a/examples/AWSDriverExample/build.gradle.kts b/examples/AWSDriverExample/build.gradle.kts index 6b1aacbce..c7a02ea3e 100644 --- a/examples/AWSDriverExample/build.gradle.kts +++ b/examples/AWSDriverExample/build.gradle.kts @@ -20,7 +20,7 @@ dependencies { implementation("com.mysql:mysql-connector-j:9.2.0") implementation("software.amazon.awssdk:rds:2.31.46") implementation("software.amazon.awssdk:secretsmanager:2.31.12") - implementation("software.amazon.awssdk:sts:2.30.27") + implementation("software.amazon.awssdk:sts:2.31.46") implementation("com.fasterxml.jackson.core:jackson-databind:2.19.0") implementation(project(":aws-advanced-jdbc-wrapper")) implementation("io.opentelemetry:opentelemetry-api:1.50.0") diff --git a/wrapper/build.gradle.kts b/wrapper/build.gradle.kts index bb30d1798..9c858607e 100644 --- a/wrapper/build.gradle.kts +++ b/wrapper/build.gradle.kts @@ -32,7 +32,7 @@ dependencies { compileOnly("software.amazon.awssdk:rds:2.31.46") compileOnly("software.amazon.awssdk:auth:2.31.45") // Required for IAM (light implementation) compileOnly("software.amazon.awssdk:http-client-spi:2.31.17") // Required for IAM (light implementation) - compileOnly("software.amazon.awssdk:sts:2.30.27") + compileOnly("software.amazon.awssdk:sts:2.31.46") compileOnly("com.zaxxer:HikariCP:4.0.3") // Version 4.+ is compatible with Java 8 compileOnly("com.mchange:c3p0:0.11.0") compileOnly("software.amazon.awssdk:secretsmanager:2.31.12") @@ -70,7 +70,7 @@ dependencies { testImplementation("software.amazon.awssdk:http-client-spi:2.31.17") // Required for IAM (light implementation) testImplementation("software.amazon.awssdk:ec2:2.31.36") testImplementation("software.amazon.awssdk:secretsmanager:2.31.12") - testImplementation("software.amazon.awssdk:sts:2.30.27") + testImplementation("software.amazon.awssdk:sts:2.31.46") // Note: all org.testcontainers dependencies should have the same version testImplementation("org.testcontainers:testcontainers:1.21.0") testImplementation("org.testcontainers:mysql:1.21.0") From 937611c6570b740be176078503f7f4cda1df0d35 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 20 May 2025 17:52:31 -0700 Subject: [PATCH 127/149] Fix checkstyle --- .../software/amazon/jdbc/ConnectionPluginChainBuilder.java | 2 +- .../java/software/amazon/jdbc/ConnectionPluginManager.java | 2 +- wrapper/src/main/java/software/amazon/jdbc/Driver.java | 4 ++-- .../software/amazon/jdbc/ServiceContainerPluginFactory.java | 6 +++--- .../java/software/amazon/jdbc/ds/AwsWrapperDataSource.java | 4 ++-- .../hostlistprovider/RdsMultiAzDbClusterListProvider.java | 2 +- .../plugin/customendpoint/CustomEndpointPluginFactory.java | 2 +- .../software/amazon/jdbc/wrapper/ConnectionWrapper.java | 4 ++-- .../container/aurora/TestAuroraHostListProvider.java | 3 ++- .../jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java | 2 +- 10 files changed, 16 insertions(+), 15 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java index 2dc1504be..da527b774 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java @@ -47,8 +47,8 @@ import software.amazon.jdbc.plugin.staledns.AuroraStaleDnsPluginFactory; import software.amazon.jdbc.plugin.strategy.fastestresponse.FastestResponseStrategyPluginFactory; import software.amazon.jdbc.profile.ConfigurationProfile; -import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java index ed3b308e7..567430acc 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java @@ -50,8 +50,8 @@ import software.amazon.jdbc.plugin.strategy.fastestresponse.FastestResponseStrategyPlugin; import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.util.AsynchronousMethodsHelper; -import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.SqlMethodAnalyzer; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.WrapperUtils; diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index 0d668d9b1..a02b95f01 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -57,14 +57,14 @@ import software.amazon.jdbc.states.TransferSessionStateOnSwitchCallable; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialectManager; +import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.CompleteServicesContainerImpl; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.CoreServicesContainer; import software.amazon.jdbc.util.DriverInfo; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; -import software.amazon.jdbc.util.CompleteServicesContainer; -import software.amazon.jdbc.util.CompleteServicesContainerImpl; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; diff --git a/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java index a4c9a387a..6bb98b6a0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java @@ -23,15 +23,15 @@ * A factory for plugins that utilizes a ServiceContainer. This interface extends {@link ConnectionPluginFactory} to * provide additional flexibility in plugin instantiation while maintaining backward compatibility. * - *

Implementations of this interface can access all services in the {@link CompleteServicesContainer} when creating connection - * plugins, rather than being limited to just the {@link PluginService}

+ *

Implementations of this interface can access all services in the {@link CompleteServicesContainer} when creating + * connection plugins, rather than being limited to just the {@link PluginService}

*/ public interface ServiceContainerPluginFactory extends ConnectionPluginFactory { /** * Get an instance of a {@link ConnectionPlugin}. * * @param servicesContainer the service container containing the services to be used by the {@link ConnectionPlugin}. - * @param props to be used by the {@link ConnectionPlugin}. + * @param props to be used by the {@link ConnectionPlugin}. * @return an instance of a {@link ConnectionPlugin}. */ ConnectionPlugin getInstance(CompleteServicesContainer servicesContainer, Properties props); diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index 663617d20..c7e72a01c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -46,12 +46,12 @@ import software.amazon.jdbc.profile.DriverConfigurationProfiles; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialectManager; +import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.CompleteServicesContainerImpl; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.CoreServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; -import software.amazon.jdbc.util.CompleteServicesContainer; -import software.amazon.jdbc.util.CompleteServicesContainerImpl; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java index 3d045132c..a161011d0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java @@ -31,8 +31,8 @@ import software.amazon.jdbc.HostRole; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.hostavailability.HostAvailability; -import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.Messages; public class RdsMultiAzDbClusterListProvider extends RdsHostListProvider { private final String fetchWriterNodeQuery; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java index bbc92a0e5..d86234db7 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java @@ -21,8 +21,8 @@ import software.amazon.jdbc.ConnectionPlugin; import software.amazon.jdbc.PluginService; import software.amazon.jdbc.ServiceContainerPluginFactory; -import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.Messages; public class CustomEndpointPluginFactory implements ServiceContainerPluginFactory { @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index 29d345113..c21d09a0a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -50,10 +50,10 @@ import software.amazon.jdbc.dialect.HostListProviderSupplier; import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.ConnectionUrlParser; -import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.CompleteServicesContainerImpl; +import software.amazon.jdbc.util.ConnectionUrlParser; +import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.WrapperUtils; diff --git a/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java b/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java index 1f055d1cc..c9a395dfc 100644 --- a/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java +++ b/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java @@ -22,7 +22,8 @@ public class TestAuroraHostListProvider extends AuroraHostListProvider { - public TestAuroraHostListProvider(CompleteServicesContainer servicesContainer, Properties properties, String originalUrl) { + public TestAuroraHostListProvider( + CompleteServicesContainer servicesContainer, Properties properties, String originalUrl) { super(properties, originalUrl, servicesContainer, "", "", ""); } diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java index 942edf007..914f23061 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java @@ -70,9 +70,9 @@ import software.amazon.jdbc.profile.ConfigurationProfileBuilder; import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Pair; -import software.amazon.jdbc.util.CompleteServicesContainer; import software.amazon.jdbc.util.telemetry.GaugeCallable; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; From 018cfb3e70927d3d57b7600447af62e9648c83ed Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 21 May 2025 09:37:19 -0700 Subject: [PATCH 128/149] Rename CompleteServicesContainer to FullServicesContainer, rename serviceContainer variables to servicesContainer --- .../ConnectionPluginManagerBenchmarks.java | 4 ++-- .../amazon/jdbc/ConnectionPluginChainBuilder.java | 10 +++++----- .../amazon/jdbc/ConnectionPluginFactory.java | 6 +++--- .../amazon/jdbc/ConnectionPluginManager.java | 6 +++--- .../src/main/java/software/amazon/jdbc/Driver.java | 8 ++++---- .../software/amazon/jdbc/PartialPluginService.java | 8 ++++---- .../software/amazon/jdbc/PluginServiceImpl.java | 10 +++++----- ...ory.java => ServicesContainerPluginFactory.java} | 13 +++++++------ .../amazon/jdbc/dialect/AuroraPgDialect.java | 8 ++++---- .../jdbc/dialect/HostListProviderSupplier.java | 4 ++-- .../amazon/jdbc/dialect/MariaDbDialect.java | 4 ++-- .../software/amazon/jdbc/dialect/MysqlDialect.java | 4 ++-- .../software/amazon/jdbc/dialect/PgDialect.java | 4 ++-- .../dialect/RdsMultiAzDbClusterMysqlDialect.java | 8 ++++---- .../jdbc/dialect/RdsMultiAzDbClusterPgDialect.java | 8 ++++---- .../amazon/jdbc/dialect/UnknownDialect.java | 4 ++-- .../amazon/jdbc/ds/AwsWrapperDataSource.java | 8 ++++---- .../hostlistprovider/AuroraHostListProvider.java | 4 ++-- .../jdbc/hostlistprovider/RdsHostListProvider.java | 6 +++--- .../RdsMultiAzDbClusterListProvider.java | 4 ++-- .../monitoring/MonitoringRdsHostListProvider.java | 6 +++--- .../MonitoringRdsMultiAzHostListProvider.java | 4 ++-- .../plugin/customendpoint/CustomEndpointPlugin.java | 8 ++++---- .../customendpoint/CustomEndpointPluginFactory.java | 10 +++++----- .../amazon/jdbc/util/CoreServicesContainer.java | 2 +- ...cesContainer.java => FullServicesContainer.java} | 2 +- ...inerImpl.java => FullServicesContainerImpl.java} | 6 +++--- .../jdbc/util/connection/ConnectionServiceImpl.java | 8 ++++---- .../amazon/jdbc/wrapper/ConnectionWrapper.java | 10 +++++----- .../aws_advanced_jdbc_wrapper_messages.properties | 2 +- .../aurora/TestAuroraHostListProvider.java | 4 ++-- .../container/aurora/TestPluginServiceImpl.java | 4 ++-- .../jdbc/ConnectionPluginChainBuilderTests.java | 4 ++-- .../amazon/jdbc/ConnectionPluginManagerTests.java | 4 ++-- .../software/amazon/jdbc/DialectDetectionTests.java | 4 ++-- .../amazon/jdbc/PluginServiceImplTests.java | 4 ++-- .../hostlistprovider/RdsHostListProviderTest.java | 4 ++-- .../RdsMultiAzDbClusterListProviderTest.java | 4 ++-- .../AwsSecretsManagerConnectionPluginTest.java | 4 ++-- .../customendpoint/CustomEndpointPluginTest.java | 4 ++-- .../plugin/dev/DeveloperConnectionPluginTest.java | 8 ++++---- 41 files changed, 119 insertions(+), 118 deletions(-) rename wrapper/src/main/java/software/amazon/jdbc/{ServiceContainerPluginFactory.java => ServicesContainerPluginFactory.java} (69%) rename wrapper/src/main/java/software/amazon/jdbc/util/{CompleteServicesContainer.java => FullServicesContainer.java} (97%) rename wrapper/src/main/java/software/amazon/jdbc/util/{CompleteServicesContainerImpl.java => FullServicesContainerImpl.java} (96%) diff --git a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java index 81839d672..c259366cd 100644 --- a/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java +++ b/benchmarks/src/jmh/java/software/amazon/jdbc/benchmarks/ConnectionPluginManagerBenchmarks.java @@ -67,7 +67,7 @@ import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.profile.ConfigurationProfileBuilder; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.telemetry.DefaultTelemetryFactory; import software.amazon.jdbc.util.telemetry.GaugeCallable; import software.amazon.jdbc.util.telemetry.TelemetryContext; @@ -94,7 +94,7 @@ public class ConnectionPluginManagerBenchmarks { @Mock ConnectionProvider mockConnectionProvider; @Mock ConnectionWrapper mockConnectionWrapper; - @Mock CompleteServicesContainer mockServicesContainer; + @Mock FullServicesContainer mockServicesContainer; @Mock PluginService mockPluginService; @Mock PluginManagerService mockPluginManagerService; @Mock TelemetryFactory mockTelemetryFactory; diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java index da527b774..ab81eb97b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java @@ -47,7 +47,7 @@ import software.amazon.jdbc.plugin.staledns.AuroraStaleDnsPluginFactory; import software.amazon.jdbc.plugin.strategy.fastestresponse.FastestResponseStrategyPluginFactory; import software.amazon.jdbc.profile.ConfigurationProfile; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; @@ -134,7 +134,7 @@ public PluginFactoryInfo(final Class factory, } public List getPlugins( - final CompleteServicesContainer servicesContainer, + final FullServicesContainer servicesContainer, final ConnectionProvider defaultConnProvider, final ConnectionProvider effectiveConnProvider, final PluginManagerService pluginManagerService, @@ -189,9 +189,9 @@ public List getPlugins( plugins = new ArrayList<>(factories.length + 1); for (final ConnectionPluginFactory factory : factories) { - if (factory instanceof ServiceContainerPluginFactory) { - ServiceContainerPluginFactory serviceContainerPluginFactory = (ServiceContainerPluginFactory) factory; - plugins.add(serviceContainerPluginFactory.getInstance(servicesContainer, props)); + if (factory instanceof ServicesContainerPluginFactory) { + ServicesContainerPluginFactory servicesContainerPluginFactory = (ServicesContainerPluginFactory) factory; + plugins.add(servicesContainerPluginFactory.getInstance(servicesContainer, props)); } else { plugins.add(factory.getInstance(servicesContainer.getPluginService(), props)); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginFactory.java index 2e7cbd896..8471cdbd4 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginFactory.java @@ -17,14 +17,14 @@ package software.amazon.jdbc; import java.util.Properties; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; /** * Interface for connection plugin factories. This class implements ways to initialize a connection * plugin. * - * @apiNote Consider using {@link ServiceContainerPluginFactory} for new implementations as it provides access to all - * services in the {@link CompleteServicesContainer}. + * @apiNote Consider using {@link ServicesContainerPluginFactory} for new implementations as it provides access to all + * services in the {@link FullServicesContainer}. */ public interface ConnectionPluginFactory { diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java index 567430acc..b83483d4c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java @@ -50,7 +50,7 @@ import software.amazon.jdbc.plugin.strategy.fastestresponse.FastestResponseStrategyPlugin; import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.util.AsynchronousMethodsHelper; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.SqlMethodAnalyzer; import software.amazon.jdbc.util.Utils; @@ -111,7 +111,7 @@ public class ConnectionPluginManager implements CanReleaseResources, Wrapper { protected final @NonNull ConnectionProvider defaultConnProvider; protected final @Nullable ConnectionProvider effectiveConnProvider; protected final ConnectionWrapper connectionWrapper; - protected CompleteServicesContainer servicesContainer; + protected FullServicesContainer servicesContainer; protected PluginService pluginService; protected TelemetryFactory telemetryFactory; @@ -189,7 +189,7 @@ public boolean isHeldByCurrentThread() { * @throws SQLException if errors occurred during the execution */ public void init( - final CompleteServicesContainer servicesContainer, + final FullServicesContainer servicesContainer, final Properties props, final PluginManagerService pluginManagerService, @Nullable ConfigurationProfile configurationProfile) diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index a02b95f01..3bf9ba422 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -57,8 +57,8 @@ import software.amazon.jdbc.states.TransferSessionStateOnSwitchCallable; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialectManager; -import software.amazon.jdbc.util.CompleteServicesContainer; -import software.amazon.jdbc.util.CompleteServicesContainerImpl; +import software.amazon.jdbc.util.FullServicesContainer; +import software.amazon.jdbc.util.FullServicesContainerImpl; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.CoreServicesContainer; import software.amazon.jdbc.util.DriverInfo; @@ -232,8 +232,8 @@ public Connection connect(final String url, final Properties info) throws SQLExc effectiveConnectionProvider = configurationProfile.getConnectionProvider(); } - CompleteServicesContainer - servicesContainer = new CompleteServicesContainerImpl(storageService, monitorService, telemetryFactory); + FullServicesContainer + servicesContainer = new FullServicesContainerImpl(storageService, monitorService, telemetryFactory); return new ConnectionWrapper( servicesContainer, diff --git a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java index 4f31bc10b..7393cd555 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java @@ -46,7 +46,7 @@ import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.states.SessionStateServiceImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.CacheMap; @@ -65,7 +65,7 @@ public class PartialPluginService implements PluginService, CanReleaseResources, protected static final long DEFAULT_HOST_AVAILABILITY_CACHE_EXPIRE_NANO = TimeUnit.MINUTES.toNanos(5); protected static final CacheMap hostAvailabilityExpiringCache = new CacheMap<>(); - protected final CompleteServicesContainer servicesContainer; + protected final FullServicesContainer servicesContainer; protected final ConnectionPluginManager pluginManager; protected final Properties props; protected volatile HostListProvider hostListProvider; @@ -88,7 +88,7 @@ public class PartialPluginService implements PluginService, CanReleaseResources, protected final ReentrantLock connectionSwitchLock = new ReentrantLock(); public PartialPluginService( - @NonNull final CompleteServicesContainer servicesContainer, + @NonNull final FullServicesContainer servicesContainer, @NonNull final Properties props, @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, @@ -107,7 +107,7 @@ public PartialPluginService( } public PartialPluginService( - @NonNull final CompleteServicesContainer servicesContainer, + @NonNull final FullServicesContainer servicesContainer, @NonNull final ExceptionManager exceptionManager, @NonNull final Properties props, @NonNull final String originalUrl, diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index e897969f7..5a6246fe8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -50,7 +50,7 @@ import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.states.SessionStateServiceImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.CacheMap; @@ -63,7 +63,7 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources, protected static final long DEFAULT_HOST_AVAILABILITY_CACHE_EXPIRE_NANO = TimeUnit.MINUTES.toNanos(5); protected static final CacheMap hostAvailabilityExpiringCache = new CacheMap<>(); - protected final CompleteServicesContainer servicesContainer; + protected final FullServicesContainer servicesContainer; protected final ConnectionPluginManager pluginManager; private final Properties props; private final String originalUrl; @@ -87,7 +87,7 @@ public class PluginServiceImpl implements PluginService, CanReleaseResources, protected final ReentrantLock connectionSwitchLock = new ReentrantLock(); public PluginServiceImpl( - @NonNull final CompleteServicesContainer servicesContainer, + @NonNull final FullServicesContainer servicesContainer, @NonNull final Properties props, @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, @@ -107,7 +107,7 @@ public PluginServiceImpl( } public PluginServiceImpl( - @NonNull final CompleteServicesContainer servicesContainer, + @NonNull final FullServicesContainer servicesContainer, @NonNull final Properties props, @NonNull final String originalUrl, @NonNull final String targetDriverProtocol, @@ -126,7 +126,7 @@ public PluginServiceImpl( } public PluginServiceImpl( - @NonNull final CompleteServicesContainer servicesContainer, + @NonNull final FullServicesContainer servicesContainer, @NonNull final ExceptionManager exceptionManager, @NonNull final Properties props, @NonNull final String originalUrl, diff --git a/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/ServicesContainerPluginFactory.java similarity index 69% rename from wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java rename to wrapper/src/main/java/software/amazon/jdbc/ServicesContainerPluginFactory.java index 6bb98b6a0..54be6db78 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ServiceContainerPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ServicesContainerPluginFactory.java @@ -17,16 +17,17 @@ package software.amazon.jdbc; import java.util.Properties; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; /** - * A factory for plugins that utilizes a ServiceContainer. This interface extends {@link ConnectionPluginFactory} to - * provide additional flexibility in plugin instantiation while maintaining backward compatibility. + * A factory for plugins that utilizes a {@link FullServicesContainer}. This interface extends + * {@link ConnectionPluginFactory} to provide additional flexibility in plugin instantiation while maintaining backward + * compatibility. * - *

Implementations of this interface can access all services in the {@link CompleteServicesContainer} when creating + *

Implementations of this interface can access all services in the {@link FullServicesContainer} when creating * connection plugins, rather than being limited to just the {@link PluginService}

*/ -public interface ServiceContainerPluginFactory extends ConnectionPluginFactory { +public interface ServicesContainerPluginFactory extends ConnectionPluginFactory { /** * Get an instance of a {@link ConnectionPlugin}. * @@ -34,5 +35,5 @@ public interface ServiceContainerPluginFactory extends ConnectionPluginFactory { * @param props to be used by the {@link ConnectionPlugin}. * @return an instance of a {@link ConnectionPlugin}. */ - ConnectionPlugin getInstance(CompleteServicesContainer servicesContainer, Properties props); + ConnectionPlugin getInstance(FullServicesContainer servicesContainer, Properties props); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java index 5322d9f15..d3a8562f6 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/AuroraPgDialect.java @@ -128,15 +128,15 @@ public boolean isDialect(final Connection connection) { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, serviceContainer) -> { - final PluginService pluginService = serviceContainer.getPluginService(); + return (properties, initialUrl, servicesContainer) -> { + final PluginService pluginService = servicesContainer.getPluginService(); final FailoverConnectionPlugin failover2Plugin = pluginService.getPlugin(FailoverConnectionPlugin.class); if (failover2Plugin != null) { return new MonitoringRdsHostListProvider( properties, initialUrl, - serviceContainer, + servicesContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, @@ -145,7 +145,7 @@ public HostListProviderSupplier getHostListProvider() { return new AuroraHostListProvider( properties, initialUrl, - serviceContainer, + servicesContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY); diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java index aab6e544d..0dfe44dc5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/HostListProviderSupplier.java @@ -19,12 +19,12 @@ import java.util.Properties; import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.HostListProvider; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; @FunctionalInterface public interface HostListProviderSupplier { @NonNull HostListProvider getProvider( final @NonNull Properties properties, final String initialUrl, - final @NonNull CompleteServicesContainer servicesContainer); + final @NonNull FullServicesContainer servicesContainer); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java index 1e0711d8c..3b368a8a1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/MariaDbDialect.java @@ -104,8 +104,8 @@ public List getDialectUpdateCandidates() { } public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, serviceContainer) -> - new ConnectionStringHostListProvider(properties, initialUrl, serviceContainer.getHostListProviderService()); + return (properties, initialUrl, servicesContainer) -> + new ConnectionStringHostListProvider(properties, initialUrl, servicesContainer.getHostListProviderService()); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java index 964bd565d..df84fd6ab 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/MysqlDialect.java @@ -108,8 +108,8 @@ public List getDialectUpdateCandidates() { } public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, serviceContainer) -> - new ConnectionStringHostListProvider(properties, initialUrl, serviceContainer.getHostListProviderService()); + return (properties, initialUrl, servicesContainer) -> + new ConnectionStringHostListProvider(properties, initialUrl, servicesContainer.getHostListProviderService()); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java index 43b60d12b..87c4c705c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/PgDialect.java @@ -106,8 +106,8 @@ public List getDialectUpdateCandidates() { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, serviceContainer) -> - new ConnectionStringHostListProvider(properties, initialUrl, serviceContainer.getHostListProviderService()); + return (properties, initialUrl, servicesContainer) -> + new ConnectionStringHostListProvider(properties, initialUrl, servicesContainer.getHostListProviderService()); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java index f1e15d56b..205dbdd16 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterMysqlDialect.java @@ -97,15 +97,15 @@ public boolean isDialect(final Connection connection) { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, serviceContainer) -> { - final PluginService pluginService = serviceContainer.getPluginService(); + return (properties, initialUrl, servicesContainer) -> { + final PluginService pluginService = servicesContainer.getPluginService(); final FailoverConnectionPlugin failover2Plugin = pluginService.getPlugin(FailoverConnectionPlugin.class); if (failover2Plugin != null) { return new MonitoringRdsMultiAzHostListProvider( properties, initialUrl, - serviceContainer, + servicesContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, @@ -116,7 +116,7 @@ public HostListProviderSupplier getHostListProvider() { return new RdsMultiAzDbClusterListProvider( properties, initialUrl, - serviceContainer, + servicesContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java index 92abd7a9f..19f950bb2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/RdsMultiAzDbClusterPgDialect.java @@ -113,15 +113,15 @@ public boolean isDialect(final Connection connection) { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, serviceContainer) -> { - final PluginService pluginService = serviceContainer.getPluginService(); + return (properties, initialUrl, servicesContainer) -> { + final PluginService pluginService = servicesContainer.getPluginService(); final FailoverConnectionPlugin failover2Plugin = pluginService.getPlugin(FailoverConnectionPlugin.class); if (failover2Plugin != null) { return new MonitoringRdsMultiAzHostListProvider( properties, initialUrl, - serviceContainer, + servicesContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, @@ -133,7 +133,7 @@ public HostListProviderSupplier getHostListProvider() { return new RdsMultiAzDbClusterListProvider( properties, initialUrl, - serviceContainer, + servicesContainer, TOPOLOGY_QUERY, NODE_ID_QUERY, IS_READER_QUERY, diff --git a/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java b/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java index 94a97ddfe..65b9eb544 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java +++ b/wrapper/src/main/java/software/amazon/jdbc/dialect/UnknownDialect.java @@ -81,8 +81,8 @@ public List getDialectUpdateCandidates() { @Override public HostListProviderSupplier getHostListProvider() { - return (properties, initialUrl, serviceContainer) -> - new ConnectionStringHostListProvider(properties, initialUrl, serviceContainer.getHostListProviderService()); + return (properties, initialUrl, servicesContainer) -> + new ConnectionStringHostListProvider(properties, initialUrl, servicesContainer.getHostListProviderService()); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index c7e72a01c..3ad78cf69 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -46,8 +46,8 @@ import software.amazon.jdbc.profile.DriverConfigurationProfiles; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialectManager; -import software.amazon.jdbc.util.CompleteServicesContainer; -import software.amazon.jdbc.util.CompleteServicesContainerImpl; +import software.amazon.jdbc.util.FullServicesContainer; +import software.amazon.jdbc.util.FullServicesContainerImpl; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.CoreServicesContainer; import software.amazon.jdbc.util.Messages; @@ -269,8 +269,8 @@ ConnectionWrapper createConnectionWrapper( final @NonNull TargetDriverDialect targetDriverDialect, final @Nullable ConfigurationProfile configurationProfile, final TelemetryFactory telemetryFactory) throws SQLException { - CompleteServicesContainer - servicesContainer = new CompleteServicesContainerImpl(storageService, monitorService, telemetryFactory); + FullServicesContainer + servicesContainer = new FullServicesContainerImpl(storageService, monitorService, telemetryFactory); return new ConnectionWrapper( servicesContainer, props, diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java index 2bbe9b177..fc53f9e1d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/AuroraHostListProvider.java @@ -19,7 +19,7 @@ import java.util.Properties; import java.util.logging.Logger; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; public class AuroraHostListProvider extends RdsHostListProvider { @@ -29,7 +29,7 @@ public class AuroraHostListProvider extends RdsHostListProvider { public AuroraHostListProvider( final Properties properties, final String originalUrl, - final CompleteServicesContainer servicesContainer, + final FullServicesContainer servicesContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index e9a3d0cd0..d898a5465 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -49,7 +49,7 @@ import software.amazon.jdbc.HostSpecBuilder; import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.hostavailability.HostAvailability; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; @@ -95,7 +95,7 @@ public class RdsHostListProvider implements DynamicHostListProvider { protected static final CacheMap suggestedPrimaryClusterIdCache = new CacheMap<>(); protected static final CacheMap primaryClusterIdCache = new CacheMap<>(); - protected final CompleteServicesContainer servicesContainer; + protected final FullServicesContainer servicesContainer; protected final HostListProviderService hostListProviderService; protected final String originalUrl; protected final String topologyQuery; @@ -128,7 +128,7 @@ public class RdsHostListProvider implements DynamicHostListProvider { public RdsHostListProvider( final Properties properties, final String originalUrl, - final CompleteServicesContainer servicesContainer, + final FullServicesContainer servicesContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java index a161011d0..a63323176 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProvider.java @@ -31,7 +31,7 @@ import software.amazon.jdbc.HostRole; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.hostavailability.HostAvailability; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.Messages; public class RdsMultiAzDbClusterListProvider extends RdsHostListProvider { @@ -42,7 +42,7 @@ public class RdsMultiAzDbClusterListProvider extends RdsHostListProvider { public RdsMultiAzDbClusterListProvider( final Properties properties, final String originalUrl, - final CompleteServicesContainer servicesContainer, + final FullServicesContainer servicesContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery, diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index a6c9f478b..64e2c34d3 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -30,7 +30,7 @@ import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.cleanup.CanReleaseResources; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; @@ -51,7 +51,7 @@ public class MonitoringRdsHostListProvider extends RdsHostListProvider PropertyDefinition.registerPluginProperties(MonitoringRdsHostListProvider.class); } - protected final CompleteServicesContainer servicesContainer; + protected final FullServicesContainer servicesContainer; protected final PluginService pluginService; protected final long highRefreshRateNano; protected final String writerTopologyQuery; @@ -59,7 +59,7 @@ public class MonitoringRdsHostListProvider extends RdsHostListProvider public MonitoringRdsHostListProvider( final Properties properties, final String originalUrl, - final CompleteServicesContainer servicesContainer, + final FullServicesContainer servicesContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery, diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java index 968351859..ba55825b5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java @@ -19,7 +19,7 @@ import java.sql.SQLException; import java.util.Properties; import java.util.logging.Logger; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; public class MonitoringRdsMultiAzHostListProvider extends MonitoringRdsHostListProvider { @@ -31,7 +31,7 @@ public class MonitoringRdsMultiAzHostListProvider extends MonitoringRdsHostListP public MonitoringRdsMultiAzHostListProvider( final Properties properties, final String originalUrl, - final CompleteServicesContainer servicesContainer, + final FullServicesContainer servicesContainer, final String topologyQuery, final String nodeIdQuery, final String isReaderQuery, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java index da25707e4..2619b7d84 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java @@ -34,7 +34,7 @@ import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.authentication.AwsCredentialsManager; import software.amazon.jdbc.plugin.AbstractConnectionPlugin; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUtils; import software.amazon.jdbc.util.RegionUtils; @@ -88,7 +88,7 @@ public class CustomEndpointPlugin extends AbstractConnectionPlugin { PropertyDefinition.registerPluginProperties(CustomEndpointPlugin.class); } - protected final CompleteServicesContainer servicesContainer; + protected final FullServicesContainer servicesContainer; protected final PluginService pluginService; protected final TelemetryFactory telemetryFactory; protected final Properties props; @@ -109,7 +109,7 @@ public class CustomEndpointPlugin extends AbstractConnectionPlugin { * @param servicesContainer The service container for the services required by this class. * @param props The properties that the custom endpoint plugin should use. */ - public CustomEndpointPlugin(final CompleteServicesContainer servicesContainer, final Properties props) { + public CustomEndpointPlugin(final FullServicesContainer servicesContainer, final Properties props) { this( servicesContainer, props, @@ -128,7 +128,7 @@ public CustomEndpointPlugin(final CompleteServicesContainer servicesContainer, f * @param rdsClientFunc The function to call to obtain an {@link RdsClient} instance. */ public CustomEndpointPlugin( - final CompleteServicesContainer servicesContainer, + final FullServicesContainer servicesContainer, final Properties props, final BiFunction rdsClientFunc) { this.servicesContainer = servicesContainer; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java index d86234db7..db79591b5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginFactory.java @@ -20,20 +20,20 @@ import java.util.Properties; import software.amazon.jdbc.ConnectionPlugin; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.ServiceContainerPluginFactory; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.ServicesContainerPluginFactory; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.Messages; -public class CustomEndpointPluginFactory implements ServiceContainerPluginFactory { +public class CustomEndpointPluginFactory implements ServicesContainerPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { throw new UnsupportedOperationException( Messages.get( - "ServiceContainerPluginFactory.serviceContainerRequired", new Object[] {"CustomEndpointPlugin"})); + "ServicesContainerPluginFactory.servicesContainerRequired", new Object[] {"CustomEndpointPlugin"})); } @Override - public ConnectionPlugin getInstance(final CompleteServicesContainer servicesContainer, final Properties props) { + public ConnectionPlugin getInstance(final FullServicesContainer servicesContainer, final Properties props) { try { Class.forName("software.amazon.awssdk.services.rds.RdsClient"); } catch (final ClassNotFoundException e) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java index 6407032a1..3ac3e0a1b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/CoreServicesContainer.java @@ -27,7 +27,7 @@ * A singleton container object used to instantiate and access core universal services. This class should be used * instead of directly instantiating core services so that only one instance of each service is instantiated. * - * @see CompleteServicesContainer for a container that holds both connection-specific services and core universal + * @see FullServicesContainer for a container that holds both connection-specific services and core universal * services. */ public class CoreServicesContainer { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainer.java b/wrapper/src/main/java/software/amazon/jdbc/util/FullServicesContainer.java similarity index 97% rename from wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainer.java rename to wrapper/src/main/java/software/amazon/jdbc/util/FullServicesContainer.java index 4f10b3c4a..e276b4503 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/FullServicesContainer.java @@ -31,7 +31,7 @@ * * @see CoreServicesContainer */ -public interface CompleteServicesContainer { +public interface FullServicesContainer { StorageService getStorageService(); MonitorService getMonitorService(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainerImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/FullServicesContainerImpl.java similarity index 96% rename from wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainerImpl.java rename to wrapper/src/main/java/software/amazon/jdbc/util/FullServicesContainerImpl.java index 808289576..ef0a0fc53 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/CompleteServicesContainerImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/FullServicesContainerImpl.java @@ -24,7 +24,7 @@ import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -public class CompleteServicesContainerImpl implements CompleteServicesContainer { +public class FullServicesContainerImpl implements FullServicesContainer { private StorageService storageService; private MonitorService monitorService; private TelemetryFactory telemetryFactory; @@ -33,7 +33,7 @@ public class CompleteServicesContainerImpl implements CompleteServicesContainer private PluginService pluginService; private PluginManagerService pluginManagerService; - public CompleteServicesContainerImpl( + public FullServicesContainerImpl( StorageService storageService, MonitorService monitorService, TelemetryFactory telemetryFactory, @@ -48,7 +48,7 @@ public CompleteServicesContainerImpl( this.pluginManagerService = pluginManagerService; } - public CompleteServicesContainerImpl( + public FullServicesContainerImpl( StorageService storageService, MonitorService monitorService, TelemetryFactory telemetryFactory) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java index 1ddd81408..73a68fdcb 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/connection/ConnectionServiceImpl.java @@ -26,8 +26,8 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.CompleteServicesContainer; -import software.amazon.jdbc.util.CompleteServicesContainerImpl; +import software.amazon.jdbc.util.FullServicesContainer; +import software.amazon.jdbc.util.FullServicesContainerImpl; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -49,8 +49,8 @@ public ConnectionServiceImpl( Properties props) throws SQLException { this.targetDriverProtocol = targetDriverProtocol; - CompleteServicesContainer - servicesContainer = new CompleteServicesContainerImpl(storageService, monitorService, telemetryFactory); + FullServicesContainer + servicesContainer = new FullServicesContainerImpl(storageService, monitorService, telemetryFactory); this.pluginManager = new ConnectionPluginManager( connectionProvider, null, diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index c21d09a0a..a9155009f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -50,8 +50,8 @@ import software.amazon.jdbc.dialect.HostListProviderSupplier; import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.CompleteServicesContainer; -import software.amazon.jdbc.util.CompleteServicesContainerImpl; +import software.amazon.jdbc.util.FullServicesContainer; +import software.amazon.jdbc.util.FullServicesContainerImpl; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.SqlState; @@ -81,7 +81,7 @@ public class ConnectionWrapper implements Connection, CanReleaseResources { protected final ConnectionUrlParser connectionUrlParser = new ConnectionUrlParser(); public ConnectionWrapper( - @NonNull final CompleteServicesContainer servicesContainer, + @NonNull final FullServicesContainer servicesContainer, @NonNull final Properties props, @NonNull final String url, @NonNull final ConnectionProvider defaultConnectionProvider, @@ -143,7 +143,7 @@ protected ConnectionWrapper( throw new IllegalArgumentException("url"); } - CompleteServicesContainer servicesContainer = new CompleteServicesContainerImpl( + FullServicesContainer servicesContainer = new FullServicesContainerImpl( storageService, monitorService, telemetryFactory, @@ -157,7 +157,7 @@ protected ConnectionWrapper( } protected void init(final Properties props, - final CompleteServicesContainer servicesContainer, + final FullServicesContainer servicesContainer, final ConnectionProvider defaultConnectionProvider, final TargetDriverDialect driverDialect) throws SQLException { this.pluginManager = servicesContainer.getConnectionPluginManager(); diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index c12011ecc..8b1c7686f 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -371,7 +371,7 @@ SAMLCredentialsProviderFactory.getSamlAssertionFailed=Failed to get SAML Asserti SamlAuthPlugin.javaStsSdkNotInClasspath=Required dependency 'AWS Java SDK for AWS Secret Token Service' is not on the classpath. SamlAuthPlugin.unhandledException=Unhandled exception: ''{0}'' -ServiceContainerPluginFactory.serviceContainerRequired=The {0} requires a ServiceContainer. Please use getInstance(ServiceContainer, Properties) instead. +ServicesContainerPluginFactory.servicesContainerRequired=The {0} requires a FullServicesContainer. Please use getInstance(FullServicesContainer, Properties) instead. StorageServiceImpl.unexpectedValueMismatch=Attempted to store value ''{0}'' under item class ''{1}'' but there was an unexpected mismatch between the passed in value type and the expected value type. The cache for item class ''{1}'' is ''{2}''. StorageServiceImpl.itemClassNotRegistered=The given item class ''{0}'' is not registered. Please register the item class before storing items of that class. diff --git a/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java b/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java index c9a395dfc..c35f6b0f8 100644 --- a/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java +++ b/wrapper/src/test/java/integration/container/aurora/TestAuroraHostListProvider.java @@ -18,12 +18,12 @@ import java.util.Properties; import software.amazon.jdbc.hostlistprovider.AuroraHostListProvider; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; public class TestAuroraHostListProvider extends AuroraHostListProvider { public TestAuroraHostListProvider( - CompleteServicesContainer servicesContainer, Properties properties, String originalUrl) { + FullServicesContainer servicesContainer, Properties properties, String originalUrl) { super(properties, originalUrl, servicesContainer, "", "", ""); } diff --git a/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java b/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java index de24f8919..b356eb391 100644 --- a/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java +++ b/wrapper/src/test/java/integration/container/aurora/TestPluginServiceImpl.java @@ -21,12 +21,12 @@ import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.PluginServiceImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; public class TestPluginServiceImpl extends PluginServiceImpl { public TestPluginServiceImpl( - @NonNull CompleteServicesContainer servicesContainer, + @NonNull FullServicesContainer servicesContainer, @NonNull Properties props, @NonNull String originalUrl, String targetDriverProtocol, diff --git a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java index c3739e85d..c577925f1 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginChainBuilderTests.java @@ -39,14 +39,14 @@ import software.amazon.jdbc.plugin.efm2.HostMonitoringConnectionPlugin; import software.amazon.jdbc.plugin.failover.FailoverConnectionPlugin; import software.amazon.jdbc.plugin.iam.IamAuthConnectionPlugin; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class ConnectionPluginChainBuilderTests { @Mock ConnectionProvider mockConnectionProvider; - @Mock CompleteServicesContainer mockServicesContainer; + @Mock FullServicesContainer mockServicesContainer; @Mock PluginService mockPluginService; @Mock PluginManagerService mockPluginManagerService; @Mock TelemetryFactory mockTelemetryFactory; diff --git a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java index 00ebb2cc8..66c1880e2 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java @@ -62,7 +62,7 @@ import software.amazon.jdbc.plugin.failover.FailoverConnectionPlugin; import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.profile.ConfigurationProfileBuilder; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.WrapperUtils; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -77,7 +77,7 @@ public class ConnectionPluginManagerTests { @Mock ConnectionWrapper mockConnectionWrapper; @Mock TelemetryFactory mockTelemetryFactory; @Mock TelemetryContext mockTelemetryContext; - @Mock CompleteServicesContainer mockServicesContainer; + @Mock FullServicesContainer mockServicesContainer; @Mock PluginService mockPluginService; @Mock PluginManagerService mockPluginManagerService; ConfigurationProfile configurationProfile = ConfigurationProfileBuilder.get().withName("test").build(); diff --git a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java index 5a999066b..7c3ff5a8a 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java @@ -51,7 +51,7 @@ import software.amazon.jdbc.dialect.RdsPgDialect; import software.amazon.jdbc.exceptions.ExceptionManager; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; public class DialectDetectionTests { private static final String LOCALHOST = "localhost"; @@ -63,7 +63,7 @@ public class DialectDetectionTests { private final DialectManager dialectManager = new DialectManager(null); private final Properties props = new Properties(); private AutoCloseable closeable; - @Mock private CompleteServicesContainer mockServicesContainer; + @Mock private FullServicesContainer mockServicesContainer; @Mock private HostListProvider mockHostListProvider; @Mock private Connection mockConnection; @Mock private Statement mockStatement; diff --git a/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java b/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java index 6d948c5a1..07a22941f 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/PluginServiceImplTests.java @@ -66,7 +66,7 @@ import software.amazon.jdbc.profile.ConfigurationProfileBuilder; import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.TestStorageServiceImpl; @@ -79,7 +79,7 @@ public class PluginServiceImplTests { private StorageService storageService; private AutoCloseable closeable; - @Mock CompleteServicesContainer servicesContainer; + @Mock FullServicesContainer servicesContainer; @Mock EventPublisher mockEventPublisher; @Mock ConnectionPluginManager pluginManager; @Mock Connection newConnection; diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java index c3b69120e..7b9ce95f7 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java @@ -64,7 +64,7 @@ import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider.FetchTopologyResult; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.TestStorageServiceImpl; @@ -77,7 +77,7 @@ class RdsHostListProviderTest { @Mock private Connection mockConnection; @Mock private Statement mockStatement; @Mock private ResultSet mockResultSet; - @Mock private CompleteServicesContainer mockServicesContainer; + @Mock private FullServicesContainer mockServicesContainer; @Mock private PluginService mockPluginService; @Mock private HostListProviderService mockHostListProviderService; @Mock private EventPublisher mockEventPublisher; diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java index 94c7536c2..e9dfaedf0 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java @@ -58,7 +58,7 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider.FetchTopologyResult; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.TestStorageServiceImpl; @@ -71,7 +71,7 @@ class RdsMultiAzDbClusterListProviderTest { @Mock private Connection mockConnection; @Mock private Statement mockStatement; @Mock private ResultSet mockResultSet; - @Mock private CompleteServicesContainer mockServicesContainer; + @Mock private FullServicesContainer mockServicesContainer; @Mock private PluginService mockPluginService; @Mock private HostListProviderService mockHostListProviderService; @Mock private EventPublisher mockEventPublisher; diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java index 914f23061..22a8339c1 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/AwsSecretsManagerConnectionPluginTest.java @@ -70,7 +70,7 @@ import software.amazon.jdbc.profile.ConfigurationProfileBuilder; import software.amazon.jdbc.states.SessionStateService; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Pair; import software.amazon.jdbc.util.telemetry.GaugeCallable; @@ -109,7 +109,7 @@ public class AwsSecretsManagerConnectionPluginTest { private AutoCloseable closeable; - @Mock CompleteServicesContainer mockServicesContainer; + @Mock FullServicesContainer mockServicesContainer; @Mock SecretsManagerClient mockSecretsManagerClient; @Mock GetSecretValueRequest mockGetValueRequest; @Mock JdbcCallable connectFunc; diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java index 631559949..58761bfdb 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java @@ -45,7 +45,7 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.hostavailability.HostAvailabilityStrategy; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; -import software.amazon.jdbc.util.CompleteServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -60,7 +60,7 @@ public class CustomEndpointPluginTest { private final HostSpec writerClusterHost = hostSpecBuilder.host(writerClusterUrl).build(); private final HostSpec host = hostSpecBuilder.host(customEndpointUrl).build(); - @Mock private CompleteServicesContainer mockServicesContainer; + @Mock private FullServicesContainer mockServicesContainer; @Mock private PluginService mockPluginService; @Mock private BiFunction mockRdsClientFunc; @Mock private TelemetryFactory mockTelemetryFactory; diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java index d4552296d..638e99a4f 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/dev/DeveloperConnectionPluginTest.java @@ -42,8 +42,8 @@ import software.amazon.jdbc.dialect.DialectCodes; import software.amazon.jdbc.dialect.DialectManager; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; -import software.amazon.jdbc.util.CompleteServicesContainer; -import software.amazon.jdbc.util.CompleteServicesContainerImpl; +import software.amazon.jdbc.util.FullServicesContainer; +import software.amazon.jdbc.util.FullServicesContainerImpl; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryContext; @@ -52,7 +52,7 @@ @SuppressWarnings({"resource"}) public class DeveloperConnectionPluginTest { - private CompleteServicesContainer servicesContainer; + private FullServicesContainer servicesContainer; @Mock StorageService mockStorageService; @Mock MonitorService mockMonitorService; @Mock ConnectionProvider mockConnectionProvider; @@ -73,7 +73,7 @@ void cleanUp() throws Exception { @BeforeEach void init() throws SQLException { closeable = MockitoAnnotations.openMocks(this); - servicesContainer = new CompleteServicesContainerImpl(mockStorageService, mockMonitorService, mockTelemetryFactory); + servicesContainer = new FullServicesContainerImpl(mockStorageService, mockMonitorService, mockTelemetryFactory); when(mockConnectionProvider.connect(any(), any(), any(), any(), any())).thenReturn(mockConnection); when(mockConnectCallback.getExceptionToRaise(any(), any(), any(), anyBoolean())).thenReturn(null); From 4b7c1d068b45d2a1fab629cbfe97187ec41aa55c Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 21 May 2025 09:55:38 -0700 Subject: [PATCH 129/149] Fix checkstyle --- wrapper/src/main/java/software/amazon/jdbc/Driver.java | 4 ++-- .../java/software/amazon/jdbc/ds/AwsWrapperDataSource.java | 4 ++-- .../amazon/jdbc/hostlistprovider/RdsHostListProvider.java | 2 +- .../java/software/amazon/jdbc/wrapper/ConnectionWrapper.java | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/Driver.java b/wrapper/src/main/java/software/amazon/jdbc/Driver.java index 3bf9ba422..9cd4a2ca0 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/Driver.java +++ b/wrapper/src/main/java/software/amazon/jdbc/Driver.java @@ -57,11 +57,11 @@ import software.amazon.jdbc.states.TransferSessionStateOnSwitchCallable; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialectManager; -import software.amazon.jdbc.util.FullServicesContainer; -import software.amazon.jdbc.util.FullServicesContainerImpl; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.CoreServicesContainer; import software.amazon.jdbc.util.DriverInfo; +import software.amazon.jdbc.util.FullServicesContainer; +import software.amazon.jdbc.util.FullServicesContainerImpl; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; diff --git a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java index 3ad78cf69..c932345df 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ds/AwsWrapperDataSource.java @@ -46,10 +46,10 @@ import software.amazon.jdbc.profile.DriverConfigurationProfiles; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialectManager; -import software.amazon.jdbc.util.FullServicesContainer; -import software.amazon.jdbc.util.FullServicesContainerImpl; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.CoreServicesContainer; +import software.amazon.jdbc.util.FullServicesContainer; +import software.amazon.jdbc.util.FullServicesContainerImpl; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.SqlState; diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index d898a5465..f72bab7fc 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -49,8 +49,8 @@ import software.amazon.jdbc.HostSpecBuilder; import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.hostavailability.HostAvailability; -import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.ConnectionUrlParser; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.RdsUrlType; import software.amazon.jdbc.util.RdsUtils; diff --git a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java index a9155009f..22db71063 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java +++ b/wrapper/src/main/java/software/amazon/jdbc/wrapper/ConnectionWrapper.java @@ -50,9 +50,9 @@ import software.amazon.jdbc.dialect.HostListProviderSupplier; import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.FullServicesContainerImpl; -import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.SqlState; import software.amazon.jdbc.util.StringUtils; From eda8c6c7b9592b109a7b649da528426860a2aa07 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 27 May 2025 16:06:25 -0700 Subject: [PATCH 130/149] Fix build issues --- .../hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java | 1 + .../util/storage/SlidingExpirationCacheWithCleanupThread.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index d9bc1f273..95ede85c7 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -48,6 +48,7 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.hostavailability.HostAvailability; +import software.amazon.jdbc.util.ExecutorFactory; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCacheWithCleanupThread.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCacheWithCleanupThread.java index e047eea9e..36c0fbfaa 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCacheWithCleanupThread.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/SlidingExpirationCacheWithCleanupThread.java @@ -17,10 +17,10 @@ package software.amazon.jdbc.util.storage; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; +import software.amazon.jdbc.util.ExecutorFactory; public class SlidingExpirationCacheWithCleanupThread extends SlidingExpirationCache { From cf9b924fa2b8d1aefbc169fc7077c2d1373c7850 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 27 May 2025 16:55:09 -0700 Subject: [PATCH 131/149] Use ExecutorFactory --- .../amazon/jdbc/util/ExecutorFactory.java | 5 +++++ .../jdbc/util/events/BatchingEventPublisher.java | 15 +++------------ .../jdbc/util/monitoring/AbstractMonitor.java | 12 ++---------- .../jdbc/util/monitoring/MonitorServiceImpl.java | 13 +++---------- .../jdbc/util/storage/StorageServiceImpl.java | 13 +++---------- 5 files changed, 16 insertions(+), 42 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/ExecutorFactory.java b/wrapper/src/main/java/software/amazon/jdbc/util/ExecutorFactory.java index a211fbdb4..26d6bb234 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/ExecutorFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/ExecutorFactory.java @@ -19,6 +19,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; @@ -37,6 +38,10 @@ public static ExecutorService newFixedThreadPool(int threadCount, String threadN return Executors.newFixedThreadPool(threadCount, getThreadFactory(threadName)); } + public static ScheduledExecutorService newSingleThreadScheduledThreadExecutor(String threadName) { + return Executors.newSingleThreadScheduledExecutor(getThreadFactory(threadName)); + } + private static ThreadFactory getThreadFactory(String threadName) { return THREAD_FACTORY_MAP.computeIfAbsent(threadName, ExecutorFactory::createThreadFactory); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java b/wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java index 2051e1e5b..a7f80832a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/events/BatchingEventPublisher.java @@ -22,10 +22,9 @@ import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import software.amazon.jdbc.util.StringUtils; +import software.amazon.jdbc.util.ExecutorFactory; /** * An event publisher that periodically publishes a batch of all unique events encountered during the latest time @@ -38,16 +37,8 @@ public class BatchingEventPublisher implements EventPublisher { // ConcurrentHashMap.newKeySet() is the recommended way to get a concurrent set. A set is used to prevent duplicate // event messages from being sent in the same message batch. protected final Set eventMessages = ConcurrentHashMap.newKeySet(); - protected static final ScheduledExecutorService publishingExecutor = Executors.newSingleThreadScheduledExecutor( - (r -> { - final Thread thread = new Thread(r); - thread.setDaemon(true); - if (!StringUtils.isNullOrEmpty(thread.getName())) { - thread.setName(thread.getName() + "-bep"); - } - return thread; - }) - ); + protected static final ScheduledExecutorService publishingExecutor = + ExecutorFactory.newSingleThreadScheduledThreadExecutor("bep"); public BatchingEventPublisher() { this(DEFAULT_MESSAGE_INTERVAL_NANOS); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index 4fc061f09..1d785e6b9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -17,13 +17,12 @@ package software.amazon.jdbc.util.monitoring; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Logger; import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; +import software.amazon.jdbc.util.ExecutorFactory; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.StringUtils; /** * An AbstractMonitor that implements common monitor logic. @@ -32,14 +31,7 @@ public abstract class AbstractMonitor implements Monitor, Runnable { private static final Logger LOGGER = Logger.getLogger(AbstractMonitor.class.getName()); protected final AtomicBoolean stop = new AtomicBoolean(false); protected final long terminationTimeoutSec; - protected final ExecutorService monitorExecutor = Executors.newSingleThreadExecutor(runnableTarget -> { - final Thread monitoringThread = new Thread(runnableTarget); - monitoringThread.setDaemon(true); - if (!StringUtils.isNullOrEmpty(monitoringThread.getName())) { - monitoringThread.setName(monitoringThread.getName() + "-" + getMonitorNameSuffix()); - } - return monitoringThread; - }); + protected final ExecutorService monitorExecutor = ExecutorFactory.newSingleThreadExecutor(getMonitorNameSuffix()); protected long lastActivityTimestampNanos; protected MonitorState state; diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 4a4eeb2c2..1ea706450 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -24,7 +24,6 @@ import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -39,9 +38,9 @@ import software.amazon.jdbc.hostlistprovider.monitoring.ClusterTopologyMonitorImpl; import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; +import software.amazon.jdbc.util.ExecutorFactory; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; -import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.connection.ConnectionServiceImpl; import software.amazon.jdbc.util.events.DataAccessEvent; import software.amazon.jdbc.util.events.Event; @@ -74,14 +73,8 @@ public class MonitorServiceImpl implements MonitorService, EventSubscriber { protected final EventPublisher publisher; protected final Map, CacheContainer> monitorCaches = new ConcurrentHashMap<>(); - protected final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { - final Thread thread = new Thread(r); - thread.setDaemon(true); - if (!StringUtils.isNullOrEmpty(thread.getName())) { - thread.setName(thread.getName() + "-msi"); - } - return thread; - })); + protected final ScheduledExecutorService cleanupExecutor = + ExecutorFactory.newSingleThreadScheduledThreadExecutor("msi"); public MonitorServiceImpl(EventPublisher publisher) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index 897583a0e..eac6baaa5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -20,15 +20,14 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.AllowedAndBlockedHosts; +import software.amazon.jdbc.util.ExecutorFactory; import software.amazon.jdbc.util.Messages; -import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.events.DataAccessEvent; import software.amazon.jdbc.util.events.EventPublisher; @@ -46,14 +45,8 @@ public class StorageServiceImpl implements StorageService { protected final EventPublisher publisher; protected final Map, ExpirationCache> caches = new ConcurrentHashMap<>(); - protected final ScheduledExecutorService cleanupExecutor = Executors.newSingleThreadScheduledExecutor((r -> { - final Thread thread = new Thread(r); - thread.setDaemon(true); - if (!StringUtils.isNullOrEmpty(thread.getName())) { - thread.setName(thread.getName() + "-ssi"); - } - return thread; - })); + protected final ScheduledExecutorService cleanupExecutor = + ExecutorFactory.newSingleThreadScheduledThreadExecutor("ssi"); public StorageServiceImpl(EventPublisher publisher) { this(DEFAULT_CLEANUP_INTERVAL_NANOS, publisher); From bac96297d2af2651b85080d8e4e2974dbc97fef0 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 27 May 2025 17:06:51 -0700 Subject: [PATCH 132/149] Add ExecutorService constructor to AbstractMonitor --- .../amazon/jdbc/util/monitoring/AbstractMonitor.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index 1d785e6b9..5eea7bfe5 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -31,13 +31,20 @@ public abstract class AbstractMonitor implements Monitor, Runnable { private static final Logger LOGGER = Logger.getLogger(AbstractMonitor.class.getName()); protected final AtomicBoolean stop = new AtomicBoolean(false); protected final long terminationTimeoutSec; - protected final ExecutorService monitorExecutor = ExecutorFactory.newSingleThreadExecutor(getMonitorNameSuffix()); + protected final ExecutorService monitorExecutor; protected long lastActivityTimestampNanos; protected MonitorState state; protected AbstractMonitor(long terminationTimeoutSec) { this.terminationTimeoutSec = terminationTimeoutSec; + this.monitorExecutor = ExecutorFactory.newSingleThreadExecutor(getMonitorNameSuffix()); + this.lastActivityTimestampNanos = System.nanoTime(); + } + + protected AbstractMonitor(long terminationTimeoutSec, ExecutorService monitorExecutor) { + this.terminationTimeoutSec = terminationTimeoutSec; + this.monitorExecutor = monitorExecutor; this.lastActivityTimestampNanos = System.nanoTime(); } From 2efaaf1dcab6e7e53d9ae01a1fa15a1edfbaf33c Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Tue, 27 May 2025 17:38:07 -0700 Subject: [PATCH 133/149] Remove support for unnecessary PartialPluginService calls --- .../amazon/jdbc/PartialPluginService.java | 123 ++---------------- .../ClusterTopologyMonitorImpl.java | 12 +- .../MonitoringRdsHostListProvider.java | 1 - .../MonitoringRdsMultiAzHostListProvider.java | 2 - .../MultiAzClusterTopologyMonitorImpl.java | 5 - .../CustomEndpointMonitorImpl.java | 1 - .../jdbc/plugin/efm2/HostMonitorImpl.java | 41 +----- 7 files changed, 17 insertions(+), 168 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java index 7393cd555..00533702d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java @@ -30,7 +30,6 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; import java.util.stream.Collectors; import org.checkerframework.checker.nullness.qual.NonNull; @@ -44,7 +43,6 @@ import software.amazon.jdbc.hostlistprovider.StaticHostListProvider; import software.amazon.jdbc.profile.ConfigurationProfile; import software.amazon.jdbc.states.SessionStateService; -import software.amazon.jdbc.states.SessionStateServiceImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.Messages; @@ -70,7 +68,6 @@ public class PartialPluginService implements PluginService, CanReleaseResources, protected final Properties props; protected volatile HostListProvider hostListProvider; protected List allHosts = new ArrayList<>(); - protected Connection currentConnection; protected HostSpec currentHostSpec; protected HostSpec initialConnectionHostSpec; protected boolean isInTransaction; @@ -83,10 +80,6 @@ public class PartialPluginService implements PluginService, CanReleaseResources, protected @Nullable final ConfigurationProfile configurationProfile; protected final ConnectionProviderManager connectionProviderManager; - protected final SessionStateService sessionStateService; - - protected final ReentrantLock connectionSwitchLock = new ReentrantLock(); - public PartialPluginService( @NonNull final FullServicesContainer servicesContainer, @NonNull final Properties props, @@ -102,7 +95,6 @@ public PartialPluginService( targetDriverProtocol, targetDriverDialect, dbDialect, - null, null); } @@ -114,8 +106,7 @@ public PartialPluginService( @NonNull final String targetDriverProtocol, @NonNull final TargetDriverDialect targetDriverDialect, @NonNull final Dialect dbDialect, - @Nullable final ConfigurationProfile configurationProfile, - @Nullable final SessionStateService sessionStateService) { + @Nullable final ConfigurationProfile configurationProfile) { this.servicesContainer = servicesContainer; this.pluginManager = servicesContainer.getConnectionPluginManager(); this.props = props; @@ -129,10 +120,6 @@ public PartialPluginService( this.pluginManager.getDefaultConnProvider(), this.pluginManager.getEffectiveConnProvider()); - this.sessionStateService = sessionStateService != null - ? sessionStateService - : new SessionStateServiceImpl(this, this.props); - this.exceptionHandler = this.configurationProfile != null && this.configurationProfile.getExceptionHandler() != null ? this.configurationProfile.getExceptionHandler() : null; @@ -140,7 +127,8 @@ public PartialPluginService( @Override public Connection getCurrentConnection() { - return this.currentConnection; + throw new UnsupportedOperationException( + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"getCurrentConnection"})); } @Override @@ -210,7 +198,8 @@ public HostSpec getHostSpecByStrategy(HostRole role, String strategy) throws SQL @Override public HostSpec getHostSpecByStrategy(List hosts, HostRole role, String strategy) throws SQLException { - return this.pluginManager.getHostSpecByStrategy(hosts, role, strategy); + throw new UnsupportedOperationException( + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"getHostSpecByStrategy"})); } @Override @@ -247,8 +236,8 @@ public String getDriverProtocol() { @Override public void setCurrentConnection( final @NonNull Connection connection, final @NonNull HostSpec hostSpec) throws SQLException { - - setCurrentConnection(connection, hostSpec, null); + throw new UnsupportedOperationException( + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"setCurrentConnection"})); } @Override @@ -257,95 +246,8 @@ public EnumSet setCurrentConnection( final @NonNull HostSpec hostSpec, @Nullable final ConnectionPlugin skipNotificationForThisPlugin) throws SQLException { - - connectionSwitchLock.lock(); - try { - - if (this.currentConnection == null) { - // setting up an initial connection - - this.currentConnection = connection; - this.currentHostSpec = hostSpec; - this.sessionStateService.reset(); - - final EnumSet changes = EnumSet.of(NodeChangeOptions.INITIAL_CONNECTION); - this.pluginManager.notifyConnectionChanged(changes, skipNotificationForThisPlugin); - - return changes; - - } else { - // update an existing connection - - final EnumSet changes = compare(this.currentConnection, this.currentHostSpec, - connection, hostSpec); - - if (!changes.isEmpty()) { - - final Connection oldConnection = this.currentConnection; - final boolean isInTransaction = this.isInTransaction; - this.sessionStateService.begin(); - - try { - this.currentConnection = connection; - this.currentHostSpec = hostSpec; - - this.sessionStateService.applyCurrentSessionState(connection); - this.setInTransaction(false); - - if (isInTransaction && PropertyDefinition.ROLLBACK_ON_SWITCH.getBoolean(this.props)) { - try { - oldConnection.rollback(); - } catch (final SQLException e) { - // Ignore any exception - } - } - - final EnumSet pluginOpinions = this.pluginManager.notifyConnectionChanged( - changes, skipNotificationForThisPlugin); - - final boolean shouldCloseConnection = - changes.contains(NodeChangeOptions.CONNECTION_OBJECT_CHANGED) - && !oldConnection.isClosed() - && !pluginOpinions.contains(OldConnectionSuggestedAction.PRESERVE); - - if (shouldCloseConnection) { - try { - this.sessionStateService.applyPristineSessionState(oldConnection); - } catch (final SQLException e) { - // Ignore any exception - } - - try { - oldConnection.close(); - } catch (final SQLException e) { - // Ignore any exception - } - } - } finally { - this.sessionStateService.complete(); - } - } - return changes; - } - } finally { - connectionSwitchLock.unlock(); - } - } - - protected EnumSet compare( - final @NonNull Connection connA, - final @NonNull HostSpec hostSpecA, - final @NonNull Connection connB, - final @NonNull HostSpec hostSpecB) { - - final EnumSet changes = EnumSet.noneOf(NodeChangeOptions.class); - - if (connA != connB) { - changes.add(NodeChangeOptions.CONNECTION_OBJECT_CHANGED); - } - - changes.addAll(compare(hostSpecA, hostSpecB)); - return changes; + throw new UnsupportedOperationException( + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"setCurrentConnection"})); } protected EnumSet compare( @@ -603,7 +505,8 @@ public Connection forceConnect( final HostSpec hostSpec, final Properties props) throws SQLException { - return this.forceConnect(hostSpec, props, null); + throw new UnsupportedOperationException( + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"forceConnect"})); } @Override @@ -612,8 +515,8 @@ public Connection forceConnect( final Properties props, final @Nullable ConnectionPlugin pluginToSkip) throws SQLException { - return this.pluginManager.forceConnect( - this.driverProtocol, hostSpec, props, this.currentConnection == null, pluginToSkip); + throw new UnsupportedOperationException( + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"forceConnect"})); } private void updateHostAvailability(final List hosts) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 95ede85c7..854e2902a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -45,7 +45,6 @@ import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.HostRole; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.PluginService; import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.ExecutorFactory; @@ -83,7 +82,6 @@ public class ClusterTopologyMonitorImpl extends AbstractMonitor implements Clust protected final long highRefreshRateNano; protected final Properties properties; protected final Properties monitoringProperties; - protected final PluginService pluginService; protected final HostSpec initialHostSpec; protected final StorageService storageService; protected final ConnectionService connectionService; @@ -116,7 +114,6 @@ public ClusterTopologyMonitorImpl( final ConnectionService connectionService, final HostSpec initialHostSpec, final Properties properties, - final PluginService pluginService, final HostListProviderService hostListProviderService, final HostSpec clusterInstanceTemplate, final long refreshRateNano, @@ -129,7 +126,6 @@ public ClusterTopologyMonitorImpl( this.clusterId = clusterId; this.storageService = storageService; this.connectionService = connectionService; - this.pluginService = pluginService; this.hostListProviderService = hostListProviderService; this.initialHostSpec = initialHostSpec; this.clusterInstanceTemplate = clusterInstanceTemplate; @@ -821,12 +817,10 @@ public void run() { try { connection = this.monitor.connectionService.open( hostSpec, this.monitor.monitoringProperties); - this.monitor.pluginService.setAvailability( - hostSpec.asAliases(), HostAvailability.AVAILABLE); } catch (SQLException ex) { - // connect issues - this.monitor.pluginService.setAvailability( - hostSpec.asAliases(), HostAvailability.NOT_AVAILABLE); + // A problem occurred while connecting. We will try again on the next iteration. + TimeUnit.MILLISECONDS.sleep(100); + continue; } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index 64e2c34d3..e80537b3a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -98,7 +98,6 @@ protected ClusterTopologyMonitor initMonitor() throws SQLException { connectionService, this.initialHostSpec, this.properties, - monitorPluginService, this.servicesContainer.getHostListProviderService(), this.clusterInstanceTemplate, this.refreshRateNano, diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java index ba55825b5..a1b314a75 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsMultiAzHostListProvider.java @@ -63,11 +63,9 @@ protected ClusterTopologyMonitor initMonitor() throws SQLException { (connectionService, pluginService) -> new MultiAzClusterTopologyMonitorImpl( this.clusterId, this.servicesContainer.getStorageService(), - this.servicesContainer.getMonitorService(), connectionService, this.initialHostSpec, this.properties, - pluginService, this.hostListProviderService, this.clusterInstanceTemplate, this.refreshRateNano, diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java index e8a0d937d..6e3c3b388 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MultiAzClusterTopologyMonitorImpl.java @@ -26,10 +26,8 @@ import java.util.logging.Logger; import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.connection.ConnectionService; -import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; public class MultiAzClusterTopologyMonitorImpl extends ClusterTopologyMonitorImpl { @@ -42,11 +40,9 @@ public class MultiAzClusterTopologyMonitorImpl extends ClusterTopologyMonitorImp public MultiAzClusterTopologyMonitorImpl( final String clusterId, final StorageService storageService, - final MonitorService monitorService, final ConnectionService connectionService, final HostSpec initialHostSpec, final Properties properties, - final PluginService pluginService, final HostListProviderService hostListProviderService, final HostSpec clusterInstanceTemplate, final long refreshRateNano, @@ -62,7 +58,6 @@ public MultiAzClusterTopologyMonitorImpl( connectionService, initialHostSpec, properties, - pluginService, hostListProviderService, clusterInstanceTemplate, refreshRateNano, diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index de0ce46db..0b31eb1fa 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -31,7 +31,6 @@ import software.amazon.awssdk.services.rds.model.Filter; import software.amazon.jdbc.AllowedAndBlockedHosts; import software.amazon.jdbc.HostSpec; -import software.amazon.jdbc.PluginService; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.monitoring.AbstractMonitor; import software.amazon.jdbc.util.storage.CacheMap; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java index e170f8c3e..055ce9d5c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java @@ -35,15 +35,12 @@ import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.hostavailability.HostAvailability; import software.amazon.jdbc.util.ExecutorFactory; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; -import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; -import software.amazon.jdbc.util.telemetry.TelemetryGauge; import software.amazon.jdbc.util.telemetry.TelemetryTraceLevel; /** @@ -80,10 +77,6 @@ public class HostMonitorImpl implements HostMonitor { private long failureCount; private boolean nodeUnhealthy = false; - - private final TelemetryGauge newContextsSizeGauge; - private final TelemetryGauge activeContextsSizeGauge; - private final TelemetryGauge nodeHealthyGauge; private final TelemetryCounter abortedConnectionsCounter; /** @@ -117,22 +110,6 @@ public HostMonitorImpl( this.failureDetectionCount = failureDetectionCount; this.abortedConnectionsCounter = abortedConnectionsCounter; - final String hostId = StringUtils.isNullOrEmpty(this.hostSpec.getHostId()) - ? this.hostSpec.getHost() - : this.hostSpec.getHostId(); - - this.newContextsSizeGauge = telemetryFactory.createGauge( - String.format("efm2.newContexts.size.%s", hostId), - this::getActiveContextSize); - - this.activeContextsSizeGauge = telemetryFactory.createGauge( - String.format("efm2.activeContexts.size.%s", hostId), - () -> (long) this.activeContexts.size()); - - this.nodeHealthyGauge = telemetryFactory.createGauge( - String.format("efm2.nodeHealthy.%s", hostId), - () -> this.nodeUnhealthy ? 0L : 1L); - this.threadPool.submit(this::newContextRun); // task to handle new contexts this.threadPool.submit(this); // task to handle active monitoring contexts this.threadPool.shutdown(); // No more tasks are accepted by pool. @@ -156,10 +133,6 @@ public void close() throws Exception { new Object[] {this.hostSpec.getHost()})); } - protected long getActiveContextSize() { - return this.newContexts.values().stream().mapToLong(java.util.Collection::size).sum(); - } - @Override public void startMonitoring(final HostMonitorConnectionContext context) { if (this.stopped.get()) { @@ -181,11 +154,6 @@ private long truncateNanoToSeconds(final long timeNano) { return TimeUnit.SECONDS.toNanos(TimeUnit.NANOSECONDS.toSeconds(timeNano)); } - public void clearContexts() { - this.newContexts.clear(); - this.activeContexts.clear(); - } - // This method helps to organize unit tests. long getCurrentTimeNano() { return System.nanoTime(); @@ -264,10 +232,6 @@ public void run() { this.updateNodeHealthStatus(isValid, statusCheckStartTimeNano, statusCheckEndTimeNano); - if (this.nodeUnhealthy) { - this.pluginService.setAvailability(this.hostSpec.asAliases(), HostAvailability.NOT_AVAILABLE); - } - final List> tmpActiveContexts = new ArrayList<>(); WeakReference monitorContextWeakRef; @@ -368,12 +332,9 @@ boolean checkConnectionStatus() { // Some drivers, like MySQL Connector/J, execute isValid() in a double of specified timeout time. final int validTimeout = (int) TimeUnit.NANOSECONDS.toSeconds( this.failureDetectionIntervalNano - THREAD_SLEEP_NANO) / 2; - final boolean isValid = this.monitoringConn.isValid(validTimeout); - return isValid; - + return this.monitoringConn.isValid(validTimeout); } catch (final SQLException sqlEx) { return false; - } finally { connectContext.closeContext(); } From 25c0f7df6739032dc2e1102e760da201feca29af Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 28 May 2025 14:29:13 -0700 Subject: [PATCH 134/149] Fix log message keys --- README.md | 2 +- .../efm/HostMonitorConnectionContext.java | 8 +- .../jdbc/plugin/efm/HostMonitorImpl.java | 14 +-- .../plugin/efm/HostMonitorServiceImpl.java | 2 +- .../efm/HostMonitorThreadContainer.java | 2 +- .../jdbc/plugin/efm2/HostMonitorImpl.java | 25 +++-- .../plugin/efm2/HostMonitorServiceImpl.java | 2 +- ..._advanced_jdbc_wrapper_messages.properties | 103 ++++-------------- 8 files changed, 48 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index 7d7434d1d..2ee423c1c 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ You can find our driver by searching in The Central Repository with GroupId and | `failureDetectionEnabled` | `HostMonitoringConnectionPlugin.FAILURE_DETECTION_ENABLED` | [HostMonitoringPlugin](./docs/using-the-jdbc-driver/using-plugins/UsingTheHostMonitoringPlugin.md) | | `failureDetectionInterval` | `HostMonitoringConnectionPlugin.FAILURE_DETECTION_INTERVAL` | [HostMonitoringPlugin](./docs/using-the-jdbc-driver/using-plugins/UsingTheHostMonitoringPlugin.md) | | `failureDetectionTime` | `HostMonitoringConnectionPlugin.FAILURE_DETECTION_TIME` | [HostMonitoringPlugin](./docs/using-the-jdbc-driver/using-plugins/UsingTheHostMonitoringPlugin.md) | -| `monitorDisposalTime` | `MonitorServiceImpl.MONITOR_DISPOSAL_TIME_MS` | [HostMonitoringPlugin](./docs/using-the-jdbc-driver/using-plugins/UsingTheHostMonitoringPlugin.md) | +| `monitorDisposalTime` | `HostMonitorServiceImpl.MONITOR_DISPOSAL_TIME_MS` | [HostMonitoringPlugin](./docs/using-the-jdbc-driver/using-plugins/UsingTheHostMonitoringPlugin.md) | | `iamDefaultPort` | `IamAuthConnectionPlugin.IAM_DEFAULT_PORT` | [IamAuthenticationPlugin](./docs/using-the-jdbc-driver/using-plugins/UsingTheIamAuthenticationPlugin.md) | | `iamHost` | `IamAuthConnectionPlugin.IAM_HOST` | [IamAuthenticationPlugin](./docs/using-the-jdbc-driver/using-plugins/UsingTheIamAuthenticationPlugin.md) | | `iamRegion` | `IamAuthConnectionPlugin.IAM_REGION` | [IamAuthenticationPlugin](./docs/using-the-jdbc-driver/using-plugins/UsingTheIamAuthenticationPlugin.md) | diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorConnectionContext.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorConnectionContext.java index aa452318c..7a5561ca1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorConnectionContext.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorConnectionContext.java @@ -155,7 +155,7 @@ void abortConnection() { // ignore LOGGER.finest( () -> Messages.get( - "MonitorConnectionContext.exceptionAbortingConnection", + "HostMonitorConnectionContext.exceptionAbortingConnection", new Object[] {sqlEx.getMessage()})); } } @@ -222,7 +222,7 @@ void setConnectionValid( * Math.max(0, this.getFailureDetectionCount()); if (invalidNodeDurationNano >= TimeUnit.MILLISECONDS.toNanos(maxInvalidNodeDurationMillis)) { - LOGGER.fine(() -> Messages.get("MonitorConnectionContext.hostDead", new Object[] {hostName})); + LOGGER.fine(() -> Messages.get("HostMonitorConnectionContext.hostDead", new Object[] {hostName})); this.setNodeUnhealthy(true); this.abortConnection(); return; @@ -230,7 +230,7 @@ void setConnectionValid( LOGGER.finest( () -> Messages.get( - "MonitorConnectionContext.hostNotResponding", + "HostMonitorConnectionContext.hostNotResponding", new Object[] {hostName, this.getFailureCount()})); return; } @@ -240,7 +240,7 @@ void setConnectionValid( this.setNodeUnhealthy(false); LOGGER.finest( - () -> Messages.get("MonitorConnectionContext.hostAlive", + () -> Messages.get("HostMonitorConnectionContext.hostAlive", new Object[] {hostName})); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorImpl.java index 64cc80306..eb83e38a9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorImpl.java @@ -114,7 +114,7 @@ public HostMonitorImpl( @Override public void startMonitoring(final HostMonitorConnectionContext context) { if (this.stopped) { - LOGGER.warning(() -> Messages.get("MonitorImpl.monitorIsStopped", new Object[] {this.hostSpec.getHost()})); + LOGGER.warning(() -> Messages.get("HostMonitorImpl.monitorIsStopped", new Object[] {this.hostSpec.getHost()})); } final long currentTimeNano = this.getCurrentTimeNano(); context.setStartMonitorTimeNano(currentTimeNano); @@ -125,7 +125,7 @@ public void startMonitoring(final HostMonitorConnectionContext context) { @Override public void stopMonitoring(final HostMonitorConnectionContext context) { if (context == null) { - LOGGER.warning(() -> Messages.get("MonitorImpl.contextNullWarning")); + LOGGER.warning(() -> Messages.get("HostMonitorImpl.contextNullWarning")); return; } @@ -142,7 +142,7 @@ public void clearContexts() { public void run() { LOGGER.finest(() -> Messages.get( - "MonitorImpl.startMonitoringThread", + "HostMonitorImpl.startMonitoringThread", new Object[]{this.hostSpec.getHost()})); try { @@ -261,7 +261,7 @@ public void run() { LOGGER.log( Level.FINEST, Messages.get( - "MonitorImpl.exceptionDuringMonitoringContinue", + "HostMonitorImpl.exceptionDuringMonitoringContinue", new Object[]{this.hostSpec.getHost()}), ex); // We want to print full trace stack of the exception. } @@ -271,7 +271,7 @@ public void run() { // exit thread LOGGER.warning( () -> Messages.get( - "MonitorImpl.interruptedExceptionDuringMonitoring", + "HostMonitorImpl.interruptedExceptionDuringMonitoring", new Object[] {this.hostSpec.getHost()})); } catch (final Exception ex) { // this should not be reached; log and exit thread @@ -279,7 +279,7 @@ public void run() { LOGGER.log( Level.FINEST, Messages.get( - "MonitorImpl.exceptionDuringMonitoringStop", + "HostMonitorImpl.exceptionDuringMonitoringStop", new Object[]{this.hostSpec.getHost()}), ex); // We want to print full trace stack of the exception. } @@ -296,7 +296,7 @@ public void run() { } LOGGER.finest(() -> Messages.get( - "MonitorImpl.stopMonitoringThread", + "HostMonitorImpl.stopMonitoringThread", new Object[]{this.hostSpec.getHost()})); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorServiceImpl.java index 55204ff7d..883179fc8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorServiceImpl.java @@ -91,7 +91,7 @@ public HostMonitorConnectionContext startMonitoring( if (nodeKeys.isEmpty()) { throw new IllegalArgumentException(Messages.get( - "MonitorServiceImpl.emptyAliasSet", + "HostMonitorServiceImpl.emptyAliasSet", new Object[] {hostSpec})); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorThreadContainer.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorThreadContainer.java index e23ec2b26..266e64630 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorThreadContainer.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorThreadContainer.java @@ -107,7 +107,7 @@ HostMonitor getMonitor(final String node) { HostMonitor getOrCreateMonitor(final Set nodeKeys, final Supplier monitorSupplier) { if (nodeKeys.isEmpty()) { - throw new IllegalArgumentException(Messages.get("MonitorThreadContainer.emptyNodeKeys")); + throw new IllegalArgumentException(Messages.get("HostMonitorThreadContainer.emptyNodeKeys")); } MONITOR_LOCK_OBJECT.lock(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java index 055ce9d5c..99e5399d8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java @@ -129,14 +129,14 @@ public void close() throws Exception { this.threadPool.shutdownNow(); } LOGGER.finest(() -> Messages.get( - "MonitorImpl.stopped", + "HostMonitorImpl.stopped", new Object[] {this.hostSpec.getHost()})); } @Override public void startMonitoring(final HostMonitorConnectionContext context) { if (this.stopped.get()) { - LOGGER.warning(() -> Messages.get("MonitorImpl.monitorIsStopped", new Object[] {this.hostSpec.getHost()})); + LOGGER.warning(() -> Messages.get("HostMonitorImpl.monitorIsStopped", new Object[] {this.hostSpec.getHost()})); } final long currentTimeNano = this.getCurrentTimeNano(); @@ -162,7 +162,7 @@ long getCurrentTimeNano() { public void newContextRun() { LOGGER.finest(() -> Messages.get( - "MonitorImpl.startMonitoringThreadNewContext", + "HostMonitorImpl.startMonitoringThreadNewContext", new Object[] {this.hostSpec.getHost()})); try { @@ -200,14 +200,14 @@ public void newContextRun() { LOGGER.log( Level.FINEST, Messages.get( - "MonitorImpl.exceptionDuringMonitoringStop", + "HostMonitorImpl.exceptionDuringMonitoringStop", new Object[] {this.hostSpec.getHost()}), ex); // We want to print full trace stack of the exception. } } LOGGER.finest(() -> Messages.get( - "MonitorImpl.stopMonitoringThreadNewContext", + "HostMonitorImpl.stopMonitoringThreadNewContext", new Object[] {this.hostSpec.getHost()})); } @@ -215,7 +215,7 @@ public void newContextRun() { public void run() { LOGGER.finest(() -> Messages.get( - "MonitorImpl.startMonitoringThread", + "HostMonitorImpl.startMonitoringThread", new Object[] {this.hostSpec.getHost()})); try { @@ -277,7 +277,7 @@ public void run() { LOGGER.log( Level.FINEST, Messages.get( - "MonitorImpl.exceptionDuringMonitoringStop", + "HostMonitorImpl.exceptionDuringMonitoringStop", new Object[] {this.hostSpec.getHost()}), ex); // We want to print full trace stack of the exception. } @@ -293,7 +293,7 @@ public void run() { } LOGGER.finest(() -> Messages.get( - "MonitorImpl.stopMonitoringThread", + "HostMonitorImpl.stopMonitoringThread", new Object[] {this.hostSpec.getHost()})); } @@ -357,14 +357,15 @@ private void updateNodeHealthStatus( this.failureDetectionIntervalNano * Math.max(0, this.failureDetectionCount - 1); if (invalidNodeDurationNano >= maxInvalidNodeDurationNano) { - LOGGER.fine(() -> Messages.get("MonitorConnectionContext.hostDead", new Object[] {this.hostSpec.getHost()})); + LOGGER.fine(() -> + Messages.get("HostMonitorConnectionContext.hostDead", new Object[] {this.hostSpec.getHost()})); this.nodeUnhealthy = true; return; } LOGGER.finest( () -> Messages.get( - "MonitorConnectionContext.hostNotResponding", + "HostMonitorConnectionContext.hostNotResponding", new Object[] {this.hostSpec.getHost(), this.failureCount})); return; } @@ -372,7 +373,7 @@ private void updateNodeHealthStatus( if (this.failureCount > 0) { // Node is back alive LOGGER.finest( - () -> Messages.get("MonitorConnectionContext.hostAlive", + () -> Messages.get("HostMonitorConnectionContext.hostAlive", new Object[] {this.hostSpec.getHost()})); } @@ -389,7 +390,7 @@ private void abortConnection(final @NonNull Connection connectionToAbort) { // ignore LOGGER.finest( () -> Messages.get( - "MonitorConnectionContext.exceptionAbortingConnection", + "HostMonitorConnectionContext.exceptionAbortingConnection", new Object[] {sqlEx.getMessage()})); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorServiceImpl.java index b61629db3..0f4a483fe 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorServiceImpl.java @@ -143,7 +143,7 @@ public void stopMonitoring( // ignore LOGGER.finest( () -> Messages.get( - "MonitorConnectionContext.exceptionAbortingConnection", + "HostMonitorConnectionContext.exceptionAbortingConnection", new Object[] {sqlEx.getMessage()})); } } else { diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 8b1c7686f..44370473d 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -20,7 +20,6 @@ AbstractMonitor.startingMonitor=Starting monitor: ''{0}''. AbstractMonitor.stoppingMonitor=Stopping monitor: ''{0}''. AbstractMonitor.unexpectedError=A monitor encountered an unexpected exception. Monitor: ''{0}''. Exception: ''{1}''. -# ADFS Credentials Provider Getter AdfsCredentialsProviderFactory.failedLogin=Failed login. Could not obtain SAML Assertion from ADFS SignOn Page POST response: \n''{0}'' AdfsCredentialsProviderFactory.invalidHttpsUrl=Invalid HTTPS URL: ''{0}'' AdfsCredentialsProviderFactory.signOnPagePostActionUrl=ADFS SignOn Action URL: ''{0}'' @@ -28,15 +27,10 @@ AdfsCredentialsProviderFactory.signOnPagePostActionRequestFailed=ADFS SignOn Pag AdfsCredentialsProviderFactory.signOnPageRequestFailed=ADFS SignOn Page Request Failed with HTTP status ''{0}'', reason phrase ''{1}'', and response ''{2}'' AdfsCredentialsProviderFactory.signOnPageUrl=ADFS SignOn URL: ''{0}'' -# Aurora Host List Connection Plugin -AuroraHostListConnectionPlugin.providerAlreadySet=Another dynamic host list provider has already been set: {0}. - -Authentication.unsupportedHostname=Unsupported AWS hostname {0}. Amazon domain name in format *.AWS-Region.rds.amazonaws.com or *.rds.AWS-Region.amazonaws.com.cn is expected. AuthenticationToken.useCachedToken=Use cached authentication token = ''{0}'' AuthenticationToken.generatedNewToken=Generated new authentication token = ''{0}'' AuthenticationToken.javaSdkNotInClasspath=Required dependency 'AWS Java SDK RDS v2.x' is not on the classpath. -# Aurora Host List Provider RdsHostListProvider.clusterInstanceHostPatternNotSupportedForRDSProxy=An RDS Proxy url can''t be used as the 'clusterInstanceHostPattern' configuration setting. RdsHostListProvider.clusterInstanceHostPatternNotSupportedForRdsCustom=A custom RDS url can''t be used as the 'clusterInstanceHostPattern' configuration setting. RdsHostListProvider.invalidPattern=Invalid value for the 'clusterInstanceHostPattern' configuration setting - the host pattern must contain a '?' character as a placeholder for the DB instance identifiers of the instances in the cluster. @@ -48,10 +42,8 @@ RdsHostListProvider.errorGettingHostRole=An error occurred while obtaining the c RdsHostListProvider.errorIdentifyConnection=An error occurred while obtaining the connection's host ID. RdsHostListProvider.errorGettingNetworkTimeout=An error occurred while getting the connection network timeout: {0} -# AWS SDK AwsSdk.unsupportedRegion=Unsupported AWS region ''{0}''. For supported regions please read https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.RegionsAndAvailabilityZones.html -# AWS Secrets Manager Connection Plugin AwsSecretsManagerConnectionPlugin.endpointOverrideMisconfigured=The provided endpoint is invalid and could not be used to create a URI: `{0}`. AwsSecretsManagerConnectionPlugin.endpointOverrideInvalidConnection=A connection to the provided endpoint could not be established: `{0}`. AwsSecretsManagerConnectionPlugin.javaSdkNotInClasspath=Required dependency 'AWS Java SDK for AWS Secrets Manager' is not on the classpath. @@ -60,12 +52,10 @@ AwsSecretsManagerConnectionPlugin.failedToFetchDbCredentials=Was not able to eit AwsSecretsManagerConnectionPlugin.missingRequiredConfigParameter=Configuration parameter ''{0}'' is required. AwsSecretsManagerConnectionPlugin.unhandledException=Unhandled exception: ''{0}'' -# AWS Wrapper Data Source AwsWrapperDataSource.missingJdbcProtocol=Missing JDBC protocol. Could not construct URL. AwsWrapperDataSource.missingTarget=JDBC url or Server name is required. AwsWrapperDataSource.configurationProfileNotFound=Configuration profile ''{0}'' not found. -# Cluster Aware Reader Failover Handler ClusterAwareReaderFailoverHandler.interruptedThread=Thread was interrupted. ClusterAwareReaderFailoverHandler.attemptingReaderConnection=Trying to connect to host: ''{0}'', with properties ''{1}'' ClusterAwareReaderFailoverHandler.readerRequired=Connected to host ''{0}'' but it has a host role of ''{1}'' and does not meet the strict-reader requirement. The connection will be closed. @@ -75,7 +65,6 @@ ClusterAwareReaderFailoverHandler.failedReaderConnection=Failed to connect to ho ClusterAwareReaderFailoverHandler.invalidTopology=''{0}'' was called with an invalid (null or empty) topology. ClusterAwareReaderFailoverHandler.timeout=Reader failover timed out after {0}ms. -# Cluster Aware Writer Failover Handler ClusterAwareWriterFailoverHandler.interruptedThread=Thread was interrupted. ClusterAwareWriterFailoverHandler.successfullyReconnectedToWriterInstance=Successfully re-connected to the current writer instance: ''{0}'' ClusterAwareWriterFailoverHandler.failedToConnectToWriterInstance=Failed to connect to the writer instance. @@ -88,48 +77,38 @@ ClusterAwareWriterFailoverHandler.taskBAttemptConnectionToNewWriterInstance=[Tas ClusterAwareWriterFailoverHandler.taskBFinished=[TaskB] Finished ClusterAwareWriterFailoverHandler.taskBConnectedToReader=[TaskB] Connected to reader: ''{0}'' ClusterAwareWriterFailoverHandler.taskBFailedToConnectToAnyReader=[TaskB] Failed to connect to any reader. -ClusterAwareWriterFailoverHandler.taskBTopologyObtained=[TaskB] Topology obtained: {0} ClusterAwareWriterFailoverHandler.taskBAttemptConnectionToNewWriter=[TaskB] Trying to connect to a new writer: ''{0}'' ClusterAwareWriterFailoverHandler.taskBEncounteredException=[TaskB] encountered an exception: {0} ClusterAwareWriterFailoverHandler.taskAEncounteredException=[TaskA] encountered an exception: {0} ClusterAwareWriterFailoverHandler.standaloneNode=[TaskB] Host {0} is not yet connected to a cluster. The cluster is still being reconfigured. ClusterAwareWriterFailoverHandler.alreadyWriter=Current reader connection is actually a new writer connection. -# Connection String Host List Provider ConnectionStringHostListProvider.parsedListEmpty=Can''t parse connection string: ''{0}''. ConnectionStringHostListProvider.unsupportedIdentifyConnection=ConnectionStringHostListProvider does not support identifyConnection. -# Connection Plugin Manager ConnectionPluginManager.releaseResources=Releasing resources. ConnectionPluginManager.unknownPluginCode=Unknown plugin code: ''{0}''. ConnectionPluginManager.unableToLoadPlugin=Unable to load connection plugin factory: ''{0}''. ConnectionPluginManager.invokedAgainstOldConnection=The internal connection has changed since ''{0}'' was created. This is likely due to failover or read-write splitting functionality. To ensure you are using the updated connection, please re-create Statement and ResultSet objects after failover and/or calling setReadOnly. -# Connection Provider ConnectionProvider.noConnection=The target driver did not return a connection. ConnectionProvider.unsupportedHostSpecSelectorStrategy=Unsupported host selection strategy ''{0}'' specified for this connection provider ''{1}''. Please visit the documentation for all supported strategies. -# Connection Url Builder ConnectionUrlBuilder.missingJdbcProtocol=Missing JDBC protocol and/or host name. Could not construct URL. -# Connection Url Parser ConnectionUrlParser.protocolNotFound=Url should contain a driver protocol. Protocol is not found in url: ''{0}'' -# Connect Time Connection Plugin ConnectTimeConnectionPlugin.connectTime=Connected in {0} nanos. -# Connection Wrapper ConnectionWrapper.unclosedConnectionInstantiated=Unclosed connection was instantiated at this point: ConnectionWrapper.connectionNotOpen=Initial connection isn't open. ConnectionWrapper.finalizingUnclosedConnection=Finalizing a connection that was never closed. -# Console Consumer ConsoleConsumer.unexpectedOutputType=Unexpected outputType: ''{0}''. CredentialsProviderFactory.failedToInitializeHttpClient=Failed to initialize HttpClient. CredentialsProviderFactory.unsupportedIdp=Unsupported Identity Provider ''{0}''. Please visit to the documentation for supported Identity Providers. -# Custom Endpoint Monitor Impl CustomEndpointMonitorImpl.clearCache=Clearing info in the custom endpoint monitor info cache. CustomEndpointMonitorImpl.detectedChangeInCustomEndpointInfo=Detected change in custom endpoint info for ''{0}'':\n{1} CustomEndpointMonitorImpl.exception=Encountered an exception while monitoring custom endpoint ''{0}''. @@ -138,7 +117,6 @@ CustomEndpointMonitorImpl.startingMonitor=Starting custom endpoint monitor for ' CustomEndpointMonitorImpl.stoppedMonitor=Stopped custom endpoint monitor for ''{0}''. CustomEndpointMonitorImpl.unexpectedNumberOfEndpoints=Unexpected number of custom endpoints with endpoint identifier ''{0}'' in region ''{1}''. Expected 1, but found {2}. Endpoints:\n{3}. -# Custom Endpoint Plugin CustomEndpointPlugin.timedOutWaitingForCustomEndpointInfo=The custom endpoint plugin timed out after {0}ms while waiting for custom endpoint info for host ''{1}''. CustomEndpointPlugin.connectionRequestToCustomEndpoint=Detected a connection request to a custom endpoint URL: ''{0}''. CustomEndpointPlugin.errorParsingEndpointIdentifier=Unable to parse custom endpoint identifier from URL: ''{0}''. @@ -146,18 +124,14 @@ CustomEndpointPlugin.interruptedThread=The custom endpoint plugin was interrupte CustomEndpointPlugin.unableToDetermineRegion=Unable to determine connection region. If you are using a non-standard RDS URL, please set the ''{0}'' property. CustomEndpointPlugin.waitingForCustomEndpointInfo=Custom endpoint info for ''{0}'' was not found. Waiting {1}ms for the endpoint monitor to fetch info... -# Custom Endpoint Plugin Factory CustomEndpointPluginFactory.awsSdkNotInClasspath=Required dependency 'AWS Java SDK RDS v2.x' is not on the classpath. -# Data Cache Connection Plugin DataCacheConnectionPlugin.queryResultsCached=[{0}] Query results will be cached: {1} -# Default Connection Plugin DefaultConnectionPlugin.executingMethod=Executing method: ''{0}'' DefaultConnectionPlugin.noHostsAvailable=The default connection plugin received an empty host list from the plugin service. DefaultConnectionPlugin.unknownRoleRequested=A HostSpec with a role of HostRole.UNKNOWN was requested via getHostSpecByStrategy. The requested role must be either HostRole.WRITER or HostRole.READER -# Driver Driver.nullUrl=Url is null. Driver.alreadyRegistered=Driver is already registered. It can only be registered once. Driver.missingDriver=Can''t find the target driver for ''{0}''. Please ensure the target driver is in the classpath and is registered. Here is the list of registered drivers in the classpath: {1} @@ -165,17 +139,14 @@ Driver.notRegistered=Driver is not registered (or it has not been registered usi Driver.urlParsingFailed=Url [{0}] parsing failed with error: [{1}] Driver.configurationProfileNotFound=Configuration profile ''{0}'' not found. -# DataSource DataSource.failedToSetProperty=Failed to set property ''{0}'' on target datasource ''{1}''. -# Execution Time Connection Plugin ExecutionTimeConnectionPlugin.executionTime=Executed {0} in {1} nanos. ExpirationCache.exceptionWhileRemovingEntry=An exception occurred while removing entry with key ''{0}'' and value ''{1}'': ''{2}''. ExternallyManagedCache.extendExpirationOnNonExistingKey=A request was made to extend the expiration of the entry at key ''{0}'', but the key does not exist. -# Failover Connection Plugin Failover.transactionResolutionUnknownError=Transaction resolution unknown. Please re-configure session state if required and try restarting the transaction. Failover.connectionClosedExplicitly=Unable to failover, the connection has been explicitly closed. Failover.connectionChangedError=The active SQL connection has changed due to a connection failure. Please re-configure session state if required. @@ -208,44 +179,32 @@ Failover.failedReaderConnection=[Reader Failover] Failed to connect to host: ''{ Failover.errorSelectingReaderHost=An error occurred while attempting to select a reader host candidate: ''{0}''. Candidates: Failover.skipFailoverOnInterruptedThread=Do not start failover since the current thread is interrupted. -# Federated Auth Plugin FederatedAuthPlugin.unableToDetermineRegion=Unable to determine connection region. If you are using a non-standard RDS URL, please set the ''{0}'' property. -# HikariPooledConnectionProvider -HikariPooledConnectionProvider.errorConnectingWithDataSource=Unable to connect to ''{0}'' using the Hikari data source. -HikariPooledConnectionProvider.errorConnectingWithDataSourceWithCause=Unable to connect to ''{0}'' using the Hikari data source. Exception message: ''{1}'' - -# Host Availability Strategy HostAvailabilityStrategy.invalidMaxRetries=Invalid value of {0} for configuration parameter `hostAvailabilityStrategyMaxRetries`. It must be an integer greater than 1. HostAvailabilityStrategy.invalidInitialBackoffTime=Invalid value of {0} for configuration parameter `hostAvailabilityStrategyInitialBackoffTime`. It must be an integer greater than 1. -# Host Monitoring Connection Plugin HostMonitoringConnectionPlugin.activatedMonitoring=Executing method ''{0}'', monitoring is activated. HostMonitoringConnectionPlugin.monitoringDeactivated=Monitoring deactivated for method ''{0}''. HostMonitoringConnectionPlugin.unavailableNode=Node ''{0}'' is unavailable. HostMonitoringConnectionPlugin.errorIdentifyingConnection=Error occurred while identifying connection: ''{0}''. HostMonitoringConnectionPlugin.unableToIdentifyConnection=Unable to identify the given connection: ''{0}'', please ensure the correct host list provider is specified. The host list provider in use is: ''{1}''. -# HostSelector HostSelector.noHostsMatchingRole=No hosts were found matching the requested ''{0}'' role. HostSelector.roundRobinInvalidHostWeightPairs=The provided host weight pairs have not been configured correctly. Please ensure the provided host weight pairs is a comma separated list of pairs, each pair in the format of :. Weight values must be an integer greater than or equal to the default weight value of 1. HostSelector.roundRobinInvalidDefaultWeight=The provided default weight value is not valid. Weight values must be an integer greater than or equal to the default weight value of 1. -# IAM Auth Connection Plugin IamAuthConnectionPlugin.unhandledException=Unhandled exception: ''{0}'' IamAuthConnectionPlugin.connectException=Error occurred while opening a connection: ''{0}'' IamAuthConnectionPlugin.unableToDetermineRegion=Unable to determine connection region. If you are using a non-standard RDS URL, please set the ''{0}'' property. PartialPluginService.unexpectedMethodCall=''LimitedPluginService#{0}()'' was unexpectedly called. Users of this class are not expected to call this method, so the method does not provide any functionality. -# Limitless Connection Plugin LimitlessConnectionPlugin.failedToConnectToHost=Failed to connect to host {0}. LimitlessConnectionPlugin.unsupportedDialectOrDatabase=Unsupported dialect ''{0}'' encountered. Please ensure JDBC connection parameters are correct, and refer to the documentation to ensure that the connecting database is compatible with the Limitless Connection Plugin. -# Limitless Query Helper LimitlessQueryHelper.unsupportedDialectOrDatabase=Unsupported dialect ''{0}'' encountered. Please ensure JDBC connection parameters are correct, and refer to the documentation to ensure that the connecting database is compatible with the Limitless Connection Plugin. -# Limitless Router Monitor LimitlessRouterMonitor.exceptionDuringMonitoringStop=Stopping monitoring after unhandled exception was thrown in Limitless Router Monitoring thread for node {0}. LimitlessRouterMonitor.interruptedExceptionDuringMonitoring=Limitless Router Monitoring thread for node {0} was interrupted. LimitlessRouterMonitor.invalidQuery=Limitless Connection Plugin has encountered an error obtaining Limitless Router endpoints. Please ensure that you are connecting to an Aurora Limitless Database Shard Group Endpoint URL. @@ -256,7 +215,6 @@ LimitlessRouterMonitor.openedConnection=Opened Limitless Router Monitor connecti LimitlessRouterMonitor.running=Limitless Router Monitor thread running on node {0}. LimitlessRouterMonitor.stopped=Limitless Router Monitor thread stopped on node {0}. -# Limitless Router Service LimitlessRouterServiceImpl.connectWithHost=Connecting to host {0}. LimitlessRouterServiceImpl.errorClosingMonitor=An error occurred while closing Limitless Router Monitor: {0} LimitlessRouterServiceImpl.errorStartingMonitor=An error occurred while starting Limitless Router Monitor: {0} @@ -274,34 +232,28 @@ LimitlessRouterServiceImpl.selectedHostForRetry=Host {0} has been selected for c LimitlessRouterServiceImpl.synchronouslyGetLimitlessRouters=Fetching Limitless Routers synchronously. LimitlessRouterServiceImpl.usingProvidedConnectUrl=Connecting using provided connection URL. -# Log Query Connection Plugin LogQueryConnectionPlugin.executingQuery=[{0}] Executing query: {1} -# Monitor Connection Context -MonitorConnectionContext.exceptionAbortingConnection=Exception during aborting connection: {0} -MonitorConnectionContext.hostDead=Host {0} is *dead*. -MonitorConnectionContext.hostNotResponding=Host {0} is not *responding* {1}. -MonitorConnectionContext.hostAlive=Host {0} is *alive*. - -# Monitor Thread Container -MonitorThreadContainer.emptyNodeKeys=Provided node keys are empty. - -# Monitor Impl -MonitorImpl.contextNullWarning=Parameter 'context' should not be null. -MonitorImpl.interruptedExceptionDuringMonitoring=Monitoring thread for node {0} was interrupted. -MonitorImpl.exceptionDuringMonitoringContinue=Continuing monitoring after unhandled exception was thrown in monitoring thread for node {0}. -MonitorImpl.exceptionDuringMonitoringStop=Stopping monitoring after unhandled exception was thrown in monitoring thread for node {0}. -MonitorImpl.monitorIsStopped=Monitoring was already stopped for node {0}. -MonitorImpl.stopped=Stopped monitoring thread for node ''{0}''. -MonitorImpl.startMonitoringThreadNewContext=Start monitoring thread for checking new contexts for {0}. -MonitorImpl.stopMonitoringThreadNewContext=Stop monitoring thread for checking new contexts for {0}. -MonitorImpl.startMonitoringThread=Start monitoring thread for {0}. -MonitorImpl.stopMonitoringThread=Stop monitoring thread for {0}. - -# efm.MonitorServiceImpl -MonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should not be empty. - -# monitoring.MonitorServiceImpl +HostMonitorConnectionContext.exceptionAbortingConnection=Exception during aborting connection: {0} +HostMonitorConnectionContext.hostDead=Host {0} is *dead*. +HostMonitorConnectionContext.hostNotResponding=Host {0} is not *responding* {1}. +HostMonitorConnectionContext.hostAlive=Host {0} is *alive*. + +HostMonitorThreadContainer.emptyNodeKeys=Provided node keys are empty. + +HostMonitorImpl.contextNullWarning=Parameter 'context' should not be null. +HostMonitorImpl.interruptedExceptionDuringMonitoring=Monitoring thread for node {0} was interrupted. +HostMonitorImpl.exceptionDuringMonitoringContinue=Continuing monitoring after unhandled exception was thrown in monitoring thread for node {0}. +HostMonitorImpl.exceptionDuringMonitoringStop=Stopping monitoring after unhandled exception was thrown in monitoring thread for node {0}. +HostMonitorImpl.monitorIsStopped=Monitoring was already stopped for node {0}. +HostMonitorImpl.stopped=Stopped monitoring thread for node ''{0}''. +HostMonitorImpl.startMonitoringThreadNewContext=Start monitoring thread for checking new contexts for {0}. +HostMonitorImpl.stopMonitoringThreadNewContext=Stop monitoring thread for checking new contexts for {0}. +HostMonitorImpl.startMonitoringThread=Start monitoring thread for {0}. +HostMonitorImpl.stopMonitoringThread=Stop monitoring thread for {0}. + +HostMonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set should not be empty. + MonitorServiceImpl.checkingMonitors=Checking monitors for errors... MonitorServiceImpl.monitorClassMismatch=The monitor stored at ''{0}'' did not have the expected type. The expected type was ''{1}'', but the monitor ''{2}'' had a type of ''{3}''. MonitorServiceImpl.monitorStuck=Monitor ''{0}'' has not been updated within the inactive timeout of {1} milliseconds. @@ -326,7 +278,6 @@ OktaCredentialsProviderFactory.unableToOpenHttpClient=Unable to open an HTTP cli OktaCredentialsProviderFactory.invalidSamlResponse=The SAML Assertion request did not return a valid response containing a SAMLResponse. OktaCredentialsProviderFactory.samlRequestFailed=Okta SAML Assertion request failed with HTTP status ''{0}'', reason phrase ''{1}'', and response ''{2}'' -# Plugin Service Impl PluginServiceImpl.currentHostNotAllowed=The current host is not in the list of allowed hosts. Current host: ''{0}''. Allowed hosts: {1} PluginServiceImpl.hostListEmpty=Current host list is empty. PluginServiceImpl.releaseResources=Releasing resources. @@ -336,12 +287,10 @@ PluginServiceImpl.failedToRetrieveHostPort=Could not retrieve Host:Port for conn PluginServiceImpl.nonEmptyAliases=fillAliases called when HostSpec already contains the following aliases: ''{0}''. PluginServiceImpl.requiredBlockingHostListProvider=The detected host list provider is not a BlockingHostListProvider. A BlockingHostListProvider is required to force refresh the host list. Detected host list provider: {0} -# Property Utils PropertyUtils.setMethodDoesNotExistOnTarget=Set method for property ''{0}'' does not exist on target ''{1}''. PropertyUtils.failedToSetProperty=Failed to set property ''{0}'' on target ''{1}''. PropertyUtils.failedToSetPropertyWithReason=Failed to set property ''{0}'' on target ''{1}''. {2} -# Read Write Splitting Plugin ReadWriteSplittingPlugin.setReadOnlyOnClosedConnection=setReadOnly cannot be called on a closed connection. ReadWriteSplittingPlugin.errorSwitchingToCachedReader=An error occurred while trying to switch to a cached reader connection: ''{0}''. The driver will attempt to establish a new reader connection. ReadWriteSplittingPlugin.errorSwitchingToCachedReaderWithCause=An error occurred while trying to switch to a cached reader connection: ''{0}''. Error message: ''{1}''. The driver will attempt to establish a new reader connection. @@ -378,12 +327,9 @@ StorageServiceImpl.itemClassNotRegistered=The given item class ''{0}'' is not re StorageServiceImpl.itemClassMismatch=The item stored at ''{0}'' did not have the expected type. The expected type was ''{1}'', but the stored item ''{2}'' had a type of ''{3}''. Returning null. StorageServiceImpl.removeExpiredItems=Removing expired items from the storage service... -# Wrapper Utils WrapperUtils.noWrapperClassExists=No wrapper class exists for ''{0}''. WrapperUtils.failedToInitializeClass=Can''t initialize class ''{0}''. -# Aurora Stale DNS -AuroraStaleDnsPlugin.requireDynamicProvider=Dynamic host list provider is required. AuroraStaleDnsHelper.clusterEndpointDns=Cluster endpoint resolves to {0}. AuroraStaleDnsHelper.currentWriterNotAllowed=The current writer is not in the list of allowed hosts. Current host: ''{0}''. Allowed hosts: {1} AuroraStaleDnsHelper.writerHostSpec=Writer host: {0} @@ -391,17 +337,13 @@ AuroraStaleDnsHelper.writerInetAddress=Writer host address: {0} AuroraStaleDnsHelper.staleDnsDetected=Stale DNS data detected. Opening a connection to ''{0}''. AuroraStaleDnsHelper.reset=Reset stored writer host. -# Opened Connection Tracker OpenedConnectionTracker.invalidatingConnections=Invalidating opened connections to host: ''{0}'' -# Util Utils.topology={0} \n{1} -# Dialect Manager DialectManager.unknownDialectCode=Unknown dialect code: ''{0}''. DialectManager.unknownDialect=Database dialect can''t be identified. Use configuration parameter ''wrapperDialect'' to configure it. -# Target Driver Dialect Manager TargetDriverDialectManager.unknownDialectCode=Unknown target driver dialect code: ''{0}''. TargetDriverDialectManager.unknownProtocol=Can not find a driver to register for protocol ''{0}''. TargetDriverDialectManager.customDialectNotSupported=Provided custom target driver dialect will be ignored. @@ -411,11 +353,9 @@ TargetDriverDialect.unsupported=This target driver dialect does not support this MysqlConnectorJDriverHelper.canNotRegister=Can''t register driver com.mysql.cj.jdbc.Driver. MariadbDriverHelper.canNotRegister=Can''t register driver org.mariadb.jdbc.Driver. -# Aurora Initial Connection Strategy Plugin AuroraInitialConnectionStrategyPlugin.unsupportedStrategy=Unsupported host selection strategy ''{0}''. AuroraInitialConnectionStrategyPlugin.requireDynamicProvider=Dynamic host list provider is required. -# Fastest Response Time Strategy Plugin NodeResponseTimeMonitor.stopped=Stopped Response time thread for node ''{0}''. NodeResponseTimeMonitor.responseTime=Response time for ''{0}'': {1} ms NodeResponseTimeMonitor.interruptedExceptionDuringMonitoring=Response time thread for node {0} was interrupted. @@ -423,12 +363,9 @@ NodeResponseTimeMonitor.exceptionDuringMonitoringStop=Stopping thread after unha NodeResponseTimeMonitor.openingConnection=Opening a Response time connection to ''{0}''. NodeResponseTimeMonitor.openedConnection=Opened Response time connection: {0}. -# Monitoring RDS HostList Provider -ClusterTopologyMonitorImpl.awaitTerminationTimeout=Awaiting termination of the monitor executor timed out after {0} {1}. ClusterTopologyMonitorImpl.startMonitoringThread=Start cluster topology monitoring thread for ''{0}''. ClusterTopologyMonitorImpl.stopMonitoringThread=Stop cluster topology monitoring thread for ''{0}''. ClusterTopologyMonitorImpl.exceptionDuringMonitoringStop=Stopping cluster topology monitoring after unhandled exception was thrown in monitoring thread for node ''{0}''. -ClusterTopologyMonitorImpl.interruptedWhileTerminating=An InterruptedException occurred while terminating or shutting down the monitor executor. ClusterTopologyMonitorImpl.invalidQuery=An error occurred while attempting to obtain the topology because the topology query was invalid. Please ensure you are connecting to an Aurora or RDS Db cluster. ClusterTopologyMonitorImpl.errorGettingNetworkTimeout=An error occurred while getting the connection network timeout: {0} ClusterTopologyMonitorImpl.invalidTopology=The topology query returned an invalid topology - no writer instance detected. From fc4b7822dc8283a71e51acfd49908074c24216ac Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 28 May 2025 15:27:09 -0700 Subject: [PATCH 135/149] Make AbstractMonitor variables Atomic --- .../ClusterTopologyMonitorImpl.java | 2 +- .../CustomEndpointMonitorImpl.java | 2 +- .../jdbc/util/monitoring/AbstractMonitor.java | 30 ++++++++++--------- .../monitoring/MonitorServiceImplTest.java | 2 +- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 854e2902a..6718bead2 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -284,7 +284,7 @@ public void monitor() { new Object[]{this.initialHostSpec.getHost()})); while (!this.stop.get() && !Thread.currentThread().isInterrupted()) { - this.lastActivityTimestampNanos = System.nanoTime(); + this.lastActivityTimestampNanos.set(System.nanoTime()); if (this.isInPanicMode()) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java index 0b31eb1fa..463ab118b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointMonitorImpl.java @@ -107,7 +107,7 @@ public void monitor() { while (!this.stop.get() && !Thread.currentThread().isInterrupted()) { try { long start = System.nanoTime(); - this.lastActivityTimestampNanos = System.nanoTime(); + this.lastActivityTimestampNanos.set(System.nanoTime()); final Filter customEndpointFilter = Filter.builder().name("db-cluster-endpoint-type").values("custom").build(); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java index 5eea7bfe5..ca7abcec9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/AbstractMonitor.java @@ -19,6 +19,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import java.util.logging.Logger; import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; import software.amazon.jdbc.util.ExecutorFactory; @@ -30,22 +32,22 @@ public abstract class AbstractMonitor implements Monitor, Runnable { private static final Logger LOGGER = Logger.getLogger(AbstractMonitor.class.getName()); protected final AtomicBoolean stop = new AtomicBoolean(false); - protected final long terminationTimeoutSec; protected final ExecutorService monitorExecutor; + protected final AtomicLong terminationTimeoutSec = new AtomicLong(); + protected final AtomicLong lastActivityTimestampNanos = new AtomicLong(); + protected final AtomicReference state = new AtomicReference<>(); - protected long lastActivityTimestampNanos; - protected MonitorState state; protected AbstractMonitor(long terminationTimeoutSec) { - this.terminationTimeoutSec = terminationTimeoutSec; + this.terminationTimeoutSec.set(terminationTimeoutSec); this.monitorExecutor = ExecutorFactory.newSingleThreadExecutor(getMonitorNameSuffix()); - this.lastActivityTimestampNanos = System.nanoTime(); + this.lastActivityTimestampNanos.set(System.nanoTime()); } protected AbstractMonitor(long terminationTimeoutSec, ExecutorService monitorExecutor) { - this.terminationTimeoutSec = terminationTimeoutSec; + this.terminationTimeoutSec.set(terminationTimeoutSec); this.monitorExecutor = monitorExecutor; - this.lastActivityTimestampNanos = System.nanoTime(); + this.lastActivityTimestampNanos.set(System.nanoTime()); } @Override @@ -63,12 +65,12 @@ public void start() { public void run() { try { LOGGER.finest(Messages.get("AbstractMonitor.startingMonitor", new Object[] {this})); - this.state = MonitorState.RUNNING; - this.lastActivityTimestampNanos = System.nanoTime(); + this.state.set(MonitorState.RUNNING); + this.lastActivityTimestampNanos.set(System.nanoTime()); monitor(); } catch (Exception e) { LOGGER.fine(Messages.get("AbstractMonitor.unexpectedError", new Object[] {this, e})); - this.state = MonitorState.ERROR; + this.state.set(MonitorState.ERROR); } } @@ -78,7 +80,7 @@ public void stop() { this.stop.set(true); try { - if (!this.monitorExecutor.awaitTermination(this.terminationTimeoutSec, TimeUnit.SECONDS)) { + if (!this.monitorExecutor.awaitTermination(this.terminationTimeoutSec.get(), TimeUnit.SECONDS)) { LOGGER.info(Messages.get( "AbstractMonitor.monitorTerminationTimeout", new Object[] {terminationTimeoutSec, this})); this.monitorExecutor.shutdownNow(); @@ -89,7 +91,7 @@ public void stop() { this.monitorExecutor.shutdownNow(); } finally { close(); - this.state = MonitorState.STOPPED; + this.state.set(MonitorState.STOPPED); } } @@ -100,12 +102,12 @@ public void close() { @Override public long getLastActivityTimestampNanos() { - return this.lastActivityTimestampNanos; + return this.lastActivityTimestampNanos.get(); } @Override public MonitorState getState() { - return this.state; + return this.state.get(); } @Override diff --git a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java index cfe8d714c..cd0bcbe3d 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/util/monitoring/MonitorServiceImplTest.java @@ -95,7 +95,7 @@ public void testMonitorError_monitorReCreated() throws SQLException, Interrupted TimeUnit.MILLISECONDS.sleep(250); assertEquals(MonitorState.RUNNING, monitor.getState()); - monitor.state = MonitorState.ERROR; + monitor.state.set(MonitorState.ERROR); monitorService.checkMonitors(); assertEquals(MonitorState.STOPPED, monitor.getState()); From d56122dab76abfd5e2e341a5866b019c13ec9564 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 28 May 2025 16:04:48 -0700 Subject: [PATCH 136/149] Make monitorErrorResponses in MonitorSettings Nullable --- .../amazon/jdbc/util/monitoring/MonitorServiceImpl.java | 2 +- .../amazon/jdbc/util/monitoring/MonitorSettings.java | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 1ea706450..e4883139a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -152,7 +152,7 @@ protected void handleMonitorError( monitor.stop(); Set errorResponses = cacheContainer.getSettings().getErrorResponses(); - if (errorResponses.contains(MonitorErrorResponse.RECREATE)) { + if (errorResponses != null && errorResponses.contains(MonitorErrorResponse.RECREATE)) { cacheContainer.getCache().computeIfAbsent(key, k -> { LOGGER.fine(Messages.get("MonitorServiceImpl.recreatingMonitor", new Object[] {monitor})); MonitorItem newMonitorItem = new MonitorItem(errorMonitorItem.getMonitorSupplier()); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java index 00d5c9e3e..6774058e8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorSettings.java @@ -18,6 +18,7 @@ import java.util.Set; import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; /** * A class defining settings for a monitor or monitor type. @@ -25,7 +26,7 @@ public class MonitorSettings { private final long expirationTimeoutNanos; private final long inactiveTimeoutNanos; - private @NonNull final Set errorResponses; + private @Nullable final Set errorResponses; /** * Constructs a MonitorSettings instance. @@ -35,7 +36,8 @@ public class MonitorSettings { * @param inactiveTimeoutNanos a duration in nanoseconds defining the maximum amount of time that a monitor should * take between updating its last-updated timestamp. If a monitor has not updated its * last-updated timestamp within this duration it will be considered stuck. - * @param errorResponses a {@link Set} defining actions to take if the monitor is in an error state. + * @param errorResponses a {@link Set} defining actions to take if the monitor is in an error state. If null, + * no action will be performed. */ public MonitorSettings( long expirationTimeoutNanos, long inactiveTimeoutNanos, @NonNull Set errorResponses) { @@ -52,7 +54,7 @@ public long getInactiveTimeoutNanos() { return inactiveTimeoutNanos; } - public @NonNull Set getErrorResponses() { + public @Nullable Set getErrorResponses() { return errorResponses; } } From e340c6172b960d0f4f7ab3bb77c870bd39872326 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 28 May 2025 16:12:50 -0700 Subject: [PATCH 137/149] Update docs for Monitor.monitor() --- .../java/software/amazon/jdbc/util/monitoring/Monitor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java index b9b47a018..d4d89dc4c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/Monitor.java @@ -24,7 +24,8 @@ public interface Monitor { /** * Executes the monitoring loop for this monitor. This method should be called in the run() method of the thread - * submitted during the call to {@link #start()}. + * submitted during the call to {@link #start()}. Additionally, the monitoring loop should regularly update the last + * activity timestamp so that the {@link MonitorService} can detect whether the monitor is stuck or not. */ void monitor(); From 591e50169a708f0809e948ef352ca6af788d2c05 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 28 May 2025 17:16:06 -0700 Subject: [PATCH 138/149] Update PartialPluginService UnsupportedOperation message --- .../resources/aws_advanced_jdbc_wrapper_messages.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 44370473d..9e9c1c502 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -198,7 +198,7 @@ IamAuthConnectionPlugin.unhandledException=Unhandled exception: ''{0}'' IamAuthConnectionPlugin.connectException=Error occurred while opening a connection: ''{0}'' IamAuthConnectionPlugin.unableToDetermineRegion=Unable to determine connection region. If you are using a non-standard RDS URL, please set the ''{0}'' property. -PartialPluginService.unexpectedMethodCall=''LimitedPluginService#{0}()'' was unexpectedly called. Users of this class are not expected to call this method, so the method does not provide any functionality. +PartialPluginService.unexpectedMethodCall=Calling ''{0}()'' is not supported with this PluginService. LimitlessConnectionPlugin.failedToConnectToHost=Failed to connect to host {0}. LimitlessConnectionPlugin.unsupportedDialectOrDatabase=Unsupported dialect ''{0}'' encountered. Please ensure JDBC connection parameters are correct, and refer to the documentation to ensure that the connecting database is compatible with the Limitless Connection Plugin. From 787ed0fa6da9f9e594bf74441a8338267fe697d8 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 28 May 2025 17:42:02 -0700 Subject: [PATCH 139/149] Adjust monitor stuck message --- .../java/software/amazon/jdbc/plugin/efm/HostMonitorImpl.java | 2 +- .../java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java | 2 +- .../amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java | 2 +- .../strategy/fastestresponse/NodeResponseTimeMonitor.java | 2 +- .../resources/aws_advanced_jdbc_wrapper_messages.properties | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorImpl.java index eb83e38a9..d256c52a1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm/HostMonitorImpl.java @@ -332,7 +332,7 @@ ConnectionStatus checkConnectionStatus(final long shortestFailureDetectionInterv LOGGER.finest(() -> "Opening a monitoring connection to " + this.hostSpec.getUrl()); startNano = this.getCurrentTimeNano(); - // TODO: replace with ConnectionService#createAuxiliaryConnection + // TODO: replace with ConnectionService#open this.monitoringConn = this.pluginService.forceConnect(this.hostSpec, monitoringConnProperties); LOGGER.finest(() -> "Opened monitoring connection: " + this.monitoringConn); return new ConnectionStatus(true, this.getCurrentTimeNano() - startNano); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java index 99e5399d8..c2768844a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/efm2/HostMonitorImpl.java @@ -323,7 +323,7 @@ boolean checkConnectionStatus() { }); LOGGER.finest(() -> "Opening a monitoring connection to " + this.hostSpec.getUrl()); - // TODO: replace with ConnectionService#createAuxiliaryConnection + // TODO: replace with ConnectionService#open this.monitoringConn = this.pluginService.forceConnect(this.hostSpec, monitoringConnProperties); LOGGER.finest(() -> "Opened monitoring connection: " + this.monitoringConn); return true; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java index 097f2f0fb..304f9d663 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/limitless/LimitlessRouterMonitor.java @@ -193,7 +193,7 @@ private void openConnection() throws SQLException { LOGGER.finest(() -> Messages.get( "LimitlessRouterMonitor.openingConnection", new Object[] {this.hostSpec.getUrl()})); - // TODO: replace with ConnectionService#createAuxiliaryConnection + // TODO: replace with ConnectionService#open this.monitoringConn = this.pluginService.forceConnect(this.hostSpec, this.props); LOGGER.finest(() -> Messages.get( "LimitlessRouterMonitor.openedConnection", diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java index daef79230..0e26a6131 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/strategy/fastestresponse/NodeResponseTimeMonitor.java @@ -214,7 +214,7 @@ private void openConnection() { LOGGER.finest(() -> Messages.get( "NodeResponseTimeMonitor.openingConnection", new Object[] {this.hostSpec.getUrl()})); - // TODO: replace with ConnectionService#createAuxiliaryConnection + // TODO: replace with ConnectionService#open this.monitoringConn = this.pluginService.forceConnect(this.hostSpec, monitoringConnProperties); LOGGER.finest(() -> Messages.get( "NodeResponseTimeMonitor.openedConnection", diff --git a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties index 9e9c1c502..00c2106be 100644 --- a/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties +++ b/wrapper/src/main/resources/aws_advanced_jdbc_wrapper_messages.properties @@ -256,7 +256,7 @@ HostMonitorServiceImpl.emptyAliasSet=Empty alias set passed for ''{0}''. Set sho MonitorServiceImpl.checkingMonitors=Checking monitors for errors... MonitorServiceImpl.monitorClassMismatch=The monitor stored at ''{0}'' did not have the expected type. The expected type was ''{1}'', but the monitor ''{2}'' had a type of ''{3}''. -MonitorServiceImpl.monitorStuck=Monitor ''{0}'' has not been updated within the inactive timeout of {1} milliseconds. +MonitorServiceImpl.monitorStuck=Monitor ''{0}'' has not been updated within the inactive timeout of {1} milliseconds. The monitor will be stopped. MonitorServiceImpl.monitorTypeNotRegistered=The given monitor class ''{0}'' is not registered. Please register the monitor class before running monitors of that class with the monitor service. MonitorServiceImpl.removedExpiredMonitor=Removed expired monitor: ''{0}''. MonitorServiceImpl.removedErrorMonitor=Removed monitor in error state: ''{0}''. From aa6778aa3e370186d3b2658517f74afce6db0fe3 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 29 May 2025 15:02:37 -0700 Subject: [PATCH 140/149] Change default monitor expiration timeout, add Topology equals/hashcode --- .../hostlistprovider/RdsHostListProvider.java | 1 - .../Topology.java | 22 ++++++++++++++++++- .../ClusterTopologyMonitorImpl.java | 2 +- .../MonitoringRdsHostListProvider.java | 2 +- .../util/monitoring/MonitorServiceImpl.java | 8 +++---- .../jdbc/util/storage/StorageServiceImpl.java | 1 + .../RdsHostListProviderTest.java | 1 - .../RdsMultiAzDbClusterListProviderTest.java | 1 - 8 files changed, 27 insertions(+), 11 deletions(-) rename wrapper/src/main/java/software/amazon/jdbc/{util/storage => hostlistprovider}/Topology.java (70%) diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java index f72bab7fc..06334733e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/RdsHostListProvider.java @@ -58,7 +58,6 @@ import software.amazon.jdbc.util.SynchronousExecutor; import software.amazon.jdbc.util.Utils; import software.amazon.jdbc.util.storage.CacheMap; -import software.amazon.jdbc.util.storage.Topology; public class RdsHostListProvider implements DynamicHostListProvider { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/Topology.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/Topology.java similarity index 70% rename from wrapper/src/main/java/software/amazon/jdbc/util/storage/Topology.java rename to wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/Topology.java index bdeec0792..c72d8f7aa 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/Topology.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/Topology.java @@ -14,9 +14,10 @@ * limitations under the License. */ -package software.amazon.jdbc.util.storage; +package software.amazon.jdbc.hostlistprovider; import java.util.List; +import java.util.Objects; import org.checkerframework.checker.nullness.qual.NonNull; import software.amazon.jdbc.HostSpec; @@ -30,4 +31,23 @@ public Topology(@NonNull List hosts) { public @NonNull List getHosts() { return hosts; } + + @Override + public int hashCode() { + return Objects.hash(hosts); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null || getClass() != obj.getClass()) { + return false; + } + + Topology other = (Topology) obj; + return Objects.equals(hosts, other.hosts); + } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index 6718bead2..c1ab92e7f 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -57,7 +57,7 @@ import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.AbstractMonitor; import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.util.storage.Topology; +import software.amazon.jdbc.hostlistprovider.Topology; public class ClusterTopologyMonitorImpl extends AbstractMonitor implements ClusterTopologyMonitor { diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index e80537b3a..ce1b5f28a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -34,7 +34,7 @@ import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.util.storage.Topology; +import software.amazon.jdbc.hostlistprovider.Topology; public class MonitoringRdsHostListProvider extends RdsHostListProvider implements BlockingHostListProvider, CanReleaseResources { diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index e4883139a..4060d11df 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -48,7 +48,7 @@ import software.amazon.jdbc.util.events.EventSubscriber; import software.amazon.jdbc.util.storage.ExternallyManagedCache; import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.util.storage.Topology; +import software.amazon.jdbc.hostlistprovider.Topology; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class MonitorServiceImpl implements MonitorService, EventSubscriber { @@ -61,13 +61,11 @@ public class MonitorServiceImpl implements MonitorService, EventSubscriber { Set recreateOnError = new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)); MonitorSettings defaultSettings = new MonitorSettings( - TimeUnit.MINUTES.toNanos(5), TimeUnit.MINUTES.toNanos(1), recreateOnError); + TimeUnit.MINUTES.toNanos(5), TimeUnit.MINUTES.toNanos(3), recreateOnError); suppliers.put( CustomEndpointMonitorImpl.class, () -> new CacheContainer(defaultSettings, AllowedAndBlockedHosts.class)); - MonitorSettings topologySettings = - new MonitorSettings(TimeUnit.MINUTES.toNanos(5), TimeUnit.MINUTES.toNanos(3), recreateOnError); - suppliers.put(ClusterTopologyMonitorImpl.class, () -> new CacheContainer(topologySettings, Topology.class)); + suppliers.put(ClusterTopologyMonitorImpl.class, () -> new CacheContainer(defaultSettings, Topology.class)); defaultSuppliers = Collections.unmodifiableMap(suppliers); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index eac6baaa5..d258cbc24 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -26,6 +26,7 @@ import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.AllowedAndBlockedHosts; +import software.amazon.jdbc.hostlistprovider.Topology; import software.amazon.jdbc.util.ExecutorFactory; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.events.DataAccessEvent; diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java index 7b9ce95f7..539ce4e09 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsHostListProviderTest.java @@ -68,7 +68,6 @@ import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.TestStorageServiceImpl; -import software.amazon.jdbc.util.storage.Topology; class RdsHostListProviderTest { private StorageService storageService; diff --git a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java index e9dfaedf0..df6d6ee50 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/hostlistprovider/RdsMultiAzDbClusterListProviderTest.java @@ -62,7 +62,6 @@ import software.amazon.jdbc.util.events.EventPublisher; import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.storage.TestStorageServiceImpl; -import software.amazon.jdbc.util.storage.Topology; class RdsMultiAzDbClusterListProviderTest { private StorageService storageService; From 22e940561a10efcccbd3b2663e73ef54c31f784e Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 29 May 2025 15:28:23 -0700 Subject: [PATCH 141/149] Adjust default monitor expiration settings --- .../customendpoint/CustomEndpointPlugin.java | 15 +++++++++++++-- .../jdbc/util/monitoring/MonitorServiceImpl.java | 8 ++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java index 2619b7d84..880ca38e4 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java @@ -41,6 +41,7 @@ import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.SubscribedMethodHelper; import software.amazon.jdbc.util.WrapperUtils; +import software.amazon.jdbc.util.monitoring.MonitorErrorResponse; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -50,9 +51,11 @@ */ public class CustomEndpointPlugin extends AbstractConnectionPlugin { private static final Logger LOGGER = Logger.getLogger(CustomEndpointPlugin.class.getName()); - private static final String TELEMETRY_WAIT_FOR_INFO_COUNTER = "customEndpoint.waitForInfo.counter"; + protected static final String TELEMETRY_WAIT_FOR_INFO_COUNTER = "customEndpoint.waitForInfo.counter"; protected static final RegionUtils regionUtils = new RegionUtils(); - private static final Set subscribedMethods = + protected static final Set monitorErrorResponses = + new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)); + protected static final Set subscribedMethods = Collections.unmodifiableSet(new HashSet() { { addAll(SubscribedMethodHelper.NETWORK_BOUND_METHODS); @@ -144,6 +147,14 @@ public CustomEndpointPlugin( TelemetryFactory telemetryFactory = servicesContainer.getTelemetryFactory(); this.waitForInfoCounter = telemetryFactory.createCounter(TELEMETRY_WAIT_FOR_INFO_COUNTER); + + this.servicesContainer.getMonitorService().registerMonitorTypeIfAbsent( + CustomEndpointMonitorImpl.class, + TimeUnit.MILLISECONDS.toNanos(this.idleMonitorExpirationMs), + TimeUnit.MINUTES.toNanos(1), + monitorErrorResponses, + CustomEndpointInfo.class + ); } @Override diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 4060d11df..72a7c850b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -30,13 +30,12 @@ import java.util.logging.Logger; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; -import software.amazon.jdbc.AllowedAndBlockedHosts; import software.amazon.jdbc.ConnectionProvider; import software.amazon.jdbc.DriverConnectionProvider; import software.amazon.jdbc.TargetDriverHelper; import software.amazon.jdbc.dialect.Dialect; +import software.amazon.jdbc.hostlistprovider.Topology; import software.amazon.jdbc.hostlistprovider.monitoring.ClusterTopologyMonitorImpl; -import software.amazon.jdbc.plugin.customendpoint.CustomEndpointMonitorImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ExecutorFactory; import software.amazon.jdbc.util.Messages; @@ -48,7 +47,6 @@ import software.amazon.jdbc.util.events.EventSubscriber; import software.amazon.jdbc.util.storage.ExternallyManagedCache; import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.hostlistprovider.Topology; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class MonitorServiceImpl implements MonitorService, EventSubscriber { @@ -61,10 +59,8 @@ public class MonitorServiceImpl implements MonitorService, EventSubscriber { Set recreateOnError = new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)); MonitorSettings defaultSettings = new MonitorSettings( - TimeUnit.MINUTES.toNanos(5), TimeUnit.MINUTES.toNanos(3), recreateOnError); + TimeUnit.MINUTES.toNanos(15), TimeUnit.MINUTES.toNanos(3), recreateOnError); - suppliers.put( - CustomEndpointMonitorImpl.class, () -> new CacheContainer(defaultSettings, AllowedAndBlockedHosts.class)); suppliers.put(ClusterTopologyMonitorImpl.class, () -> new CacheContainer(defaultSettings, Topology.class)); defaultSuppliers = Collections.unmodifiableMap(suppliers); } From 4a747cf8aad013fb665ff2e6f3a0f578d05d4ee9 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 29 May 2025 15:31:15 -0700 Subject: [PATCH 142/149] Fix checkstyle --- .../hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java | 2 +- .../monitoring/MonitoringRdsHostListProvider.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java index c1ab92e7f..08630097d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/ClusterTopologyMonitorImpl.java @@ -47,6 +47,7 @@ import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.hostavailability.HostAvailability; +import software.amazon.jdbc.hostlistprovider.Topology; import software.amazon.jdbc.util.ExecutorFactory; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; @@ -57,7 +58,6 @@ import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.AbstractMonitor; import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.hostlistprovider.Topology; public class ClusterTopologyMonitorImpl extends AbstractMonitor implements ClusterTopologyMonitor { diff --git a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java index ce1b5f28a..29aae6ddd 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/hostlistprovider/monitoring/MonitoringRdsHostListProvider.java @@ -30,11 +30,11 @@ import software.amazon.jdbc.PropertyDefinition; import software.amazon.jdbc.cleanup.CanReleaseResources; import software.amazon.jdbc.hostlistprovider.RdsHostListProvider; +import software.amazon.jdbc.hostlistprovider.Topology; import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.connection.ConnectionService; import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.storage.StorageService; -import software.amazon.jdbc.hostlistprovider.Topology; public class MonitoringRdsHostListProvider extends RdsHostListProvider implements BlockingHostListProvider, CanReleaseResources { From 54023a4e987e47ff1b51c5e792a119237e662677 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 2 Jun 2025 12:24:11 -0700 Subject: [PATCH 143/149] Fix broken unit tests --- .../jdbc/plugin/customendpoint/CustomEndpointPluginTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java index 58761bfdb..883cac834 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java +++ b/wrapper/src/test/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPluginTest.java @@ -46,6 +46,7 @@ import software.amazon.jdbc.hostavailability.HostAvailabilityStrategy; import software.amazon.jdbc.hostavailability.SimpleHostAvailabilityStrategy; import software.amazon.jdbc.util.FullServicesContainer; +import software.amazon.jdbc.util.monitoring.MonitorService; import software.amazon.jdbc.util.telemetry.TelemetryCounter; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -62,6 +63,7 @@ public class CustomEndpointPluginTest { @Mock private FullServicesContainer mockServicesContainer; @Mock private PluginService mockPluginService; + @Mock private MonitorService mockMonitorService; @Mock private BiFunction mockRdsClientFunc; @Mock private TelemetryFactory mockTelemetryFactory; @Mock private TelemetryCounter mockTelemetryCounter; @@ -75,6 +77,7 @@ public void init() throws SQLException { closeable = MockitoAnnotations.openMocks(this); when(mockServicesContainer.getPluginService()).thenReturn(mockPluginService); + when(mockServicesContainer.getMonitorService()).thenReturn(mockMonitorService); when(mockServicesContainer.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockTelemetryFactory.createCounter(any(String.class))).thenReturn(mockTelemetryCounter); when(mockMonitor.hasCustomEndpointInfo()).thenReturn(true); From a02272c7cb4739728b716993a6568e67e0a3e363 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 2 Jul 2025 15:28:15 -0700 Subject: [PATCH 144/149] Fix build issues wip --- .../jdbc/ConnectionPluginChainBuilder.java | 11 +++--- .../amazon/jdbc/ConnectionPluginManager.java | 1 - .../amazon/jdbc/PartialPluginService.java | 32 +++++++++++++++++ .../amazon/jdbc/PluginServiceImpl.java | 5 --- .../bluegreen/BlueGreenConnectionPlugin.java | 13 ++++--- .../BlueGreenConnectionPluginFactory.java | 17 ++++++--- .../bluegreen/BlueGreenProviderSupplier.java | 4 +-- .../bluegreen/BlueGreenStatusMonitor.java | 11 +++--- .../bluegreen/BlueGreenStatusProvider.java | 11 +++--- .../customendpoint/CustomEndpointPlugin.java | 2 +- .../amazon/jdbc/DialectDetectionTests.java | 35 ++++++++++--------- .../jdbc/plugin/efm/ConcurrencyTests.java | 0 12 files changed, 91 insertions(+), 51 deletions(-) delete mode 100644 wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java index 94115d16b..411e40cd8 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginChainBuilder.java @@ -220,13 +220,12 @@ public List getPlugins( plugins = new ArrayList<>(pluginFactories.size() + 1); for (final ConnectionPluginFactory factory : pluginFactories) { if (factory instanceof ServicesContainerPluginFactory) { - ServicesContainerPluginFactory servicesContainerPluginFactory = (ServicesContainerPluginFactory) factory; - plugins.add(servicesContainerPluginFactory.getInstance(servicesContainer, props)); - } else { - plugins.add(factory.getInstance(servicesContainer.getPluginService(), props)); - } + ServicesContainerPluginFactory servicesContainerPluginFactory = (ServicesContainerPluginFactory) factory; + plugins.add(servicesContainerPluginFactory.getInstance(servicesContainer, props)); + } else { + plugins.add(factory.getInstance(servicesContainer.getPluginService(), props)); } - + } } else { plugins = new ArrayList<>(1); // one spot for default connection plugin } diff --git a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java index 9d28e8f23..afe0d3c77 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java +++ b/wrapper/src/main/java/software/amazon/jdbc/ConnectionPluginManager.java @@ -49,7 +49,6 @@ import software.amazon.jdbc.plugin.staledns.AuroraStaleDnsPlugin; import software.amazon.jdbc.plugin.strategy.fastestresponse.FastestResponseStrategyPlugin; import software.amazon.jdbc.profile.ConfigurationProfile; -import software.amazon.jdbc.util.AsynchronousMethodsHelper; import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Utils; diff --git a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java index 00533702d..67027195b 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PartialPluginService.java @@ -662,6 +662,38 @@ public T getPlugin(final Class pluginClazz) { return null; } + @Override + public void setStatus(Class clazz, @Nullable T status, boolean clusterBound) { + throw new UnsupportedOperationException( + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"setStatus"})); + } + + @Override + public void setStatus(Class clazz, @Nullable T status, String key) { + throw new UnsupportedOperationException( + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"setStatus"})); + } + + @Override + public T getStatus(@NonNull Class clazz, boolean clusterBound) { + throw new UnsupportedOperationException( + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"getStatus"})); + } + + @Override + public T getStatus(@NonNull Class clazz, String key) { + throw new UnsupportedOperationException( + Messages.get("PartialPluginService.unexpectedMethodCall", new Object[] {"getStatus"})); + } + + public boolean isPluginInUse(final Class pluginClazz) { + try { + return this.pluginManager.isWrapperFor(pluginClazz); + } catch (SQLException e) { + return false; + } + } + public static void clearCache() { hostAvailabilityExpiringCache.clear(); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index da372b067..fd506cc41 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -217,11 +217,6 @@ public String getOriginalUrl() { return this.originalUrl; } - @Override - public String getOriginalUrl() { - return this.originalUrl; - } - @Override @Deprecated public void setAllowedAndBlockedHosts(AllowedAndBlockedHosts allowedAndBlockedHosts) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenConnectionPlugin.java index bcb5be447..e955146f9 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenConnectionPlugin.java @@ -40,6 +40,7 @@ import software.amazon.jdbc.plugin.bluegreen.routing.ConnectRouting; import software.amazon.jdbc.plugin.bluegreen.routing.ExecuteRouting; import software.amazon.jdbc.plugin.iam.IamAuthConnectionPlugin; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.RdsUtils; import software.amazon.jdbc.util.telemetry.TelemetryFactory; @@ -71,6 +72,7 @@ public class BlueGreenConnectionPlugin extends AbstractConnectionPlugin { PropertyDefinition.registerPluginProperties(BlueGreenConnectionPlugin.class); } + protected final FullServicesContainer servicesContainer; protected final PluginService pluginService; protected final Properties props; protected BlueGreenProviderSupplier providerSupplier; @@ -89,17 +91,18 @@ public class BlueGreenConnectionPlugin extends AbstractConnectionPlugin { protected final Set subscribedMethods; public BlueGreenConnectionPlugin( - final @NonNull PluginService pluginService, + final @NonNull FullServicesContainer servicesContainer, final @NonNull Properties props) { - this(pluginService, props, BlueGreenStatusProvider::new); + this(servicesContainer, props, BlueGreenStatusProvider::new); } public BlueGreenConnectionPlugin( - final @NonNull PluginService pluginService, + final @NonNull FullServicesContainer servicesContainer, final @NonNull Properties props, final @NonNull BlueGreenProviderSupplier providerSupplier) { - this.pluginService = pluginService; + this.servicesContainer = servicesContainer; + this.pluginService = servicesContainer.getPluginService(); this.props = props; this.telemetryFactory = pluginService.getTelemetryFactory(); this.providerSupplier = providerSupplier; @@ -299,7 +302,7 @@ public T execute( protected void initProvider() { provider.computeIfAbsent(this.bgdId, - (key) -> this.providerSupplier.create(this.pluginService, this.props, this.bgdId)); + (key) -> this.providerSupplier.create(this.servicesContainer, this.props, this.bgdId)); } // For testing purposes diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenConnectionPluginFactory.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenConnectionPluginFactory.java index 1667a39c9..e743d2f9a 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenConnectionPluginFactory.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenConnectionPluginFactory.java @@ -18,14 +18,21 @@ import java.util.Properties; import software.amazon.jdbc.ConnectionPlugin; -import software.amazon.jdbc.ConnectionPluginFactory; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.plugin.iam.IamAuthConnectionPlugin; - -public class BlueGreenConnectionPluginFactory implements ConnectionPluginFactory { +import software.amazon.jdbc.ServicesContainerPluginFactory; +import software.amazon.jdbc.util.FullServicesContainer; +import software.amazon.jdbc.util.Messages; +public class BlueGreenConnectionPluginFactory implements ServicesContainerPluginFactory { @Override public ConnectionPlugin getInstance(final PluginService pluginService, final Properties props) { - return new BlueGreenConnectionPlugin(pluginService, props); + throw new UnsupportedOperationException( + Messages.get( + "ServicesContainerPluginFactory.servicesContainerRequired", new Object[] {"BlueGreenConnectionPlugin"})); + } + + @Override + public ConnectionPlugin getInstance(final FullServicesContainer servicesContainer, final Properties props) { + return new BlueGreenConnectionPlugin(servicesContainer, props); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenProviderSupplier.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenProviderSupplier.java index 7404b8a0c..3cc3bcfa6 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenProviderSupplier.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenProviderSupplier.java @@ -17,10 +17,10 @@ package software.amazon.jdbc.plugin.bluegreen; import java.util.Properties; -import software.amazon.jdbc.PluginService; +import software.amazon.jdbc.util.FullServicesContainer; @FunctionalInterface public interface BlueGreenProviderSupplier { - BlueGreenStatusProvider create(PluginService pluginService, Properties props, String bgdId); + BlueGreenStatusProvider create(FullServicesContainer servicesContainer, Properties props, String bgdId); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusMonitor.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusMonitor.java index 290fbac18..a3a717638 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusMonitor.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusMonitor.java @@ -44,7 +44,6 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.HostListProvider; -import software.amazon.jdbc.HostListProviderService; import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.HostSpecBuilder; import software.amazon.jdbc.PluginService; @@ -54,6 +53,7 @@ import software.amazon.jdbc.plugin.iam.IamAuthConnectionPlugin; import software.amazon.jdbc.util.ConnectionUrlParser; import software.amazon.jdbc.util.ExecutorFactory; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; @@ -69,6 +69,7 @@ public class BlueGreenStatusMonitor { // Add more versions here if needed. protected static final Set knownVersions = new HashSet<>(Collections.singletonList(latestKnownVersion)); protected final BlueGreenDialect blueGreenDialect; + protected final FullServicesContainer servicesContainer; protected final PluginService pluginService; protected final String bgdId; protected final Properties props; @@ -125,7 +126,7 @@ public BlueGreenStatusMonitor( final @NonNull BlueGreenRole role, final @NonNull String bgdId, final @NonNull HostSpec initialHostSpec, - final @NonNull PluginService pluginService, + final @NonNull FullServicesContainer servicesContainer, final @NonNull Properties props, final @NonNull Map statusCheckIntervalMap, final @Nullable OnBlueGreenStatusChange onBlueGreenStatusChangeFunc) { @@ -133,7 +134,8 @@ public BlueGreenStatusMonitor( this.role = role; this.bgdId = bgdId; this.initialHostSpec = initialHostSpec; - this.pluginService = pluginService; + this.servicesContainer = servicesContainer; + this.pluginService = servicesContainer.getPluginService(); this.props = props; this.statusCheckIntervalMap = statusCheckIntervalMap; this.onBlueGreenStatusChangeFunc = onBlueGreenStatusChangeFunc; @@ -617,8 +619,7 @@ protected void initHostListProvider() { .getProvider( hostListProperties, hostListProviderUrl, - (HostListProviderService) this.pluginService, - this.pluginService); + this.servicesContainer); } else { LOGGER.warning(() -> Messages.get("bgd.hostSpecNull")); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusProvider.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusProvider.java index 16bcdf6c7..591ef3f00 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusProvider.java @@ -52,6 +52,7 @@ import software.amazon.jdbc.plugin.bluegreen.routing.SuspendConnectRouting; import software.amazon.jdbc.plugin.bluegreen.routing.SuspendExecuteRouting; import software.amazon.jdbc.plugin.bluegreen.routing.SuspendUntilCorrespondingNodeFoundConnectRouting; +import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Pair; import software.amazon.jdbc.util.PropertyUtils; @@ -119,6 +120,7 @@ public class BlueGreenStatusProvider { protected final long switchoverTimeoutNano; protected final boolean suspendNewBlueConnectionsWhenInProgress; + protected final FullServicesContainer servicesContainer; protected final PluginService pluginService; protected final Properties props; protected final String bgdId; @@ -126,11 +128,12 @@ public class BlueGreenStatusProvider { protected final RdsUtils rdsUtils = new RdsUtils(); public BlueGreenStatusProvider( - final @NonNull PluginService pluginService, + final @NonNull FullServicesContainer servicesContainer, final @NonNull Properties props, final @NonNull String bgdId) { - this.pluginService = pluginService; + this.servicesContainer = servicesContainer; + this.pluginService = servicesContainer.getPluginService(); this.props = props; this.bgdId = bgdId; @@ -156,7 +159,7 @@ protected void initMonitoring() { BlueGreenRole.SOURCE, this.bgdId, this.pluginService.getCurrentHostSpec(), - this.pluginService, + this.servicesContainer, this.getMonitoringProperties(), statusCheckIntervalMap, this::prepareStatus); @@ -165,7 +168,7 @@ protected void initMonitoring() { BlueGreenRole.TARGET, this.bgdId, this.pluginService.getCurrentHostSpec(), - this.pluginService, + this.servicesContainer, this.getMonitoringProperties(), statusCheckIntervalMap, this::prepareStatus); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java index b6bab3ed5..b6a4a6047 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/customendpoint/CustomEndpointPlugin.java @@ -55,7 +55,6 @@ public class CustomEndpointPlugin extends AbstractConnectionPlugin { protected static final RegionUtils regionUtils = new RegionUtils(); protected static final Set monitorErrorResponses = new HashSet<>(Collections.singletonList(MonitorErrorResponse.RECREATE)); - protected static final Set subscribedMethods; public static final AwsWrapperProperty CUSTOM_ENDPOINT_INFO_REFRESH_RATE_MS = new AwsWrapperProperty( "customEndpointInfoRefreshRateMs", "30000", @@ -92,6 +91,7 @@ public class CustomEndpointPlugin extends AbstractConnectionPlugin { protected final RdsUtils rdsUtils = new RdsUtils(); protected final BiFunction rdsClientFunc; + protected final Set subscribedMethods; protected final TelemetryCounter waitForInfoCounter; protected final boolean shouldWaitForInfo; protected final int waitOnCachedInfoDurationMs; diff --git a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java index b92a1c748..94fce6e86 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java @@ -64,7 +64,7 @@ public class DialectDetectionTests { private final Properties props = new Properties(); private AutoCloseable closeable; @Mock private FullServicesContainer mockServicesContainer; - @Mock private HostListProvider mockHostListProvider; + @Mock private HostListProviderService mockHostListProviderService; @Mock private Connection mockConnection; @Mock private Statement mockStatement; @Mock private ResultSet mockSuccessResultSet; @@ -77,6 +77,7 @@ public class DialectDetectionTests { @BeforeEach void setUp() throws SQLException { closeable = MockitoAnnotations.openMocks(this); + when(this.mockServicesContainer.getHostListProviderService()).thenReturn(mockHostListProviderService); when(this.mockServicesContainer.getConnectionPluginManager()).thenReturn(mockPluginManager); when(this.mockConnection.createStatement()).thenReturn(this.mockStatement); when(this.mockHost.getUrl()).thenReturn("url"); @@ -90,13 +91,13 @@ void cleanUp() throws Exception { DialectManager.resetEndpointCache(); } - PluginServiceImpl getPluginService(String protocol) throws SQLException { + PluginServiceImpl getPluginService(String host, String protocol) throws SQLException { return spy( new PluginServiceImpl( mockServicesContainer, new ExceptionManager(), props, - protocol + DialectDetectionTests.LOCALHOST, + protocol + host, protocol, null, mockTargetDriverDialect, @@ -128,7 +129,7 @@ static Stream getInitialDialectArguments() { @Test void testUpdateDialectMysqlUnchanged() throws SQLException { when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet); - final PluginServiceImpl target = getPluginService(MYSQL_PROTOCOL); + final PluginServiceImpl target = getPluginService(LOCALHOST, MYSQL_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(MysqlDialect.class, target.dialect.getClass()); @@ -158,7 +159,7 @@ void testUpdateDialectMysqlToRds() throws SQLException { void testUpdateDialectMysqlToTaz() throws SQLException { when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet, mockSuccessResultSet); when(mockSuccessResultSet.next()).thenReturn(true); - final PluginServiceImpl target = getPluginService(MYSQL_PROTOCOL); + final PluginServiceImpl target = getPluginService(LOCALHOST, MYSQL_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(AuroraMysqlDialect.class, target.dialect.getClass()); @@ -169,7 +170,7 @@ void testUpdateDialectMysqlToAurora() throws SQLException { when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet); when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'aurora_version'")).thenReturn(mockSuccessResultSet); when(mockSuccessResultSet.next()).thenReturn(true, false); - final PluginServiceImpl target = getPluginService(MYSQL_PROTOCOL); + final PluginServiceImpl target = getPluginService(LOCALHOST, MYSQL_PROTOCOL); when(mockServicesContainer.getPluginService()).thenReturn(target); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); @@ -179,7 +180,7 @@ void testUpdateDialectMysqlToAurora() throws SQLException { @Test void testUpdateDialectPgUnchanged() throws SQLException { when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet); - final PluginServiceImpl target = getPluginService(PG_PROTOCOL); + final PluginServiceImpl target = getPluginService(LOCALHOST, PG_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(PgDialect.class, target.dialect.getClass()); @@ -194,7 +195,7 @@ void testUpdateDialectPgToRds() throws SQLException { when(mockSuccessResultSet.getBoolean("aurora_stat_utils")).thenReturn(false); when(mockSuccessResultSet.next()).thenReturn(true); when(mockFailResultSet.next()).thenReturn(false); - final PluginServiceImpl target = getPluginService(PG_PROTOCOL); + final PluginServiceImpl target = getPluginService(LOCALHOST, PG_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(RdsPgDialect.class, target.dialect.getClass()); @@ -209,7 +210,7 @@ void testUpdateDialectPgToTaz() throws SQLException { when(mockStatement.executeQuery(any())).thenReturn(mockSuccessResultSet); when(mockSuccessResultSet.getBoolean(any())).thenReturn(false); when(mockSuccessResultSet.next()).thenReturn(true); - final PluginServiceImpl target = getPluginService(PG_PROTOCOL); + final PluginServiceImpl target = getPluginService(LOCALHOST, PG_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(RdsMultiAzDbClusterPgDialect.class, target.dialect.getClass()); @@ -224,7 +225,7 @@ void testUpdateDialectPgToAurora() throws SQLException { when(mockStatement.executeQuery(any())).thenReturn(mockSuccessResultSet); when(mockSuccessResultSet.next()).thenReturn(true); when(mockSuccessResultSet.getBoolean(any())).thenReturn(true); - final PluginServiceImpl target = getPluginService(PG_PROTOCOL); + final PluginServiceImpl target = getPluginService(LOCALHOST, PG_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(AuroraPgDialect.class, target.dialect.getClass()); @@ -233,7 +234,7 @@ void testUpdateDialectPgToAurora() throws SQLException { @Test void testUpdateDialectMariaUnchanged() throws SQLException { when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet); - final PluginServiceImpl target = getPluginService(MARIA_PROTOCOL); + final PluginServiceImpl target = getPluginService(LOCALHOST, MARIA_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(MariaDbDialect.class, target.dialect.getClass()); @@ -244,11 +245,11 @@ void testUpdateDialectMariaToMysqlRds() throws SQLException { when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet); when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'version_comment'")).thenReturn(mockSuccessResultSet); when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'report_host'")).thenReturn(mockSuccessResultSet); - when(successResultSet.getString(2)).thenReturn( + when(mockSuccessResultSet.getString(2)).thenReturn( "Source distribution", "Source distribution", ""); - when(successResultSet.next()).thenReturn(true, false, true, true); - when(successResultSet.getMetaData()).thenReturn(mockResultSetMetaData); - when(failResultSet.next()).thenReturn(false); + when(mockSuccessResultSet.next()).thenReturn(true, false, true, true); + when(mockSuccessResultSet.getMetaData()).thenReturn(mockResultSetMetaData); + when(mockFailResultSet.next()).thenReturn(false); final PluginServiceImpl target = getPluginService(LOCALHOST, MARIA_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); @@ -262,7 +263,7 @@ void testUpdateDialectMariaToMysqlRds() throws SQLException { // 2) test PluginServiceImpl.updateDialect() with mocked DialectManager.getDialect() void testUpdateDialectMariaToMysqlTaz() throws SQLException { when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet, mockSuccessResultSet); - final PluginServiceImpl target = getPluginService(MARIA_PROTOCOL); + final PluginServiceImpl target = getPluginService(LOCALHOST, MARIA_PROTOCOL); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); assertEquals(RdsMultiAzDbClusterMysqlDialect.class, target.dialect.getClass()); @@ -273,7 +274,7 @@ void testUpdateDialectMariaToMysqlAurora() throws SQLException { when(mockStatement.executeQuery(any())).thenReturn(mockFailResultSet); when(mockStatement.executeQuery("SHOW VARIABLES LIKE 'aurora_version'")).thenReturn(mockSuccessResultSet); when(mockSuccessResultSet.next()).thenReturn(true, false); - final PluginServiceImpl target = getPluginService(MARIA_PROTOCOL); + final PluginServiceImpl target = getPluginService(LOCALHOST, MARIA_PROTOCOL); when(mockServicesContainer.getPluginService()).thenReturn(target); target.setInitialConnectionHostSpec(mockHost); target.updateDialect(mockConnection); diff --git a/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java b/wrapper/src/test/java/software/amazon/jdbc/plugin/efm/ConcurrencyTests.java deleted file mode 100644 index e69de29bb..000000000 From 42bbed68d0c43ec44180f80fb15fefc81b60e79b Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Wed, 2 Jul 2025 16:26:25 -0700 Subject: [PATCH 145/149] Fix build --- .../amazon/jdbc/ConnectionPluginManagerTests.java | 1 + .../java/software/amazon/jdbc/DialectDetectionTests.java | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java index adf33c794..355276ad3 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/ConnectionPluginManagerTests.java @@ -95,6 +95,7 @@ void cleanUp() throws Exception { void init() { closeable = MockitoAnnotations.openMocks(this); when(mockServicesContainer.getPluginService()).thenReturn(mockPluginService); + when(mockServicesContainer.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockPluginService.getTelemetryFactory()).thenReturn(mockTelemetryFactory); when(mockTelemetryFactory.openTelemetryContext(anyString(), any())).thenReturn(mockTelemetryContext); when(mockTelemetryFactory.openTelemetryContext(eq(null), any())).thenReturn(mockTelemetryContext); diff --git a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java index 94fce6e86..f543d50c6 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java @@ -52,6 +52,7 @@ import software.amazon.jdbc.exceptions.ExceptionManager; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.FullServicesContainer; +import software.amazon.jdbc.util.storage.StorageService; public class DialectDetectionTests { private static final String LOCALHOST = "localhost"; @@ -65,6 +66,7 @@ public class DialectDetectionTests { private AutoCloseable closeable; @Mock private FullServicesContainer mockServicesContainer; @Mock private HostListProviderService mockHostListProviderService; + @Mock private StorageService mockStorageService; @Mock private Connection mockConnection; @Mock private Statement mockStatement; @Mock private ResultSet mockSuccessResultSet; @@ -79,6 +81,7 @@ void setUp() throws SQLException { closeable = MockitoAnnotations.openMocks(this); when(this.mockServicesContainer.getHostListProviderService()).thenReturn(mockHostListProviderService); when(this.mockServicesContainer.getConnectionPluginManager()).thenReturn(mockPluginManager); + when(this.mockServicesContainer.getStorageService()).thenReturn(mockStorageService); when(this.mockConnection.createStatement()).thenReturn(this.mockStatement); when(this.mockHost.getUrl()).thenReturn("url"); when(this.mockFailResultSet.next()).thenReturn(false); @@ -92,7 +95,7 @@ void cleanUp() throws Exception { } PluginServiceImpl getPluginService(String host, String protocol) throws SQLException { - return spy( + PluginServiceImpl pluginService = spy( new PluginServiceImpl( mockServicesContainer, new ExceptionManager(), @@ -103,6 +106,9 @@ PluginServiceImpl getPluginService(String host, String protocol) throws SQLExcep mockTargetDriverDialect, null, null)); + + when(this.mockServicesContainer.getHostListProviderService()).thenReturn(pluginService); + return pluginService; } @ParameterizedTest From 7abea2ae6528905d81d61f97cffefb0921f3d44c Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 3 Jul 2025 09:51:19 -0700 Subject: [PATCH 146/149] Use StorageService instead of getStatus/setStatus --- .../software/amazon/jdbc/PluginServiceImpl.java | 4 ++++ .../bluegreen/BlueGreenConnectionPlugin.java | 17 ++++++++++++----- .../bluegreen/BlueGreenStatusProvider.java | 7 +++++-- .../bluegreen/routing/BaseConnectRouting.java | 3 ++- .../bluegreen/routing/BaseExecuteRouting.java | 2 ++ .../plugin/bluegreen/routing/BaseRouting.java | 6 +++--- .../routing/CloseConnectionExecuteRouting.java | 2 ++ .../bluegreen/routing/ConnectRouting.java | 2 ++ .../bluegreen/routing/ExecuteRouting.java | 2 ++ .../routing/PassThroughConnectRouting.java | 6 +++--- .../routing/PassThroughExecuteRouting.java | 2 ++ .../bluegreen/routing/RejectConnectRouting.java | 4 +++- .../routing/SubstituteConnectRouting.java | 4 +++- .../routing/SuspendConnectRouting.java | 9 +++++---- .../routing/SuspendExecuteRouting.java | 9 +++++---- ...tilCorrespondingNodeFoundConnectRouting.java | 12 ++++++------ .../jdbc/util/storage/StorageServiceImpl.java | 3 +++ 17 files changed, 64 insertions(+), 30 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java index fd506cc41..e04a4690c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/PluginServiceImpl.java @@ -806,6 +806,7 @@ public static void clearCache() { hostAvailabilityExpiringCache.clear(); } + @Deprecated // Use StorageService#set instead. public void setStatus(final Class clazz, final @Nullable T status, final boolean clusterBound) { String clusterId = null; if (clusterBound) { @@ -818,6 +819,7 @@ public void setStatus(final Class clazz, final @Nullable T status, final this.setStatus(clazz, status, clusterId); } + @Deprecated // Use StorageService#set instead. public void setStatus(final Class clazz, final @Nullable T status, final String key) { final String cacheKey = this.getStatusCacheKey(clazz, key); if (status == null) { @@ -827,6 +829,7 @@ public void setStatus(final Class clazz, final @Nullable T status, final } } + @Deprecated // Use StorageService#get instead. public T getStatus(final @NonNull Class clazz, final boolean clusterBound) { String clusterId = null; if (clusterBound) { @@ -839,6 +842,7 @@ public T getStatus(final @NonNull Class clazz, final boolean clusterBound return this.getStatus(clazz, clusterId); } + @Deprecated // Use StorageService#get instead. public T getStatus(final @NonNull Class clazz, String key) { return clazz.cast(statusesExpiringCache.get(this.getStatusCacheKey(clazz, key))); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenConnectionPlugin.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenConnectionPlugin.java index e955146f9..54fbb9313 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenConnectionPlugin.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenConnectionPlugin.java @@ -42,6 +42,7 @@ import software.amazon.jdbc.plugin.iam.IamAuthConnectionPlugin; import software.amazon.jdbc.util.FullServicesContainer; import software.amazon.jdbc.util.RdsUtils; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryFactory; public class BlueGreenConnectionPlugin extends AbstractConnectionPlugin { @@ -73,6 +74,7 @@ public class BlueGreenConnectionPlugin extends AbstractConnectionPlugin { } protected final FullServicesContainer servicesContainer; + protected final StorageService storageService; protected final PluginService pluginService; protected final Properties props; protected BlueGreenProviderSupplier providerSupplier; @@ -102,6 +104,7 @@ public BlueGreenConnectionPlugin( final @NonNull BlueGreenProviderSupplier providerSupplier) { this.servicesContainer = servicesContainer; + this.storageService = servicesContainer.getStorageService(); this.pluginService = servicesContainer.getPluginService(); this.props = props; this.telemetryFactory = pluginService.getTelemetryFactory(); @@ -134,7 +137,7 @@ public Connection connect( try { - this.bgStatus = this.pluginService.getStatus(BlueGreenStatus.class, this.bgdId); + this.bgStatus = this.storageService.get(BlueGreenStatus.class, this.bgdId); if (this.bgStatus == null) { return regularOpenConnection(connectFunc, isInitialConnection); @@ -170,11 +173,14 @@ public Connection connect( props, isInitialConnection, connectFunc, + this.storageService, this.pluginService); if (conn == null) { - - this.bgStatus = this.pluginService.getStatus(BlueGreenStatus.class, this.bgdId); + BlueGreenStatus latestStatus = this.storageService.get(BlueGreenStatus.class, this.bgdId); + if (latestStatus != null) { + this.bgStatus = latestStatus; + } routing = this.bgStatus.getConnectRouting().stream() .filter(r -> r.isMatch(hostSpec, hostRole)) @@ -236,7 +242,7 @@ public T execute( return jdbcMethodFunc.call(); } - this.bgStatus = this.pluginService.getStatus(BlueGreenStatus.class, this.bgdId); + this.bgStatus = this.storageService.get(BlueGreenStatus.class, this.bgdId); if (this.bgStatus == null) { return jdbcMethodFunc.call(); @@ -271,12 +277,13 @@ public T execute( methodName, jdbcMethodFunc, jdbcMethodArgs, + this.storageService, this.pluginService, this.props); if (!result.isPresent()) { - this.bgStatus = this.pluginService.getStatus(BlueGreenStatus.class, this.bgdId); + this.bgStatus = this.storageService.get(BlueGreenStatus.class, this.bgdId); routing = this.bgStatus.getExecuteRouting().stream() .filter(r -> r.isMatch(currentHostSpec, hostRole)) diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusProvider.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusProvider.java index 591ef3f00..2690e3d25 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusProvider.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/BlueGreenStatusProvider.java @@ -59,6 +59,7 @@ import software.amazon.jdbc.util.RdsUtils; import software.amazon.jdbc.util.StringUtils; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.storage.StorageService; public class BlueGreenStatusProvider { @@ -121,6 +122,7 @@ public class BlueGreenStatusProvider { protected final boolean suspendNewBlueConnectionsWhenInProgress; protected final FullServicesContainer servicesContainer; + protected final StorageService storageService; protected final PluginService pluginService; protected final Properties props; protected final String bgdId; @@ -133,6 +135,7 @@ public BlueGreenStatusProvider( final @NonNull String bgdId) { this.servicesContainer = servicesContainer; + this.storageService = servicesContainer.getStorageService(); this.pluginService = servicesContainer.getPluginService(); this.props = props; this.bgdId = bgdId; @@ -273,8 +276,8 @@ protected void updatePhase(BlueGreenRole role, BlueGreenInterimStatus interimSta } protected void updateStatusCache() { - final BlueGreenStatus latestStatus = this.pluginService.getStatus(BlueGreenStatus.class, this.bgdId); - this.pluginService.setStatus(BlueGreenStatus.class, this.summaryStatus, this.bgdId); + final BlueGreenStatus latestStatus = this.storageService.get(BlueGreenStatus.class, this.bgdId); + this.storageService.set(this.bgdId, this.summaryStatus); this.storePhaseTime(this.summaryStatus.getCurrentPhase()); // Notify all waiting threads that status is updated. diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/BaseConnectRouting.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/BaseConnectRouting.java index 1d29c21f6..276cac529 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/BaseConnectRouting.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/BaseConnectRouting.java @@ -25,8 +25,8 @@ import software.amazon.jdbc.HostSpec; import software.amazon.jdbc.JdbcCallable; import software.amazon.jdbc.PluginService; -import software.amazon.jdbc.plugin.bluegreen.BlueGreenConnectionPlugin; import software.amazon.jdbc.plugin.bluegreen.BlueGreenRole; +import software.amazon.jdbc.util.storage.StorageService; public abstract class BaseConnectRouting extends BaseRouting implements ConnectRouting { @@ -54,6 +54,7 @@ public abstract Connection apply( Properties props, boolean isInitialConnection, JdbcCallable connectFunc, + StorageService storageService, PluginService pluginService) throws SQLException; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/BaseExecuteRouting.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/BaseExecuteRouting.java index d498378db..62ac29e3c 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/BaseExecuteRouting.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/BaseExecuteRouting.java @@ -25,6 +25,7 @@ import software.amazon.jdbc.JdbcCallable; import software.amazon.jdbc.PluginService; import software.amazon.jdbc.plugin.bluegreen.BlueGreenRole; +import software.amazon.jdbc.util.storage.StorageService; public abstract class BaseExecuteRouting extends BaseRouting implements ExecuteRouting { @@ -52,6 +53,7 @@ public boolean isMatch(HostSpec hostSpec, BlueGreenRole hostRole) { final String methodName, final JdbcCallable jdbcMethodFunc, final Object[] jdbcMethodArgs, + final StorageService storageService, final PluginService pluginService, final Properties props) throws E; diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/BaseRouting.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/BaseRouting.java index b36c18404..3032b2fbc 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/BaseRouting.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/BaseRouting.java @@ -17,8 +17,8 @@ package software.amazon.jdbc.plugin.bluegreen.routing; import java.util.concurrent.TimeUnit; -import software.amazon.jdbc.PluginService; import software.amazon.jdbc.plugin.bluegreen.BlueGreenStatus; +import software.amazon.jdbc.util.storage.StorageService; public abstract class BaseRouting { @@ -29,7 +29,7 @@ protected long getNanoTime() { } @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") - protected void delay(long delayMs, BlueGreenStatus bgStatus, PluginService pluginService, String bgdId) + protected void delay(long delayMs, BlueGreenStatus bgStatus, StorageService storageService, String bgdId) throws InterruptedException { long start = System.nanoTime(); @@ -46,7 +46,7 @@ protected void delay(long delayMs, BlueGreenStatus bgStatus, PluginService plugi } } while ( // check if status reference is changed - bgStatus == pluginService.getStatus(BlueGreenStatus.class, bgdId) + bgStatus == storageService.get(BlueGreenStatus.class, bgdId) && System.nanoTime() < end && !Thread.currentThread().isInterrupted()); } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/CloseConnectionExecuteRouting.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/CloseConnectionExecuteRouting.java index 70b236e1b..04e1055a7 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/CloseConnectionExecuteRouting.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/CloseConnectionExecuteRouting.java @@ -27,6 +27,7 @@ import software.amazon.jdbc.plugin.bluegreen.BlueGreenRole; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.WrapperUtils; +import software.amazon.jdbc.util.storage.StorageService; // Close current connection. public class CloseConnectionExecuteRouting extends BaseExecuteRouting { @@ -46,6 +47,7 @@ public Optional apply( final String methodName, final JdbcCallable jdbcMethodFunc, final Object[] jdbcMethodArgs, + final StorageService storageService, final PluginService pluginService, final Properties props) throws E { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/ConnectRouting.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/ConnectRouting.java index 53c788916..7125453bb 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/ConnectRouting.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/ConnectRouting.java @@ -24,6 +24,7 @@ import software.amazon.jdbc.JdbcCallable; import software.amazon.jdbc.PluginService; import software.amazon.jdbc.plugin.bluegreen.BlueGreenRole; +import software.amazon.jdbc.util.storage.StorageService; public interface ConnectRouting { boolean isMatch(HostSpec hostSpec, BlueGreenRole hostRole); @@ -34,5 +35,6 @@ Connection apply( Properties props, boolean isInitialConnection, JdbcCallable connectFunc, + StorageService storageService, PluginService pluginService) throws SQLException; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/ExecuteRouting.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/ExecuteRouting.java index 54ea20b5c..39bedfb72 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/ExecuteRouting.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/ExecuteRouting.java @@ -24,6 +24,7 @@ import software.amazon.jdbc.JdbcCallable; import software.amazon.jdbc.PluginService; import software.amazon.jdbc.plugin.bluegreen.BlueGreenRole; +import software.amazon.jdbc.util.storage.StorageService; public interface ExecuteRouting { boolean isMatch(HostSpec hostSpec, BlueGreenRole hostRole); @@ -36,6 +37,7 @@ public interface ExecuteRouting { final String methodName, final JdbcCallable jdbcMethodFunc, final Object[] jdbcMethodArgs, + final StorageService storageService, final PluginService pluginService, final Properties props) throws E; } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/PassThroughConnectRouting.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/PassThroughConnectRouting.java index 97e6470b5..361898056 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/PassThroughConnectRouting.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/PassThroughConnectRouting.java @@ -26,6 +26,7 @@ import software.amazon.jdbc.JdbcCallable; import software.amazon.jdbc.PluginService; import software.amazon.jdbc.plugin.bluegreen.BlueGreenRole; +import software.amazon.jdbc.util.storage.StorageService; public class PassThroughConnectRouting extends BaseConnectRouting { @@ -37,9 +38,8 @@ public PassThroughConnectRouting(@Nullable String hostAndPort, @Nullable BlueGre @Override public Connection apply(ConnectionPlugin plugin, HostSpec hostSpec, Properties props, boolean isInitialConnection, - JdbcCallable connectFunc, PluginService pluginService) - throws SQLException { - + JdbcCallable connectFunc, StorageService storageService, + PluginService pluginService) throws SQLException { return connectFunc.call(); } } diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/PassThroughExecuteRouting.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/PassThroughExecuteRouting.java index b0fd5147e..ab8b90c86 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/PassThroughExecuteRouting.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/PassThroughExecuteRouting.java @@ -25,6 +25,7 @@ import software.amazon.jdbc.JdbcCallable; import software.amazon.jdbc.PluginService; import software.amazon.jdbc.plugin.bluegreen.BlueGreenRole; +import software.amazon.jdbc.util.storage.StorageService; // Normally execute JDBC call. public class PassThroughExecuteRouting extends BaseExecuteRouting { @@ -44,6 +45,7 @@ public PassThroughExecuteRouting(@Nullable String hostAndPort, @Nullable BlueGre final String methodName, final JdbcCallable jdbcMethodFunc, final Object[] jdbcMethodArgs, + final StorageService storageService, final PluginService pluginService, final Properties props) throws E { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/RejectConnectRouting.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/RejectConnectRouting.java index cd89667b2..87720e128 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/RejectConnectRouting.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/RejectConnectRouting.java @@ -27,6 +27,7 @@ import software.amazon.jdbc.PluginService; import software.amazon.jdbc.plugin.bluegreen.BlueGreenRole; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.storage.StorageService; // Reject an attempt to open a new connection. public class RejectConnectRouting extends BaseConnectRouting { @@ -39,7 +40,8 @@ public RejectConnectRouting(@Nullable String hostAndPort, @Nullable BlueGreenRol @Override public Connection apply(ConnectionPlugin plugin, HostSpec hostSpec, Properties props, boolean isInitialConnection, - JdbcCallable connectFunc, PluginService pluginService) throws SQLException { + JdbcCallable connectFunc, StorageService storageService, + PluginService pluginService) throws SQLException { LOGGER.finest(() -> Messages.get("bgd.inProgressCantConnect")); throw new SQLException(Messages.get("bgd.inProgressCantConnect")); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SubstituteConnectRouting.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SubstituteConnectRouting.java index ddbe7ba74..e06a649fc 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SubstituteConnectRouting.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SubstituteConnectRouting.java @@ -35,6 +35,7 @@ import software.amazon.jdbc.util.PropertyUtils; import software.amazon.jdbc.util.RdsUtils; import software.amazon.jdbc.util.Utils; +import software.amazon.jdbc.util.storage.StorageService; /** * Open a new connection to a provided substitute host. @@ -60,7 +61,8 @@ public SubstituteConnectRouting(@Nullable String hostAndPort, @Nullable BlueGree @Override public Connection apply(ConnectionPlugin plugin, HostSpec hostSpec, Properties props, boolean isInitialConnection, - JdbcCallable connectFunc, PluginService pluginService) throws SQLException { + JdbcCallable connectFunc, StorageService storageService, PluginService pluginService) + throws SQLException { if (!RDS_UTILS.isIP(this.substituteHostSpec.getHost())) { return pluginService.connect(this.substituteHostSpec, props, plugin); diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SuspendConnectRouting.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SuspendConnectRouting.java index 6087330ed..4991e1125 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SuspendConnectRouting.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SuspendConnectRouting.java @@ -33,6 +33,7 @@ import software.amazon.jdbc.plugin.bluegreen.BlueGreenRole; import software.amazon.jdbc.plugin.bluegreen.BlueGreenStatus; import software.amazon.jdbc.util.Messages; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryTraceLevel; @@ -59,16 +60,16 @@ public Connection apply( Properties props, boolean isInitialConnection, JdbcCallable connectFunc, + StorageService storageService, PluginService pluginService) throws SQLException { LOGGER.finest(() -> Messages.get("bgd.inProgressHoldConnect")); - TelemetryFactory telemetryFactory = pluginService.getTelemetryFactory(); TelemetryContext telemetryContext = telemetryFactory.openTelemetryContext(TELEMETRY_SWITCHOVER, TelemetryTraceLevel.NESTED); - BlueGreenStatus bgStatus = pluginService.getStatus(BlueGreenStatus.class, this.bgdId); + BlueGreenStatus bgStatus = storageService.get(BlueGreenStatus.class, this.bgdId); long timeoutNano = TimeUnit.MILLISECONDS.toNanos(BG_CONNECT_TIMEOUT.getLong(props)); long holdStartTime = this.getNanoTime(); @@ -80,13 +81,13 @@ public Connection apply( && bgStatus.getCurrentPhase() == BlueGreenPhase.IN_PROGRESS) { try { - this.delay(SLEEP_TIME_MS, bgStatus, pluginService, this.bgdId); + this.delay(SLEEP_TIME_MS, bgStatus, storageService, this.bgdId); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } - bgStatus = pluginService.getStatus(BlueGreenStatus.class, this.bgdId); + bgStatus = storageService.get(BlueGreenStatus.class, this.bgdId); } if (bgStatus != null && bgStatus.getCurrentPhase() == BlueGreenPhase.IN_PROGRESS) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SuspendExecuteRouting.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SuspendExecuteRouting.java index cdeb2db0d..18a4b74bd 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SuspendExecuteRouting.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SuspendExecuteRouting.java @@ -33,6 +33,7 @@ import software.amazon.jdbc.plugin.bluegreen.BlueGreenStatus; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.WrapperUtils; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryTraceLevel; @@ -61,16 +62,16 @@ public SuspendExecuteRouting(@Nullable String hostAndPort, @Nullable BlueGreenRo final String methodName, final JdbcCallable jdbcMethodFunc, final Object[] jdbcMethodArgs, + final StorageService storageService, final PluginService pluginService, final Properties props) throws E { LOGGER.finest(Messages.get("bgd.inProgressSuspendMethod", new Object[] {methodName})); - TelemetryFactory telemetryFactory = pluginService.getTelemetryFactory(); TelemetryContext telemetryContext = telemetryFactory.openTelemetryContext(TELEMETRY_SWITCHOVER, TelemetryTraceLevel.NESTED); - BlueGreenStatus bgStatus = pluginService.getStatus(BlueGreenStatus.class, this.bgdId); + BlueGreenStatus bgStatus = storageService.get(BlueGreenStatus.class, this.bgdId); long timeoutNano = TimeUnit.MILLISECONDS.toNanos(BG_CONNECT_TIMEOUT.getLong(props)); long holdStartTime = this.getNanoTime(); @@ -83,13 +84,13 @@ public SuspendExecuteRouting(@Nullable String hostAndPort, @Nullable BlueGreenRo && bgStatus.getCurrentPhase() == BlueGreenPhase.IN_PROGRESS) { try { - this.delay(SLEEP_TIME_MS, bgStatus, pluginService, this.bgdId); + this.delay(SLEEP_TIME_MS, bgStatus, storageService, this.bgdId); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } - bgStatus = pluginService.getStatus(BlueGreenStatus.class, this.bgdId); + bgStatus = storageService.get(BlueGreenStatus.class, this.bgdId); } if (bgStatus != null && bgStatus.getCurrentPhase() == BlueGreenPhase.IN_PROGRESS) { diff --git a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SuspendUntilCorrespondingNodeFoundConnectRouting.java b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SuspendUntilCorrespondingNodeFoundConnectRouting.java index 27bf00ccf..bb384b73d 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SuspendUntilCorrespondingNodeFoundConnectRouting.java +++ b/wrapper/src/main/java/software/amazon/jdbc/plugin/bluegreen/routing/SuspendUntilCorrespondingNodeFoundConnectRouting.java @@ -34,6 +34,7 @@ import software.amazon.jdbc.plugin.bluegreen.BlueGreenStatus; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.Pair; +import software.amazon.jdbc.util.storage.StorageService; import software.amazon.jdbc.util.telemetry.TelemetryContext; import software.amazon.jdbc.util.telemetry.TelemetryFactory; import software.amazon.jdbc.util.telemetry.TelemetryTraceLevel; @@ -62,17 +63,16 @@ public Connection apply( Properties props, boolean isInitialConnection, JdbcCallable connectFunc, - PluginService pluginService) - throws SQLException { + StorageService storageService, + PluginService pluginService) throws SQLException { LOGGER.finest(() -> Messages.get("bgd.waitConnectUntilCorrespondingNodeFound", new Object[] {hostSpec.getHost()})); - TelemetryFactory telemetryFactory = pluginService.getTelemetryFactory(); TelemetryContext telemetryContext = telemetryFactory.openTelemetryContext(TELEMETRY_SWITCHOVER, TelemetryTraceLevel.NESTED); - BlueGreenStatus bgStatus = pluginService.getStatus(BlueGreenStatus.class, this.bgdId); + BlueGreenStatus bgStatus = storageService.get(BlueGreenStatus.class, this.bgdId); Pair correspondingPair = bgStatus == null ? null : bgStatus.getCorrespondingNodes().get(hostSpec.getHost()); @@ -89,13 +89,13 @@ public Connection apply( && (correspondingPair == null || correspondingPair.getValue2() == null)) { try { - this.delay(SLEEP_TIME_MS, bgStatus, pluginService, this.bgdId); + this.delay(SLEEP_TIME_MS, bgStatus, storageService, this.bgdId); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } - bgStatus = pluginService.getStatus(BlueGreenStatus.class, this.bgdId); + bgStatus = storageService.get(BlueGreenStatus.class, this.bgdId); correspondingPair = bgStatus == null ? null : bgStatus.getCorrespondingNodes().get(hostSpec.getHost()); diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java index d258cbc24..e59e8d72e 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/StorageServiceImpl.java @@ -27,6 +27,7 @@ import org.checkerframework.checker.nullness.qual.Nullable; import software.amazon.jdbc.AllowedAndBlockedHosts; import software.amazon.jdbc.hostlistprovider.Topology; +import software.amazon.jdbc.plugin.bluegreen.BlueGreenStatus; import software.amazon.jdbc.util.ExecutorFactory; import software.amazon.jdbc.util.Messages; import software.amazon.jdbc.util.events.DataAccessEvent; @@ -41,6 +42,8 @@ public class StorageServiceImpl implements StorageService { Map, Supplier>> suppliers = new HashMap<>(); suppliers.put(Topology.class, ExpirationCache::new); suppliers.put(AllowedAndBlockedHosts.class, ExpirationCache::new); + suppliers.put(BlueGreenStatus.class, + () -> new ExpirationCache<>(false, TimeUnit.MINUTES.toNanos(60), null, null)); defaultCacheSuppliers = Collections.unmodifiableMap(suppliers); } From c344c9325e78eb86fe7b34f5e26d3268354734da Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 3 Jul 2025 10:09:02 -0700 Subject: [PATCH 147/149] Add CacheItem changes --- .../java/software/amazon/jdbc/util/storage/CacheItem.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java b/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java index 1a4c2ee18..e6d38e4ae 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/storage/CacheItem.java @@ -16,6 +16,7 @@ package software.amazon.jdbc.util.storage; +import java.util.Objects; import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; @@ -86,10 +87,7 @@ protected boolean shouldCleanup() { @Override public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + item.hashCode(); - return result; + return Objects.hashCode(this.item); } @Override @@ -104,7 +102,7 @@ public boolean equals(final Object obj) { return false; } final CacheItem other = (CacheItem) obj; - return item.equals(other.item); + return Objects.equals(item, other.item); } @Override From ebf4c6c8dfffb64d5a8525c2504914258c63c6a6 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Thu, 3 Jul 2025 17:29:32 -0700 Subject: [PATCH 148/149] cleanup --- .../test/java/software/amazon/jdbc/DialectDetectionTests.java | 1 - 1 file changed, 1 deletion(-) diff --git a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java index e65ca8322..3b47f12bb 100644 --- a/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java +++ b/wrapper/src/test/java/software/amazon/jdbc/DialectDetectionTests.java @@ -61,7 +61,6 @@ public class DialectDetectionTests { private static final String MYSQL_PROTOCOL = "jdbc:mysql://"; private static final String PG_PROTOCOL = "jdbc:postgresql://"; private static final String MARIA_PROTOCOL = "jdbc:mariadb://"; - private final DialectManager dialectManager = new DialectManager(null); private final Properties props = new Properties(); private AutoCloseable closeable; @Mock private FullServicesContainer mockServicesContainer; From 305ac43b5666c22fd894344b762f149298c1a684 Mon Sep 17 00:00:00 2001 From: aaron-congo Date: Mon, 14 Jul 2025 15:05:33 -0700 Subject: [PATCH 149/149] Add MultiAzClusterTopologyMonitorImpl to the registered monitors --- .../amazon/jdbc/util/monitoring/MonitorServiceImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java index 72a7c850b..b6804f9a1 100644 --- a/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java +++ b/wrapper/src/main/java/software/amazon/jdbc/util/monitoring/MonitorServiceImpl.java @@ -36,6 +36,7 @@ import software.amazon.jdbc.dialect.Dialect; import software.amazon.jdbc.hostlistprovider.Topology; import software.amazon.jdbc.hostlistprovider.monitoring.ClusterTopologyMonitorImpl; +import software.amazon.jdbc.hostlistprovider.monitoring.MultiAzClusterTopologyMonitorImpl; import software.amazon.jdbc.targetdriverdialect.TargetDriverDialect; import software.amazon.jdbc.util.ExecutorFactory; import software.amazon.jdbc.util.Messages; @@ -62,6 +63,7 @@ public class MonitorServiceImpl implements MonitorService, EventSubscriber { TimeUnit.MINUTES.toNanos(15), TimeUnit.MINUTES.toNanos(3), recreateOnError); suppliers.put(ClusterTopologyMonitorImpl.class, () -> new CacheContainer(defaultSettings, Topology.class)); + suppliers.put(MultiAzClusterTopologyMonitorImpl.class, () -> new CacheContainer(defaultSettings, Topology.class)); defaultSuppliers = Collections.unmodifiableMap(suppliers); }