Skip to content

Commit 41bcec3

Browse files
authored
Apply timeout to server selection (#1226)
JAVA-4065
1 parent ea6c37c commit 41bcec3

File tree

5 files changed

+125
-2
lines changed

5 files changed

+125
-2
lines changed

driver-core/src/main/com/mongodb/internal/TimeoutContext.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,8 @@ private static Timeout calculateTimeout(@Nullable final Long timeoutMS) {
156156

157157
public Timeout startServerSelectionTimeout() {
158158
long ms = getTimeoutSettings().getServerSelectionTimeoutMS();
159-
return StartTime.now().timeoutAfterOrInfiniteIfNegative(ms, MILLISECONDS);
159+
Timeout serverSelectionTimeout = StartTime.now().timeoutAfterOrInfiniteIfNegative(ms, MILLISECONDS);
160+
return serverSelectionTimeout.orEarlier(timeout);
160161
}
161162

162163
public Timeout startWaitQueueTimeout(final StartTime checkoutStart) {

driver-core/src/main/com/mongodb/internal/time/TimePoint.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,12 @@ public int hashCode() {
219219

220220
@Override
221221
public String toString() {
222+
long remainingMs = nanos == null
223+
? -1
224+
: TimeUnit.MILLISECONDS.convert(currentNanos() - nanos, NANOSECONDS);
222225
return "TimePoint{"
223226
+ "nanos=" + nanos
227+
+ "remainingMs=" + remainingMs
224228
+ '}';
225229
}
226230
}

driver-core/src/main/com/mongodb/internal/time/Timeout.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ static Timeout infinite() {
8888
* @return a timeout that expires in the specified duration after now.
8989
*/
9090
static Timeout expiresIn(final long duration, final TimeUnit unit) {
91-
// TODO (CSOT) confirm that all usages in final PR are non-negative
91+
// TODO (CSOT) confirm that all usages in final PR always supply a non-negative duration
9292
if (duration < 0) {
9393
throw new AssertionError("Timeouts must not be in the past");
9494
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.mongodb.internal;
18+
19+
import com.mongodb.MongoClientSettings;
20+
import com.mongodb.client.MongoClient;
21+
import com.mongodb.reactivestreams.client.MongoClients;
22+
import com.mongodb.reactivestreams.client.syncadapter.SyncMongoClient;
23+
24+
public class ClientSideOperationsTimeoutAsyncProseTests extends ClientSideOperationsTimeoutProseTests {
25+
26+
@Override
27+
protected MongoClient createMongoClient(final MongoClientSettings settings) {
28+
return new SyncMongoClient(MongoClients.create(settings));
29+
}
30+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.mongodb.internal;
18+
19+
import com.mongodb.ConnectionString;
20+
import com.mongodb.MongoClientSettings;
21+
import com.mongodb.MongoTimeoutException;
22+
import com.mongodb.ReadConcern;
23+
import com.mongodb.ReadPreference;
24+
import com.mongodb.WriteConcern;
25+
import com.mongodb.client.MongoClient;
26+
import com.mongodb.client.MongoClients;
27+
import org.bson.BsonDocument;
28+
import org.bson.BsonInt32;
29+
import org.jetbrains.annotations.NotNull;
30+
import org.junit.jupiter.params.ParameterizedTest;
31+
import org.junit.jupiter.params.provider.ValueSource;
32+
33+
import java.util.concurrent.TimeUnit;
34+
35+
import static org.junit.jupiter.api.Assertions.assertThrows;
36+
import static org.junit.jupiter.api.Assertions.assertTrue;
37+
38+
/**
39+
* See
40+
* <a href="https://github.com/mongodb/specifications/blob/master/source/client-side-operations-timeout/tests/README.rst">Prose Tests</a>.
41+
*/
42+
public class ClientSideOperationsTimeoutProseTests {
43+
44+
protected MongoClient createMongoClient(final MongoClientSettings settings) {
45+
return MongoClients.create(settings);
46+
}
47+
48+
private long msElapsedSince(final long t1) {
49+
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - t1);
50+
}
51+
52+
@NotNull
53+
private MongoClientSettings createMongoClientSettings(final String connectionString) {
54+
// All MongoClient instances created for tests MUST be configured
55+
// with read/write concern majority, read preference primary, and
56+
// TODO (CSOT): command monitoring enabled to listen for command_started events.
57+
ConnectionString cs = new ConnectionString(connectionString);
58+
MongoClientSettings.Builder builder = MongoClientSettings.builder()
59+
.readConcern(ReadConcern.MAJORITY)
60+
.writeConcern(WriteConcern.MAJORITY)
61+
.readPreference(ReadPreference.primary())
62+
.applyConnectionString(cs);
63+
return builder.build();
64+
}
65+
66+
@ParameterizedTest
67+
@ValueSource(strings = {
68+
"mongodb://invalid/?serverSelectionTimeoutMS=10",
69+
"mongodb://invalid/?timeoutMS=10&serverSelectionTimeoutMS=200",
70+
"mongodb://invalid/?timeoutMS=200&serverSelectionTimeoutMS=10",
71+
"mongodb://invalid/?timeoutMS=0&serverSelectionTimeoutMS=10",
72+
})
73+
public void test8ServerSelectionPart1(final String connectionString) {
74+
int timeoutBuffer = 100; // 5 in spec, Java is slower
75+
// 1. Create a MongoClient
76+
try (MongoClient mongoClient = createMongoClient(createMongoClientSettings(connectionString))) {
77+
long start = System.nanoTime();
78+
// 2. Using client, execute:
79+
Throwable throwable = assertThrows(MongoTimeoutException.class, () -> {
80+
mongoClient.getDatabase("admin").runCommand(new BsonDocument("ping", new BsonInt32(1)));
81+
});
82+
// Expect this to fail with a server selection timeout error after no more than 15ms [this is increased]
83+
long elapsed = msElapsedSince(start);
84+
assertTrue(throwable.getMessage().contains("while waiting for a server"));
85+
assertTrue(elapsed < 10 + timeoutBuffer, "Took too long to time out, elapsedMS: " + elapsed);
86+
}
87+
}
88+
}

0 commit comments

Comments
 (0)