Skip to content

Commit e89ada5

Browse files
authored
Set maxTimeMS explicitly for commands being explained (#1497)
JAVA-5580
1 parent eea937c commit e89ada5

File tree

8 files changed

+129
-15
lines changed

8 files changed

+129
-15
lines changed

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import com.mongodb.MongoClientException;
1919
import com.mongodb.MongoOperationTimeoutException;
20+
import com.mongodb.internal.connection.CommandMessage;
2021
import com.mongodb.internal.time.StartTime;
2122
import com.mongodb.internal.time.Timeout;
2223
import com.mongodb.lang.Nullable;
@@ -213,14 +214,23 @@ public void resetToDefaultMaxTime() {
213214

214215
/**
215216
* The override will be provided as the remaining value in
216-
* {@link #runMaxTimeMS}, where 0 is ignored.
217+
* {@link #runMaxTimeMS}, where 0 is ignored. This is useful for setting timeout
218+
* in {@link CommandMessage} as an extra element before we send it to the server.
219+
*
217220
* <p>
218221
* NOTE: Suitable for static user-defined values only (i.e MaxAwaitTimeMS),
219-
* not for running timeouts that adjust dynamically.
222+
* not for running timeouts that adjust dynamically (CSOT).
220223
*/
221224
public void setMaxTimeOverride(final long maxTimeMS) {
222225
this.maxTimeSupplier = () -> maxTimeMS;
223226
}
227+
/**
228+
* Disable the maxTimeMS override. This way the maxTimeMS will not
229+
* be appended to the command in the {@link CommandMessage}.
230+
*/
231+
public void disableMaxTimeOverride() {
232+
this.maxTimeSupplier = () -> 0;
233+
}
224234

225235
/**
226236
* The override will be provided as the remaining value in

driver-core/src/main/com/mongodb/internal/connection/CommandHelper.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@
2020
import com.mongodb.MongoServerException;
2121
import com.mongodb.ServerApi;
2222
import com.mongodb.connection.ClusterConnectionMode;
23+
import com.mongodb.internal.TimeoutContext;
2324
import com.mongodb.internal.async.SingleResultCallback;
2425
import com.mongodb.internal.validator.NoOpFieldNameValidator;
2526
import com.mongodb.lang.Nullable;
2627
import org.bson.BsonDocument;
28+
import org.bson.BsonInt64;
2729
import org.bson.BsonValue;
2830
import org.bson.codecs.BsonDocumentCodec;
2931

@@ -117,6 +119,20 @@ private static CommandMessage getCommandMessage(final String database, final Bso
117119
clusterConnectionMode, serverApi);
118120
}
119121

122+
123+
/**
124+
* Appends a user-defined maxTimeMS to the command if CSOT is not enabled.
125+
* This is necessary when maxTimeMS must be explicitly set on the command being explained,
126+
* rather than appending it lazily to the explain command in the {@link CommandMessage} via {@link TimeoutContext#setMaxTimeOverride(long)}.
127+
* This ensures backwards compatibility with pre-CSOT behavior.
128+
*/
129+
public static void applyMaxTimeMS(final TimeoutContext timeoutContext, final BsonDocument command) {
130+
if (!timeoutContext.hasTimeoutMS()) {
131+
command.append("maxTimeMS", new BsonInt64(timeoutContext.getTimeoutSettings().getMaxTimeMS()));
132+
timeoutContext.disableMaxTimeOverride();
133+
}
134+
}
135+
120136
private CommandHelper() {
121137
}
122138
}

driver-core/src/main/com/mongodb/internal/operation/AggregateOperation.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import java.util.List;
3434

35+
import static com.mongodb.internal.connection.CommandHelper.applyMaxTimeMS;
3536
import static com.mongodb.internal.operation.ExplainHelper.asExplainCommand;
3637
import static com.mongodb.internal.operation.ServerVersionHelper.MIN_WIRE_VERSION;
3738

@@ -155,8 +156,11 @@ public <R> AsyncReadOperation<R> asAsyncExplainableOperation(@Nullable final Exp
155156

156157
<R> CommandReadOperation<R> createExplainableOperation(@Nullable final ExplainVerbosity verbosity, final Decoder<R> resultDecoder) {
157158
return new CommandReadOperation<>(getNamespace().getDatabaseName(),
158-
(operationContext, serverDescription, connectionDescription) ->
159-
asExplainCommand(wrapped.getCommand(operationContext, MIN_WIRE_VERSION), verbosity), resultDecoder);
159+
(operationContext, serverDescription, connectionDescription) -> {
160+
BsonDocument command = wrapped.getCommand(operationContext, MIN_WIRE_VERSION);
161+
applyMaxTimeMS(operationContext.getTimeoutContext(), command);
162+
return asExplainCommand(command, verbosity);
163+
}, resultDecoder);
160164
}
161165

162166
MongoNamespace getNamespace() {

driver-core/src/main/com/mongodb/internal/operation/FindOperation.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242

4343
import static com.mongodb.assertions.Assertions.notNull;
4444
import static com.mongodb.internal.async.ErrorHandlingResultCallback.errorHandlingCallback;
45+
import static com.mongodb.internal.connection.CommandHelper.applyMaxTimeMS;
4546
import static com.mongodb.internal.operation.AsyncOperationHelper.CommandReadTransformerAsync;
4647
import static com.mongodb.internal.operation.AsyncOperationHelper.createReadCommandAndExecuteAsync;
4748
import static com.mongodb.internal.operation.AsyncOperationHelper.decorateReadWithRetriesAsync;
@@ -364,8 +365,11 @@ public <R> AsyncReadOperation<R> asAsyncExplainableOperation(@Nullable final Exp
364365

365366
<R> CommandReadOperation<R> createExplainableOperation(@Nullable final ExplainVerbosity verbosity, final Decoder<R> resultDecoder) {
366367
return new CommandReadOperation<>(getNamespace().getDatabaseName(),
367-
(operationContext, serverDescription, connectionDescription) ->
368-
asExplainCommand(getCommand(operationContext, MIN_WIRE_VERSION), verbosity), resultDecoder);
368+
(operationContext, serverDescription, connectionDescription) -> {
369+
BsonDocument command = getCommand(operationContext, MIN_WIRE_VERSION);
370+
applyMaxTimeMS(operationContext.getTimeoutContext(), command);
371+
return asExplainCommand(command, verbosity);
372+
}, resultDecoder);
369373
}
370374

371375
private BsonDocument getCommand(final OperationContext operationContext, final int maxWireVersion) {
@@ -397,7 +401,7 @@ private BsonDocument getCommand(final OperationContext operationContext, final i
397401
if (isAwaitData()) {
398402
commandDocument.put("awaitData", BsonBoolean.TRUE);
399403
} else {
400-
operationContext.getTimeoutContext().setMaxTimeOverride(0L);
404+
operationContext.getTimeoutContext().disableMaxTimeOverride();
401405
}
402406
} else {
403407
setNonTailableCursorMaxTimeSupplier(timeoutMode, operationContext);

driver-core/src/main/com/mongodb/internal/operation/MapReduceToCollectionOperation.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535

3636
import static com.mongodb.assertions.Assertions.isTrue;
3737
import static com.mongodb.assertions.Assertions.notNull;
38+
import static com.mongodb.internal.connection.CommandHelper.applyMaxTimeMS;
3839
import static com.mongodb.internal.operation.AsyncOperationHelper.CommandWriteTransformerAsync;
3940
import static com.mongodb.internal.operation.AsyncOperationHelper.executeCommandAsync;
4041
import static com.mongodb.internal.operation.CommandOperationHelper.CommandCreator;
@@ -243,9 +244,11 @@ public AsyncReadOperation<BsonDocument> asExplainableOperationAsync(final Explai
243244

244245
private CommandReadOperation<BsonDocument> createExplainableOperation(final ExplainVerbosity explainVerbosity) {
245246
return new CommandReadOperation<>(getNamespace().getDatabaseName(),
246-
(operationContext, serverDescription, connectionDescription) ->
247-
asExplainCommand(getCommandCreator().create(operationContext, serverDescription, connectionDescription),
248-
explainVerbosity), new BsonDocumentCodec());
247+
(operationContext, serverDescription, connectionDescription) -> {
248+
BsonDocument command = getCommandCreator().create(operationContext, serverDescription, connectionDescription);
249+
applyMaxTimeMS(operationContext.getTimeoutContext(), command);
250+
return asExplainCommand(command, explainVerbosity);
251+
}, new BsonDocumentCodec());
249252
}
250253

251254
private CommandWriteTransformer<BsonDocument, MapReduceStatistics> transformer(final TimeoutContext timeoutContext) {

driver-core/src/main/com/mongodb/internal/operation/MapReduceWithInlineResultsOperation.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232

3333
import static com.mongodb.assertions.Assertions.notNull;
3434
import static com.mongodb.internal.async.ErrorHandlingResultCallback.errorHandlingCallback;
35+
import static com.mongodb.internal.connection.CommandHelper.applyMaxTimeMS;
3536
import static com.mongodb.internal.operation.AsyncOperationHelper.CommandReadTransformerAsync;
3637
import static com.mongodb.internal.operation.AsyncOperationHelper.executeRetryableReadAsync;
3738
import static com.mongodb.internal.operation.CommandOperationHelper.CommandCreator;
@@ -188,9 +189,12 @@ public AsyncReadOperation<BsonDocument> asExplainableOperationAsync(final Explai
188189

189190
private CommandReadOperation<BsonDocument> createExplainableOperation(final ExplainVerbosity explainVerbosity) {
190191
return new CommandReadOperation<>(namespace.getDatabaseName(),
191-
(operationContext, serverDescription, connectionDescription) ->
192-
asExplainCommand(getCommandCreator().create(operationContext, serverDescription, connectionDescription),
193-
explainVerbosity), new BsonDocumentCodec());
192+
(operationContext, serverDescription, connectionDescription) -> {
193+
BsonDocument command = getCommandCreator().create(operationContext, serverDescription, connectionDescription);
194+
applyMaxTimeMS(operationContext.getTimeoutContext(), command);
195+
return asExplainCommand(command,
196+
explainVerbosity);
197+
}, new BsonDocumentCodec());
194198
}
195199

196200
private CommandReadTransformer<BsonDocument, MapReduceBatchCursor<T>> transformer() {

driver-core/src/main/com/mongodb/internal/operation/OperationHelper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ static boolean canRetryRead(final ServerDescription serverDescription, final Ope
198198

199199
static void setNonTailableCursorMaxTimeSupplier(final TimeoutMode timeoutMode, final OperationContext operationContext) {
200200
if (timeoutMode == TimeoutMode.ITERATION) {
201-
operationContext.getTimeoutContext().setMaxTimeOverride(0L);
201+
operationContext.getTimeoutContext().disableMaxTimeOverride();
202202
}
203203
}
204204

driver-sync/src/test/functional/com/mongodb/client/AbstractExplainTest.java

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,25 @@
1818

1919
import com.mongodb.ExplainVerbosity;
2020
import com.mongodb.MongoClientSettings;
21+
import com.mongodb.MongoCommandException;
2122
import com.mongodb.client.model.Aggregates;
2223
import com.mongodb.client.model.Filters;
24+
import com.mongodb.event.CommandStartedEvent;
25+
import com.mongodb.internal.connection.TestCommandListener;
2326
import org.bson.BsonDocument;
2427
import org.bson.BsonInt32;
2528
import org.bson.Document;
2629
import org.junit.After;
2730
import org.junit.Before;
2831
import org.junit.Test;
2932

33+
import java.util.concurrent.TimeUnit;
34+
3035
import static com.mongodb.ClusterFixture.serverVersionAtLeast;
3136
import static com.mongodb.ClusterFixture.serverVersionLessThan;
3237
import static com.mongodb.client.Fixture.getDefaultDatabaseName;
3338
import static java.util.Collections.singletonList;
39+
import static org.junit.Assert.assertEquals;
3440
import static org.junit.Assert.assertFalse;
3541
import static org.junit.Assert.assertNotNull;
3642
import static org.junit.Assert.assertTrue;
@@ -39,17 +45,20 @@
3945
public abstract class AbstractExplainTest {
4046

4147
private MongoClient client;
48+
private TestCommandListener commandListener;
4249

4350
protected abstract MongoClient createMongoClient(MongoClientSettings settings);
4451

4552
@Before
4653
public void setUp() {
47-
client = createMongoClient(Fixture.getMongoClientSettings());
54+
commandListener = new TestCommandListener();
55+
client = createMongoClient(Fixture.getMongoClientSettingsBuilder().addCommandListener(commandListener).build());
4856
}
4957

5058
@After
5159
public void tearDown() {
5260
client.close();
61+
commandListener.reset();
5362
}
5463

5564
@Test
@@ -83,6 +92,60 @@ public void testExplainOfFind() {
8392
assertFalse(explainBsonDocument.containsKey("executionStats"));
8493
}
8594

95+
@Test
96+
public void testFindContainsMaxTimeMsInExplain() {
97+
//given
98+
MongoCollection<BsonDocument> collection = client.getDatabase(getDefaultDatabaseName())
99+
.getCollection("explainTest", BsonDocument.class);
100+
101+
FindIterable<BsonDocument> iterable = collection.find()
102+
.maxTime(500, TimeUnit.MILLISECONDS);
103+
104+
//when
105+
iterable.explain();
106+
107+
//then
108+
assertExplainableCommandContainMaxTimeMS();
109+
}
110+
111+
@Test
112+
public void testAggregateContainsMaxTimeMsInExplain() {
113+
//given
114+
MongoCollection<BsonDocument> collection = client.getDatabase(getDefaultDatabaseName())
115+
.getCollection("explainTest", BsonDocument.class);
116+
117+
AggregateIterable<BsonDocument> iterable = collection.aggregate(
118+
singletonList(Aggregates.match(Filters.eq("_id", 1))))
119+
.maxTime(500, TimeUnit.MILLISECONDS);
120+
121+
//when
122+
iterable.explain();
123+
124+
//then
125+
assertExplainableCommandContainMaxTimeMS();
126+
}
127+
128+
@Test
129+
public void testListSearchIndexesContainsMaxTimeMsInExplain() {
130+
//given
131+
assumeTrue(serverVersionAtLeast(6, 0));
132+
MongoCollection<BsonDocument> collection = client.getDatabase(getDefaultDatabaseName())
133+
.getCollection("explainTest", BsonDocument.class);
134+
135+
ListSearchIndexesIterable<Document> iterable = collection.listSearchIndexes()
136+
.maxTime(500, TimeUnit.MILLISECONDS);
137+
138+
//when
139+
try {
140+
iterable.explain();
141+
} catch (MongoCommandException throwable) {
142+
//we expect listSearchIndexes is only supported in Atlas Search in some deployments.
143+
}
144+
145+
//then
146+
assertExplainableCommandContainMaxTimeMS();
147+
}
148+
86149
@Test
87150
public void testExplainOfAggregateWithNewResponseStructure() {
88151
// Aggregate explain is supported on earlier versions, but the structure of the response on which we're asserting in this test
@@ -167,4 +230,14 @@ public void testExplainOfAggregateWithOldResponseStructure() {
167230
explainBsonDocument = iterable.explain(BsonDocument.class, ExplainVerbosity.QUERY_PLANNER);
168231
assertNotNull(explainBsonDocument);
169232
}
233+
234+
private void assertExplainableCommandContainMaxTimeMS() {
235+
assertEquals(1, commandListener.getCommandStartedEvents().size());
236+
CommandStartedEvent explain = commandListener.getCommandStartedEvent("explain");
237+
BsonDocument explainCommand = explain.getCommand();
238+
BsonDocument explainableCommand = explainCommand.getDocument("explain");
239+
240+
assertFalse(explainCommand.containsKey("maxTimeMS"));
241+
assertTrue(explainableCommand.containsKey("maxTimeMS"));
242+
}
170243
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy