diff --git a/driver-core/src/test/functional/com/mongodb/ClusterFixture.java b/driver-core/src/test/functional/com/mongodb/ClusterFixture.java index 6bbf9233cb1..30a28027002 100644 --- a/driver-core/src/test/functional/com/mongodb/ClusterFixture.java +++ b/driver-core/src/test/functional/com/mongodb/ClusterFixture.java @@ -475,7 +475,9 @@ private static Cluster createCluster(final MongoCredential credential, final Str } private static Cluster createCluster(final ConnectionString connectionString, final StreamFactory streamFactory) { - MongoClientSettings mongoClientSettings = MongoClientSettings.builder().applyConnectionString(connectionString).build(); + MongoClientSettings mongoClientSettings = MongoClientSettings.builder().applyConnectionString(connectionString) + .applyToServerSettings(builder -> builder.heartbeatFrequency(1, SECONDS).minHeartbeatFrequency(1, MILLISECONDS)) + .build(); return new DefaultClusterFactory().createCluster(mongoClientSettings.getClusterSettings(), mongoClientSettings.getServerSettings(), mongoClientSettings.getConnectionPoolSettings(), @@ -570,7 +572,7 @@ public static ServerAddress getSecondary() { return serverDescriptions.get(0).getAddress(); } - public static void sleep(final int sleepMS) { + public static void sleep(final long sleepMS) { try { Thread.sleep(sleepMS); } catch (InterruptedException e) { diff --git a/driver-core/src/test/unit/com/mongodb/internal/TimeoutContextTest.java b/driver-core/src/test/unit/com/mongodb/internal/TimeoutContextTest.java index 130d408076e..545dc4b5458 100644 --- a/driver-core/src/test/unit/com/mongodb/internal/TimeoutContextTest.java +++ b/driver-core/src/test/unit/com/mongodb/internal/TimeoutContextTest.java @@ -331,9 +331,10 @@ static Stream shouldChooseTimeoutMsWhenItIsLessThenConnectTimeoutMS() ); } - @ParameterizedTest - @MethodSource @DisplayName("should choose timeoutMS when timeoutMS is less than connectTimeoutMS") + @ParameterizedTest(name = "should choose timeoutMS when timeoutMS is less than connectTimeoutMS. " + + "Parameters: connectTimeoutMS: {0}, timeoutMS: {1}, expected: {2}") + @MethodSource void shouldChooseTimeoutMsWhenItIsLessThenConnectTimeoutMS(final Long connectTimeoutMS, final Long timeoutMS, final long expected) { @@ -345,7 +346,7 @@ void shouldChooseTimeoutMsWhenItIsLessThenConnectTimeoutMS(final Long connectTim 0)); long calculatedTimeoutMS = timeoutContext.getConnectTimeoutMs(); - assertTrue(expected - calculatedTimeoutMS <= 1); + assertTrue(expected - calculatedTimeoutMS <= 2); } private TimeoutContextTest() { diff --git a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/ClientSideOperationTimeoutProseTest.java b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/ClientSideOperationTimeoutProseTest.java index fedd8e0efc4..aa1b995ebce 100644 --- a/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/ClientSideOperationTimeoutProseTest.java +++ b/driver-reactive-streams/src/test/functional/com/mongodb/reactivestreams/client/ClientSideOperationTimeoutProseTest.java @@ -16,16 +16,15 @@ package com.mongodb.reactivestreams.client; -import com.mongodb.ClusterFixture; import com.mongodb.MongoClientSettings; import com.mongodb.MongoCommandException; +import com.mongodb.MongoExecutionTimeoutException; import com.mongodb.MongoNamespace; import com.mongodb.MongoOperationTimeoutException; import com.mongodb.MongoSocketReadTimeoutException; import com.mongodb.ReadPreference; import com.mongodb.WriteConcern; import com.mongodb.client.AbstractClientSideOperationsTimeoutProseTest; -import com.mongodb.client.model.CreateCollectionOptions; import com.mongodb.client.model.changestream.FullDocument; import com.mongodb.event.CommandFailedEvent; import com.mongodb.event.CommandStartedEvent; @@ -61,6 +60,7 @@ import static com.mongodb.ClusterFixture.isDiscoverableReplicaSet; import static com.mongodb.ClusterFixture.serverVersionAtLeast; import static com.mongodb.ClusterFixture.sleep; +import static com.mongodb.assertions.Assertions.assertTrue; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; @@ -108,7 +108,6 @@ protected boolean isAsync() { @Override public void testGridFSUploadViaOpenUploadStreamTimeout() { assumeTrue(serverVersionAtLeast(4, 4)); - long rtt = ClusterFixture.getPrimaryRTT(); //given collectionHelper.runAdminCommand("{" @@ -117,12 +116,12 @@ public void testGridFSUploadViaOpenUploadStreamTimeout() { + " data: {" + " failCommands: [\"insert\"]," + " blockConnection: true," - + " blockTimeMS: " + (rtt + 405) + + " blockTimeMS: " + 405 + " }" + "}"); try (MongoClient client = createReactiveClient(getMongoClientSettingsBuilder() - .timeout(rtt + 400, TimeUnit.MILLISECONDS))) { + .timeout(withRttAdjustment(400, 2), TimeUnit.MILLISECONDS))) { MongoDatabase database = client.getDatabase(gridFsFileNamespace.getDatabaseName()); GridFSBucket gridFsBucket = createReaciveGridFsBucket(database, GRID_FS_BUCKET_NAME); @@ -151,7 +150,11 @@ public void testGridFSUploadViaOpenUploadStreamTimeout() { Throwable commandError = onErrorEvents.get(0); Throwable operationTimeoutErrorCause = commandError.getCause(); assertInstanceOf(MongoOperationTimeoutException.class, commandError); - assertInstanceOf(MongoSocketReadTimeoutException.class, operationTimeoutErrorCause); + assertTrue(operationTimeoutErrorCause instanceof MongoSocketReadTimeoutException + || operationTimeoutErrorCause instanceof MongoExecutionTimeoutException, + "Expected operationTimeoutErrorCause to be either MongoSocketReadTimeoutException" + + " or MongoExecutionTimeoutException, but was: " + + operationTimeoutErrorCause.getClass().getName()); CommandFailedEvent chunkInsertFailedEvent = commandListener.getCommandFailedEvent("insert"); assertNotNull(chunkInsertFailedEvent); @@ -164,7 +167,6 @@ public void testGridFSUploadViaOpenUploadStreamTimeout() { @Override public void testAbortingGridFsUploadStreamTimeout() throws ExecutionException, InterruptedException, TimeoutException { assumeTrue(serverVersionAtLeast(4, 4)); - long rtt = ClusterFixture.getPrimaryRTT(); //given CompletableFuture droppedErrorFuture = new CompletableFuture<>(); @@ -176,12 +178,12 @@ public void testAbortingGridFsUploadStreamTimeout() throws ExecutionException, I + " data: {" + " failCommands: [\"delete\"]," + " blockConnection: true," - + " blockTimeMS: " + (rtt + 405) + + " blockTimeMS: " + 405 + " }" + "}"); try (MongoClient client = createReactiveClient(getMongoClientSettingsBuilder() - .timeout(rtt + 400, TimeUnit.MILLISECONDS))) { + .timeout(withRttAdjustment(400, 2), TimeUnit.MILLISECONDS))) { MongoDatabase database = client.getDatabase(gridFsFileNamespace.getDatabaseName()); GridFSBucket gridFsBucket = createReaciveGridFsBucket(database, GRID_FS_BUCKET_NAME); @@ -207,7 +209,11 @@ public void testAbortingGridFsUploadStreamTimeout() throws ExecutionException, I Throwable operationTimeoutErrorCause = commandError.getCause(); assertInstanceOf(MongoOperationTimeoutException.class, commandError); - assertInstanceOf(MongoSocketReadTimeoutException.class, operationTimeoutErrorCause); + assertTrue(operationTimeoutErrorCause instanceof MongoSocketReadTimeoutException + || operationTimeoutErrorCause instanceof MongoExecutionTimeoutException, + "Expected operationTimeoutErrorCause to be either MongoSocketReadTimeoutException" + + " or MongoExecutionTimeoutException, but was: " + + operationTimeoutErrorCause.getClass().getName()); CommandFailedEvent deleteFailedEvent = commandListener.getCommandFailedEvent("delete"); assertNotNull(deleteFailedEvent); @@ -228,9 +234,8 @@ public void testTimeoutMSAppliesToFullResumeAttemptInNextCall() { assumeTrue(isDiscoverableReplicaSet()); //given - long rtt = ClusterFixture.getPrimaryRTT(); try (MongoClient client = createReactiveClient(getMongoClientSettingsBuilder() - .timeout(rtt + 500, TimeUnit.MILLISECONDS))) { + .timeout(withRttAdjustment(500), TimeUnit.MILLISECONDS))) { MongoNamespace namespace = generateNamespace(); MongoCollection collection = client.getDatabase(namespace.getDatabaseName()) @@ -282,9 +287,8 @@ public void testTimeoutMSAppliedToInitialAggregate() { assumeTrue(isDiscoverableReplicaSet()); //given - long rtt = ClusterFixture.getPrimaryRTT(); try (MongoClient client = createReactiveClient(getMongoClientSettingsBuilder() - .timeout(rtt + 200, TimeUnit.MILLISECONDS))) { + .timeout(200, TimeUnit.MILLISECONDS))) { MongoNamespace namespace = generateNamespace(); MongoCollection collection = client.getDatabase(namespace.getDatabaseName()) @@ -299,7 +303,7 @@ public void testTimeoutMSAppliedToInitialAggregate() { + " data: {" + " failCommands: [\"aggregate\" ]," + " blockConnection: true," - + " blockTimeMS: " + (rtt + 201) + + " blockTimeMS: " + 201 + " }" + "}"); @@ -330,13 +334,10 @@ public void testTimeoutMsRefreshedForGetMoreWhenMaxAwaitTimeMsNotSet() { //given BsonTimestamp startTime = new BsonTimestamp((int) Instant.now().getEpochSecond(), 0); - collectionHelper.create(namespace.getCollectionName(), new CreateCollectionOptions()); sleep(2000); - - long rtt = ClusterFixture.getPrimaryRTT(); try (MongoClient client = createReactiveClient(getMongoClientSettingsBuilder() - .timeout(rtt + 300, TimeUnit.MILLISECONDS))) { + .timeout(withRttAdjustment(500), TimeUnit.MILLISECONDS))) { MongoCollection collection = client.getDatabase(namespace.getDatabaseName()) .getCollection(namespace.getCollectionName()).withReadPreference(ReadPreference.primary()); @@ -347,7 +348,7 @@ public void testTimeoutMsRefreshedForGetMoreWhenMaxAwaitTimeMsNotSet() { + " data: {" + " failCommands: [\"getMore\", \"aggregate\"]," + " blockConnection: true," - + " blockTimeMS: " + (rtt + 200) + + " blockTimeMS: " + 200 + " }" + "}"); @@ -398,12 +399,10 @@ public void testTimeoutMsRefreshedForGetMoreWhenMaxAwaitTimeMsSet() { //given BsonTimestamp startTime = new BsonTimestamp((int) Instant.now().getEpochSecond(), 0); - collectionHelper.create(namespace.getCollectionName(), new CreateCollectionOptions()); sleep(2000); - long rtt = ClusterFixture.getPrimaryRTT(); try (MongoClient client = createReactiveClient(getMongoClientSettingsBuilder() - .timeout(rtt + 300, TimeUnit.MILLISECONDS))) { + .timeout(withRttAdjustment(500), TimeUnit.MILLISECONDS))) { MongoCollection collection = client.getDatabase(namespace.getDatabaseName()) .getCollection(namespace.getCollectionName()) @@ -415,7 +414,7 @@ public void testTimeoutMsRefreshedForGetMoreWhenMaxAwaitTimeMsSet() { + " data: {" + " failCommands: [\"aggregate\", \"getMore\"]," + " blockConnection: true," - + " blockTimeMS: " + (rtt + 200) + + " blockTimeMS: " + 200 + " }" + "}"); @@ -458,9 +457,8 @@ public void testTimeoutMsISHonoredForNnextOperationWhenSeveralGetMoreExecutedInt assumeTrue(isDiscoverableReplicaSet()); //given - long rtt = ClusterFixture.getPrimaryRTT(); try (MongoClient client = createReactiveClient(getMongoClientSettingsBuilder() - .timeout(rtt + 2500, TimeUnit.MILLISECONDS))) { + .timeout(withRttAdjustment(2500), TimeUnit.MILLISECONDS))) { MongoCollection collection = client.getDatabase(namespace.getDatabaseName()) .getCollection(namespace.getCollectionName()).withReadPreference(ReadPreference.primary()); diff --git a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideOperationsTimeoutProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideOperationsTimeoutProseTest.java index c1f2f88c1b4..ea3c82d9510 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideOperationsTimeoutProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/AbstractClientSideOperationsTimeoutProseTest.java @@ -51,11 +51,8 @@ import com.mongodb.internal.connection.TestCommandListener; import com.mongodb.internal.connection.TestConnectionPoolListener; import com.mongodb.test.FlakyTest; -import org.bson.BsonArray; -import org.bson.BsonBoolean; import org.bson.BsonDocument; import org.bson.BsonInt32; -import org.bson.BsonString; import org.bson.BsonTimestamp; import org.bson.Document; import org.bson.codecs.BsonDocumentCodec; @@ -251,7 +248,6 @@ public void testBlockingIterationMethodsChangeStream() { assumeFalse(isAsync()); // Async change stream cursor is non-deterministic for cursor::next BsonTimestamp startTime = new BsonTimestamp((int) Instant.now().getEpochSecond(), 0); - collectionHelper.create(namespace.getCollectionName(), new CreateCollectionOptions()); sleep(2000); collectionHelper.insertDocuments(singletonList(BsonDocument.parse("{x: 1}")), WriteConcern.MAJORITY); @@ -293,7 +289,6 @@ public void testBlockingIterationMethodsChangeStream() { @FlakyTest(maxAttempts = 3) public void testGridFSUploadViaOpenUploadStreamTimeout() { assumeTrue(serverVersionAtLeast(4, 4)); - long rtt = ClusterFixture.getPrimaryRTT(); collectionHelper.runAdminCommand("{" + " configureFailPoint: \"failCommand\"," @@ -301,7 +296,7 @@ public void testGridFSUploadViaOpenUploadStreamTimeout() { + " data: {" + " failCommands: [\"insert\"]," + " blockConnection: true," - + " blockTimeMS: " + (rtt + 205) + + " blockTimeMS: " + 205 + " }" + "}"); @@ -309,7 +304,7 @@ public void testGridFSUploadViaOpenUploadStreamTimeout() { filesCollectionHelper.create(); try (MongoClient client = createMongoClient(getMongoClientSettingsBuilder() - .timeout(rtt + 200, TimeUnit.MILLISECONDS))) { + .timeout(withRttAdjustment(200, 2), TimeUnit.MILLISECONDS))) { MongoDatabase database = client.getDatabase(namespace.getDatabaseName()); GridFSBucket gridFsBucket = createGridFsBucket(database, GRID_FS_BUCKET_NAME); @@ -324,7 +319,6 @@ public void testGridFSUploadViaOpenUploadStreamTimeout() { @Test public void testAbortingGridFsUploadStreamTimeout() throws Throwable { assumeTrue(serverVersionAtLeast(4, 4)); - long rtt = ClusterFixture.getPrimaryRTT(); collectionHelper.runAdminCommand("{" + " configureFailPoint: \"failCommand\"," @@ -332,7 +326,7 @@ public void testAbortingGridFsUploadStreamTimeout() throws Throwable { + " data: {" + " failCommands: [\"delete\"]," + " blockConnection: true," - + " blockTimeMS: " + (rtt + 305) + + " blockTimeMS: " + 320 + " }" + "}"); @@ -340,7 +334,7 @@ public void testAbortingGridFsUploadStreamTimeout() throws Throwable { filesCollectionHelper.create(); try (MongoClient client = createMongoClient(getMongoClientSettingsBuilder() - .timeout(rtt + 300, TimeUnit.MILLISECONDS))) { + .timeout(withRttAdjustment(300, 2), TimeUnit.MILLISECONDS))) { MongoDatabase database = client.getDatabase(namespace.getDatabaseName()); GridFSBucket gridFsBucket = createGridFsBucket(database, GRID_FS_BUCKET_NAME).withChunkSizeBytes(2); @@ -355,7 +349,6 @@ public void testAbortingGridFsUploadStreamTimeout() throws Throwable { @Test public void testGridFsDownloadStreamTimeout() { assumeTrue(serverVersionAtLeast(4, 4)); - long rtt = ClusterFixture.getPrimaryRTT(); chunksCollectionHelper.create(); filesCollectionHelper.create(); @@ -377,18 +370,19 @@ public void testGridFsDownloadStreamTimeout() { + " metadata: {}" + "}" )), WriteConcern.MAJORITY); + collectionHelper.runAdminCommand("{" + " configureFailPoint: \"failCommand\"," + " mode: { skip: 1 }," + " data: {" + " failCommands: [\"find\"]," + " blockConnection: true," - + " blockTimeMS: " + (rtt + 95) + + " blockTimeMS: " + 500 + " }" + "}"); try (MongoClient client = createMongoClient(getMongoClientSettingsBuilder() - .timeout(rtt + 100, TimeUnit.MILLISECONDS))) { + .timeout(withRttAdjustment(300, 2), TimeUnit.MILLISECONDS))) { MongoDatabase database = client.getDatabase(namespace.getDatabaseName()); GridFSBucket gridFsBucket = createGridFsBucket(database, GRID_FS_BUCKET_NAME).withChunkSizeBytes(2); @@ -396,7 +390,9 @@ public void testGridFsDownloadStreamTimeout() { assertThrows(MongoOperationTimeoutException.class, downloadStream::read); List events = commandListener.getCommandStartedEvents(); - List findCommands = events.stream().filter(e -> e.getCommandName().equals("find")).collect(Collectors.toList()); + List findCommands = events.stream() + .filter(e -> e.getCommandName().equals("find")) + .collect(Collectors.toList()); assertEquals(2, findCommands.size()); assertEquals(gridFsFileNamespace.getCollectionName(), findCommands.get(0).getCommand().getString("find").getValue()); @@ -409,7 +405,7 @@ public void testGridFsDownloadStreamTimeout() { @ParameterizedTest(name = "[{index}] {0}") @MethodSource("test8ServerSelectionArguments") public void test8ServerSelection(final String connectionString) { - int timeoutBuffer = 100; // 5 in spec, Java is slower + int timeoutBuffer = 150; // 5 in spec, Java is slower // 1. Create a MongoClient try (MongoClient mongoClient = createMongoClient(getMongoClientSettingsBuilder() .applyConnectionString(new ConnectionString(connectionString))) @@ -422,6 +418,7 @@ public void test8ServerSelection(final String connectionString) { // Expect this to fail with a server selection timeout error after no more than 15ms [this is increased] long elapsed = msElapsedSince(start); assertTrue(throwable.getMessage().contains("while waiting for a server")); + System.err.println("Elapsed time: " + elapsed + "ms"); assertTrue(elapsed < 10 + timeoutBuffer, "Took too long to time out, elapsedMS: " + elapsed); } } @@ -445,7 +442,7 @@ public void test8ServerSelectionHandshake(final String ignoredTestName, final in + " data: {" + " failCommands: [\"saslContinue\"]," + " blockConnection: true," - + " blockTimeMS: 350" + + " blockTimeMS: 600" + " }" + "}"); @@ -461,7 +458,8 @@ public void test8ServerSelectionHandshake(final String ignoredTestName, final in .insertOne(new Document("x", 1)); }); long elapsed = msElapsedSince(start); - assertTrue(elapsed <= 310, "Took too long to time out, elapsedMS: " + elapsed); + System.err.println("test8ServerSelectionHandshake elapsed " + elapsed + "ms"); + assertTrue(elapsed <= 350, "Took too long to time out, elapsedMS: " + elapsed); } } @@ -478,23 +476,23 @@ public void test9EndSessionClientTimeout() { + " data: {" + " failCommands: [\"abortTransaction\"]," + " blockConnection: true," - + " blockTimeMS: " + 150 + + " blockTimeMS: " + 700 + " }" + "}"); try (MongoClient mongoClient = createMongoClient(getMongoClientSettingsBuilder().retryWrites(false) - .timeout(100, TimeUnit.MILLISECONDS))) { - MongoCollection collection = mongoClient.getDatabase(namespace.getDatabaseName()) + .timeout(withRttAdjustment(500, 2), TimeUnit.MILLISECONDS))) { + MongoDatabase database = mongoClient.getDatabase(namespace.getDatabaseName()); + MongoCollection collection = database .getCollection(namespace.getCollectionName()); try (ClientSession session = mongoClient.startSession()) { session.startTransaction(); collection.insertOne(session, new Document("x", 1)); - long start = System.nanoTime(); session.close(); long elapsed = msElapsedSince(start) - postSessionCloseSleep(); - assertTrue(elapsed <= 150, "Took too long to time out, elapsedMS: " + elapsed); + assertTrue(elapsed <= 700, "Took too long to time out, elapsedMS: " + elapsed); } } CommandFailedEvent abortTransactionEvent = assertDoesNotThrow(() -> @@ -515,7 +513,7 @@ public void test9EndSessionSessionTimeout() { + " data: {" + " failCommands: [\"abortTransaction\"]," + " blockConnection: true," - + " blockTimeMS: " + 150 + + " blockTimeMS: " + 400 + " }" + "}"); @@ -524,14 +522,14 @@ public void test9EndSessionSessionTimeout() { .getCollection(namespace.getCollectionName()); try (ClientSession session = mongoClient.startSession(ClientSessionOptions.builder() - .defaultTimeout(100, TimeUnit.MILLISECONDS).build())) { + .defaultTimeout(withRttAdjustment(300, 2), TimeUnit.MILLISECONDS).build())) { session.startTransaction(); collection.insertOne(session, new Document("x", 1)); long start = System.nanoTime(); session.close(); long elapsed = msElapsedSince(start) - postSessionCloseSleep(); - assertTrue(elapsed <= 150, "Took too long to time out, elapsedMS: " + elapsed); + assertTrue(elapsed <= withRttAdjustment(400, 2), "Took too long to time out, elapsedMS: " + elapsed); } } CommandFailedEvent abortTransactionEvent = assertDoesNotThrow(() -> @@ -558,11 +556,12 @@ public void test9EndSessionCustomTesEachOperationHasItsOwnTimeoutWithCommit() { MongoCollection collection = mongoClient.getDatabase(namespace.getDatabaseName()) .getCollection(namespace.getCollectionName()); + long defaultTimeout = withRttAdjustment(300); try (ClientSession session = mongoClient.startSession(ClientSessionOptions.builder() - .defaultTimeout(200, TimeUnit.MILLISECONDS).build())) { + .defaultTimeout(defaultTimeout, TimeUnit.MILLISECONDS).build())) { session.startTransaction(); collection.insertOne(session, new Document("x", 1)); - sleep(200); + sleep(defaultTimeout); assertDoesNotThrow(session::commitTransaction); } @@ -589,11 +588,12 @@ public void test9EndSessionCustomTesEachOperationHasItsOwnTimeoutWithAbort() { MongoCollection collection = mongoClient.getDatabase(namespace.getDatabaseName()) .getCollection(namespace.getCollectionName()); + long defaultTimeout = withRttAdjustment(300); try (ClientSession session = mongoClient.startSession(ClientSessionOptions.builder() - .defaultTimeout(200, TimeUnit.MILLISECONDS).build())) { + .defaultTimeout(defaultTimeout, TimeUnit.MILLISECONDS).build())) { session.startTransaction(); collection.insertOne(session, new Document("x", 1)); - sleep(200); + sleep(defaultTimeout); assertDoesNotThrow(session::close); } @@ -613,12 +613,12 @@ public void test10ConvenientTransactions() { + " data: {" + " failCommands: [\"insert\", \"abortTransaction\"]," + " blockConnection: true," - + " blockTimeMS: " + 150 + + " blockTimeMS: " + 200 + " }" + "}"); try (MongoClient mongoClient = createMongoClient(getMongoClientSettingsBuilder() - .timeout(100, TimeUnit.MILLISECONDS))) { + .timeout(150, TimeUnit.MILLISECONDS))) { MongoCollection collection = mongoClient.getDatabase(namespace.getDatabaseName()) .getCollection(namespace.getCollectionName()); @@ -656,12 +656,13 @@ public void test10CustomTestWithTransactionUsesASingleTimeout() { MongoCollection collection = mongoClient.getDatabase(namespace.getDatabaseName()) .getCollection(namespace.getCollectionName()); + long defaultTimeout = withRttAdjustment(200); try (ClientSession session = mongoClient.startSession(ClientSessionOptions.builder() - .defaultTimeout(200, TimeUnit.MILLISECONDS).build())) { + .defaultTimeout(defaultTimeout, TimeUnit.MILLISECONDS).build())) { assertThrows(MongoOperationTimeoutException.class, () -> session.withTransaction(() -> { collection.insertOne(session, new Document("x", 1)); - sleep(200); + sleep(defaultTimeout); return true; }) ); @@ -691,12 +692,13 @@ public void test10CustomTestWithTransactionUsesASingleTimeoutWithLock() { MongoCollection collection = mongoClient.getDatabase(namespace.getDatabaseName()) .getCollection(namespace.getCollectionName()); + long defaultTimeout = withRttAdjustment(200); try (ClientSession session = mongoClient.startSession(ClientSessionOptions.builder() - .defaultTimeout(200, TimeUnit.MILLISECONDS).build())) { + .defaultTimeout(defaultTimeout, TimeUnit.MILLISECONDS).build())) { assertThrows(MongoOperationTimeoutException.class, () -> session.withTransaction(() -> { collection.insertOne(session, new Document("x", 1)); - sleep(200); + sleep(defaultTimeout); return true; }) ); @@ -705,7 +707,7 @@ public void test10CustomTestWithTransactionUsesASingleTimeoutWithLock() { } @DisplayName("11. Multi-batch bulkWrites") - @Test + @FlakyTest(maxAttempts = 3) @SuppressWarnings("try") protected void test11MultiBatchBulkWrites() throws InterruptedException { assumeTrue(serverVersionAtLeast(8, 0)); @@ -713,12 +715,22 @@ protected void test11MultiBatchBulkWrites() throws InterruptedException { // a workaround for https://jira.mongodb.org/browse/DRIVERS-2997, remove this block when the aforementioned bug is fixed client.getDatabase(namespace.getDatabaseName()).drop(); } - BsonDocument failPointDocument = new BsonDocument("configureFailPoint", new BsonString("failCommand")) - .append("mode", new BsonDocument("times", new BsonInt32(2))) - .append("data", new BsonDocument("failCommands", new BsonArray(singletonList(new BsonString("bulkWrite")))) - .append("blockConnection", BsonBoolean.TRUE) - .append("blockTimeMS", new BsonInt32(2020))); - try (MongoClient client = createMongoClient(getMongoClientSettingsBuilder().timeout(4000, TimeUnit.MILLISECONDS)); + BsonDocument failPointDocument = BsonDocument.parse("{" + + " configureFailPoint: \"failCommand\"," + + " mode: { times: 2}," + + " data: {" + + " failCommands: [\"bulkWrite\" ]," + + " blockConnection: true," + + " blockTimeMS: " + 2020 + + " }" + + "}"); + + /* + We use 2 operation count to adjust the timeout by RTT, even though we have only one successful insert to account for, because + the payload is larger than measured RTT on "hello" command. + */ + long timeout = withRttAdjustment(4000, 2); + try (MongoClient client = createMongoClient(getMongoClientSettingsBuilder().timeout(timeout, TimeUnit.MILLISECONDS)); FailPoint ignored = FailPoint.enable(failPointDocument, getPrimary())) { MongoDatabase db = client.getDatabase(namespace.getDatabaseName()); db.drop(); @@ -741,8 +753,8 @@ protected void test11MultiBatchBulkWrites() throws InterruptedException { * Not a prose spec test. However, it is additional test case for better coverage. */ @Test - @DisplayName("Should ignore wTimeoutMS of WriteConcern to initial and subsequent commitTransaction operations") - public void shouldIgnoreWtimeoutMsOfWriteConcernToInitialAndSubsequentCommitTransactionOperations() { + @DisplayName("Should not include wTimeoutMS of WriteConcern to initial and subsequent commitTransaction operations") + public void shouldNotIncludeWtimeoutMsOfWriteConcernToInitialAndSubsequentCommitTransactionOperations() { assumeTrue(serverVersionAtLeast(4, 4)); assumeFalse(isStandalone()); @@ -750,14 +762,15 @@ public void shouldIgnoreWtimeoutMsOfWriteConcernToInitialAndSubsequentCommitTran MongoCollection collection = mongoClient.getDatabase(namespace.getDatabaseName()) .getCollection(namespace.getCollectionName()); + long defaultTimeout = withRttAdjustment(200); try (ClientSession session = mongoClient.startSession(ClientSessionOptions.builder() - .defaultTimeout(200, TimeUnit.MILLISECONDS) + .defaultTimeout(defaultTimeout, TimeUnit.MILLISECONDS) .build())) { session.startTransaction(TransactionOptions.builder() .writeConcern(WriteConcern.ACKNOWLEDGED.withWTimeout(100, TimeUnit.MILLISECONDS)) .build()); collection.insertOne(session, new Document("x", 1)); - sleep(200); + sleep(defaultTimeout); assertDoesNotThrow(session::commitTransaction); //repeat commit. @@ -891,7 +904,6 @@ public void testKillCursorsIsNotExecutedAfterGetMoreNetworkErrorWhenTimeoutIsNot assumeTrue(serverVersionAtLeast(4, 4)); assumeTrue(isLoadBalanced()); - long rtt = ClusterFixture.getPrimaryRTT(); collectionHelper.create(namespace.getCollectionName(), new CreateCollectionOptions()); collectionHelper.insertDocuments(new Document(), new Document()); collectionHelper.runAdminCommand("{" @@ -900,7 +912,7 @@ public void testKillCursorsIsNotExecutedAfterGetMoreNetworkErrorWhenTimeoutIsNot + " data: {" + " failCommands: [\"getMore\" ]," + " blockConnection: true," - + " blockTimeMS: " + (rtt + 600) + + " blockTimeMS: " + 600 + " }" + "}"); @@ -938,7 +950,6 @@ public void testKillCursorsIsNotExecutedAfterGetMoreNetworkError() { assumeTrue(serverVersionAtLeast(4, 4)); assumeTrue(isLoadBalanced()); - long rtt = ClusterFixture.getPrimaryRTT(); collectionHelper.create(namespace.getCollectionName(), new CreateCollectionOptions()); collectionHelper.insertDocuments(new Document(), new Document()); collectionHelper.runAdminCommand("{" @@ -947,7 +958,7 @@ public void testKillCursorsIsNotExecutedAfterGetMoreNetworkError() { + " data: {" + " failCommands: [\"getMore\" ]," + " blockConnection: true," - + " blockTimeMS: " + (rtt + 600) + + " blockTimeMS: " + 600 + " }" + "}"); @@ -1033,9 +1044,10 @@ private static Stream test8ServerSelectionArguments() { } private static Stream test8ServerSelectionHandshakeArguments() { + return Stream.of( - Arguments.of("timeoutMS honored for connection handshake commands if it's lower than serverSelectionTimeoutMS", 200, 300), - Arguments.of("serverSelectionTimeoutMS honored for connection handshake commands if it's lower than timeoutMS", 300, 200) + Arguments.of("timeoutMS honored for connection handshake commands if it's lower than serverSelectionTimeoutMS", 200, 500), + Arguments.of("serverSelectionTimeoutMS honored for connection handshake commands if it's lower than timeoutMS", 500, 200) ); } @@ -1046,7 +1058,8 @@ protected MongoNamespace generateNamespace() { protected MongoClientSettings.Builder getMongoClientSettingsBuilder() { commandListener.reset(); - return Fixture.getMongoClientSettingsBuilder() + MongoClientSettings.Builder mongoClientSettingsBuilder = Fixture.getMongoClientSettingsBuilder(); + return mongoClientSettingsBuilder .readConcern(ReadConcern.MAJORITY) .writeConcern(WriteConcern.MAJORITY) .readPreference(ReadPreference.primary()) @@ -1061,6 +1074,9 @@ public void setUp() { gridFsChunksNamespace = new MongoNamespace(getDefaultDatabaseName(), GRID_FS_BUCKET_NAME + ".chunks"); collectionHelper = new CollectionHelper<>(new BsonDocumentCodec(), namespace); + // in some test collection might not have been created yet, thus dropping it in afterEach will throw an error + collectionHelper.create(); + filesCollectionHelper = new CollectionHelper<>(new BsonDocumentCodec(), gridFsFileNamespace); chunksCollectionHelper = new CollectionHelper<>(new BsonDocumentCodec(), gridFsChunksNamespace); commandListener = new TestCommandListener(); @@ -1100,4 +1116,14 @@ private MongoClient createMongoClient(final MongoClientSettings.Builder builder) private long msElapsedSince(final long t1) { return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1); } + + protected long withRttAdjustment(int timeout, int countOfOperations) { + long primaryRTT = ClusterFixture.getPrimaryRTT(); + long adjustedTimeout = timeout + (primaryRTT * countOfOperations); + return adjustedTimeout; + } + + protected long withRttAdjustment(int timeout) { + return withRttAdjustment(timeout, 1); + } } diff --git a/driver-sync/src/test/functional/com/mongodb/client/csot/AbstractClientSideOperationsEncryptionTimeoutProseTest.java b/driver-sync/src/test/functional/com/mongodb/client/csot/AbstractClientSideOperationsEncryptionTimeoutProseTest.java index dd45bc8ae2c..e5dd140cdda 100644 --- a/driver-sync/src/test/functional/com/mongodb/client/csot/AbstractClientSideOperationsEncryptionTimeoutProseTest.java +++ b/driver-sync/src/test/functional/com/mongodb/client/csot/AbstractClientSideOperationsEncryptionTimeoutProseTest.java @@ -173,12 +173,12 @@ void shouldThrowOperationTimeoutExceptionWhenDecryptData() { try (ClientEncryption clientEncryption = createClientEncryption(getClientEncryptionSettingsBuilder(rtt + 400))) { keyVaultCollectionHelper.runAdminCommand("{" - + " configureFailPoint: \"" + FAIL_COMMAND_NAME + "\"," + + " configureFailPoint: \"" + FAIL_COMMAND_NAME + "\"," + " mode: { times: 1 }," + " data: {" + " failCommands: [\"find\"]," + " blockConnection: true," - + " blockTimeMS: " + (rtt + 500) + + " blockTimeMS: " + 500 + " }" + "}"); commandListener.reset(); @@ -206,7 +206,7 @@ void shouldDecreaseOperationTimeoutForSubsequentOperations() { + " data: {" + " failCommands: [\"insert\", \"find\", \"listCollections\"]," + " blockConnection: true," - + " blockTimeMS: " + (rtt + 10) + + " blockTimeMS: " + 10 + " }" + "}"); 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 d5cf994f920..bab4de63aca 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 @@ -63,6 +63,18 @@ public static void applyCustomizations(final TestDef def) { .test("change-streams", "change-streams-errors", "The watch helper must not throw a custom exception when executed against a single server topology, but instead depend on a server error"); // client-side-operation-timeout (CSOT) + def.retry("Unified CSOT tests do not account for RTT which varies in TLS vs non-TLS runs") + .whenFailureContains("timeout") + .test("client-side-operations-timeout", + "timeoutMS behaves correctly for non-tailable cursors", + "timeoutMS is refreshed for getMore if timeoutMode is iteration - success"); + + def.retry("Unified CSOT tests do not account for RTT which varies in TLS vs non-TLS runs") + .whenFailureContains("timeout") + .test("client-side-operations-timeout", + "timeoutMS behaves correctly for tailable non-awaitData cursors", + "timeoutMS is refreshed for getMore - success"); + def.skipNoncompliantReactive("No good way to fulfill tryNext() requirement with a Publisher") .test("client-side-operations-timeout", "timeoutMS behaves correctly for tailable awaitData cursors",