From 7ed1dc3413f2ce402bb3821abab9bb0cc513716a Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Wed, 8 Oct 2025 19:59:52 -0700 Subject: [PATCH 1/5] Add support for awaitMinPoolSize in unified test runner. JAVA-5957 --- .../TestConnectionPoolListener.java | 9 +++++ .../logging/TestLoggingInterceptor.java | 4 +++ .../com/mongodb/client/unified/Entities.java | 35 +++++++++++++++++-- .../mongodb/client/unified/UnifiedTest.java | 2 +- 4 files changed, 47 insertions(+), 3 deletions(-) diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/TestConnectionPoolListener.java b/driver-core/src/test/unit/com/mongodb/internal/connection/TestConnectionPoolListener.java index 12008cdec93..ebf385d3d89 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/TestConnectionPoolListener.java +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/TestConnectionPoolListener.java @@ -145,6 +145,15 @@ private void addEvent(final Object event) { } } + public void clearEvents() { + lock.lock(); + try { + events.clear(); + } finally { + lock.unlock(); + } + } + @Override public void connectionPoolCreated(final ConnectionPoolCreatedEvent event) { if (eventTypes.contains("poolCreatedEvent")) { diff --git a/driver-core/src/test/unit/com/mongodb/logging/TestLoggingInterceptor.java b/driver-core/src/test/unit/com/mongodb/logging/TestLoggingInterceptor.java index 785252b1902..b657eba4323 100644 --- a/driver-core/src/test/unit/com/mongodb/logging/TestLoggingInterceptor.java +++ b/driver-core/src/test/unit/com/mongodb/logging/TestLoggingInterceptor.java @@ -69,4 +69,8 @@ boolean match(final LogMessage message){ return false; } } + + public synchronized void clearMessages() { + messages.clear(); + } } diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java b/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java index 6f6e5bb66c8..03ba40b47ef 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java @@ -38,6 +38,7 @@ import com.mongodb.client.vault.ClientEncryption; import com.mongodb.connection.ClusterConnectionMode; import com.mongodb.connection.ClusterDescription; +import com.mongodb.event.ConnectionReadyEvent; import com.mongodb.event.TestServerMonitorListener; import com.mongodb.internal.connection.ServerMonitoringModeUtil; import com.mongodb.internal.connection.TestClusterListener; @@ -64,6 +65,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; @@ -82,6 +84,7 @@ import static com.mongodb.client.unified.UnifiedCrudHelper.asReadPreference; import static com.mongodb.client.unified.UnifiedCrudHelper.asWriteConcern; import static com.mongodb.internal.connection.AbstractConnectionPoolTest.waitForPoolAsyncWorkManagerStart; +import static java.lang.String.format; import static java.lang.System.getenv; import static java.util.Arrays.asList; import static org.junit.Assume.assumeTrue; @@ -90,7 +93,8 @@ public final class Entities { private static final Set SUPPORTED_CLIENT_ENTITY_OPTIONS = new HashSet<>( asList( "id", "autoEncryptOpts", "uriOptions", "serverApi", "useMultipleMongoses", "storeEventsAsEntities", - "observeEvents", "observeLogMessages", "observeSensitiveCommands", "ignoreCommandMonitoringEvents")); + "observeEvents", "observeLogMessages", "observeSensitiveCommands", "ignoreCommandMonitoringEvents", + "awaitMinPoolSizeMS")); private final Set entityNames = new HashSet<>(); private final Map threads = new HashMap<>(); private final Map>> tasks = new HashMap<>(); @@ -306,6 +310,7 @@ private void initClient(final BsonDocument entity, final String id, throw new UnsupportedOperationException("Client entity contains unsupported options: " + entity.keySet() + ". Supported options are " + SUPPORTED_CLIENT_ENTITY_OPTIONS); } + boolean waitForMinPoolSizeToPopulate = isWaitForMinPoolSizeToPopulate(entity); MongoClientSettings.Builder clientSettingsBuilder; if (entity.getBoolean("useMultipleMongoses", BsonBoolean.FALSE).getValue() && (isSharded() || isLoadBalanced())) { assumeTrue("Multiple mongos connection string not available for sharded cluster", @@ -331,6 +336,9 @@ private void initClient(final BsonDocument entity, final String id, if (entity.containsKey("observeEvents")) { List observeEvents = entity.getArray("observeEvents").stream() .map(type -> type.asString().getValue()).collect(Collectors.toList()); + if (waitForMinPoolSizeToPopulate) { + observeEvents.add("connectionReadyEvent"); + } List ignoreCommandMonitoringEvents = entity .getArray("ignoreCommandMonitoringEvents", new BsonArray()).stream() .map(type -> type.asString().getValue()).collect(Collectors.toList()); @@ -341,7 +349,6 @@ private void initClient(final BsonDocument entity, final String id, null); clientSettingsBuilder.addCommandListener(testCommandListener); putEntity(id + "-command-listener", testCommandListener, clientCommandListeners); - TestConnectionPoolListener testConnectionPoolListener = new TestConnectionPoolListener(observeEvents); clientSettingsBuilder.applyToConnectionPoolSettings(builder -> builder.addConnectionPoolListener(testConnectionPoolListener)); @@ -580,11 +587,35 @@ private void initClient(final BsonDocument entity, final String id, } putEntity(id, mongoClientSupplier.apply(clientSettings), clients); + if (waitForMinPoolSizeToPopulate) { + waitForMinPoolSizeToPopulate(entity, id, clientSettings); + } if (waitForPoolAsyncWorkManagerStart) { waitForPoolAsyncWorkManagerStart(); } } + private void waitForMinPoolSizeToPopulate(final BsonDocument entity, final String id, final MongoClientSettings clientSettings) { + int minSize = clientSettings.getConnectionPoolSettings().getMinSize(); + int awaitMinPoolSizeMS = entity.getInt32("awaitMinPoolSizeMS").getValue(); + TestConnectionPoolListener testConnectionPoolListener = getConnectionPoolListener(id); + try { + testConnectionPoolListener.waitForEvent(ConnectionReadyEvent.class, minSize, awaitMinPoolSizeMS, TimeUnit.MILLISECONDS); +// testConnectionPoolListener.clearEvents(); +// getClientLoggingInterceptor(id).clearMessages(); + } catch (TimeoutException | InterruptedException e) { + throw new RuntimeException(format("Error waiting for awaitMinPoolSizeMS [%s] to establish minPoolSize [%s] connections", + awaitMinPoolSizeMS, minSize)); + } + } + + private static boolean isWaitForMinPoolSizeToPopulate(final BsonDocument clientEntity) { + int minPoolSize = clientEntity.getDocument("uriOptions", new BsonDocument()) + .get("minPoolSize", new BsonInt32(0)) + .asInt32().getValue(); + return minPoolSize != 0 && clientEntity.containsKey("awaitMinPoolSizeMS"); + } + private static LogMessage.Component toComponent(final Map.Entry entry) { String componentName = entry.getKey(); return LogMessage.Component.of(componentName); diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java index 79b2a9c9da9..62d641d12d7 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTest.java @@ -109,7 +109,7 @@ public abstract class UnifiedTest { private static final Set PRESTART_POOL_ASYNC_WORK_MANAGER_FILE_DESCRIPTIONS = Collections.singleton( "wait queue timeout errors include details about checked out connections"); - private static final String MAX_SUPPORTED_SCHEMA_VERSION = "1.25"; + private static final String MAX_SUPPORTED_SCHEMA_VERSION = "1.26"; private static final List MAX_SUPPORTED_SCHEMA_VERSION_COMPONENTS = Arrays.stream(MAX_SUPPORTED_SCHEMA_VERSION.split("\\.")) .map(Integer::parseInt) .collect(Collectors.toList()); From 60b31b046cccaa7f9b75e888e1653fd6abdeb21b Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Thu, 9 Oct 2025 12:28:32 -0700 Subject: [PATCH 2/5] Reorder waitForMinPoolSizeToPopulate call. --- .../functional/com/mongodb/client/unified/Entities.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java b/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java index 03ba40b47ef..df087ed307c 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java @@ -587,12 +587,12 @@ private void initClient(final BsonDocument entity, final String id, } putEntity(id, mongoClientSupplier.apply(clientSettings), clients); - if (waitForMinPoolSizeToPopulate) { - waitForMinPoolSizeToPopulate(entity, id, clientSettings); - } if (waitForPoolAsyncWorkManagerStart) { waitForPoolAsyncWorkManagerStart(); } + if (waitForMinPoolSizeToPopulate) { + waitForMinPoolSizeToPopulate(entity, id, clientSettings); + } } private void waitForMinPoolSizeToPopulate(final BsonDocument entity, final String id, final MongoClientSettings clientSettings) { From 7bcb68abae31ba2aeafea5cc59ed8945f83d1856 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Fri, 10 Oct 2025 11:08:15 -0700 Subject: [PATCH 3/5] Ignore non-spec-compliant tests. --- .../connection/TestConnectionPoolListener.java | 9 --------- .../com/mongodb/logging/TestLoggingInterceptor.java | 4 ---- .../com/mongodb/client/unified/Entities.java | 11 +++++++++-- .../client/unified/UnifiedTestModifications.java | 13 ++++++++++++- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/driver-core/src/test/unit/com/mongodb/internal/connection/TestConnectionPoolListener.java b/driver-core/src/test/unit/com/mongodb/internal/connection/TestConnectionPoolListener.java index ebf385d3d89..12008cdec93 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/connection/TestConnectionPoolListener.java +++ b/driver-core/src/test/unit/com/mongodb/internal/connection/TestConnectionPoolListener.java @@ -145,15 +145,6 @@ private void addEvent(final Object event) { } } - public void clearEvents() { - lock.lock(); - try { - events.clear(); - } finally { - lock.unlock(); - } - } - @Override public void connectionPoolCreated(final ConnectionPoolCreatedEvent event) { if (eventTypes.contains("poolCreatedEvent")) { diff --git a/driver-core/src/test/unit/com/mongodb/logging/TestLoggingInterceptor.java b/driver-core/src/test/unit/com/mongodb/logging/TestLoggingInterceptor.java index b657eba4323..785252b1902 100644 --- a/driver-core/src/test/unit/com/mongodb/logging/TestLoggingInterceptor.java +++ b/driver-core/src/test/unit/com/mongodb/logging/TestLoggingInterceptor.java @@ -69,8 +69,4 @@ boolean match(final LogMessage message){ return false; } } - - public synchronized void clearMessages() { - messages.clear(); - } } diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java b/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java index df087ed307c..444e01ffb93 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java @@ -600,9 +600,16 @@ private void waitForMinPoolSizeToPopulate(final BsonDocument entity, final Strin int awaitMinPoolSizeMS = entity.getInt32("awaitMinPoolSizeMS").getValue(); TestConnectionPoolListener testConnectionPoolListener = getConnectionPoolListener(id); try { + /* + From the spec: + Any CMAP and SDAM event/log listeners configured on the client should ignore any events that occur before the pool is being + populated. + + Spec advices SHOULD, not MUST. + Currently, no flaky or racy behavior is caused by not cleaning pre-pool-population CMAP and SDAM events. + In case any race behaviour is observed, consider cleaning events. + */ testConnectionPoolListener.waitForEvent(ConnectionReadyEvent.class, minSize, awaitMinPoolSizeMS, TimeUnit.MILLISECONDS); -// testConnectionPoolListener.clearEvents(); -// getClientLoggingInterceptor(id).clearMessages(); } catch (TimeoutException | InterruptedException e) { throw new RuntimeException(format("Error waiting for awaitMinPoolSizeMS [%s] to establish minPoolSize [%s] connections", awaitMinPoolSizeMS, minSize)); diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTestModifications.java b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTestModifications.java index 327cc3f3da8..d1408ac29a7 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTestModifications.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/UnifiedTestModifications.java @@ -60,7 +60,18 @@ public static void applyCustomizations(final TestDef def) { .file("client-side-encryption/tests/unified", "client bulkWrite with queryable encryption"); // client-side-operation-timeout (CSOT) - + /* + As to the background connection pooling section: + timeoutMS set at the MongoClient level MUST be used as the timeout for all commands sent as part of the handshake. + We first configure a failpoint to block all hello/isMaster commands for 50 ms, then set timeoutMS = 10 ms on MongoClient + and wait for awaitMinPoolSize = 1000. So that means the background thread tries to populate connections under a 10ms timeout + cap while the failpoint blocks for 50ms, so all attempts effectively fail. + */ + def.skipAccordingToSpec("background connection pooling section") + .test("client-side-operations-timeout", "timeoutMS behaves correctly during command execution", + "short-circuit is not enabled with only 1 RTT measurement") + .test("client-side-operations-timeout", "timeoutMS behaves correctly during command execution", + "command is not sent if RTT is greater than timeoutMS"); def.skipNoncompliantReactive("No good way to fulfill tryNext() requirement with a Publisher") .test("client-side-operations-timeout", "timeoutMS behaves correctly for tailable awaitData cursors", "apply remaining timeoutMS if less than maxAwaitTimeMS"); From 16a65fa3e407107e2305e335ad6cc2f1b777e472 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Fri, 10 Oct 2025 11:12:17 -0700 Subject: [PATCH 4/5] Adjust comment. --- .../com/mongodb/client/unified/Entities.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java b/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java index 444e01ffb93..c02433e20f5 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java @@ -600,14 +600,12 @@ private void waitForMinPoolSizeToPopulate(final BsonDocument entity, final Strin int awaitMinPoolSizeMS = entity.getInt32("awaitMinPoolSizeMS").getValue(); TestConnectionPoolListener testConnectionPoolListener = getConnectionPoolListener(id); try { - /* + /* From the spec: - Any CMAP and SDAM event/log listeners configured on the client should ignore any events that occur before the pool is being - populated. + Any CMAP and SDAM event/log listeners configured on the client SHOULD ignore any events that occur before the pool is populated. - Spec advices SHOULD, not MUST. - Currently, no flaky or racy behavior is caused by not cleaning pre-pool-population CMAP and SDAM events. - In case any race behaviour is observed, consider cleaning events. + Currently, no flaky or racy behavior is caused by not clearing, pre-pool-population CMAP and SDAM events. + If any race behavior is observed, consider clearing events. */ testConnectionPoolListener.waitForEvent(ConnectionReadyEvent.class, minSize, awaitMinPoolSizeMS, TimeUnit.MILLISECONDS); } catch (TimeoutException | InterruptedException e) { From 111bf60d8a0d291058fd8c59bb0748636ae4f7b1 Mon Sep 17 00:00:00 2001 From: "slav.babanin" Date: Fri, 10 Oct 2025 11:13:07 -0700 Subject: [PATCH 5/5] Adjust comment. --- .../test/functional/com/mongodb/client/unified/Entities.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java b/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java index c02433e20f5..2e6c5f0b54c 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java +++ b/driver-sync/src/test/functional/com/mongodb/client/unified/Entities.java @@ -604,7 +604,7 @@ private void waitForMinPoolSizeToPopulate(final BsonDocument entity, final Strin From the spec: Any CMAP and SDAM event/log listeners configured on the client SHOULD ignore any events that occur before the pool is populated. - Currently, no flaky or racy behavior is caused by not clearing, pre-pool-population CMAP and SDAM events. + Currently, no flaky or racy behavior is caused by not clearing pre-pool-population CMAP and SDAM events. If any race behavior is observed, consider clearing events. */ testConnectionPoolListener.waitForEvent(ConnectionReadyEvent.class, minSize, awaitMinPoolSizeMS, TimeUnit.MILLISECONDS);