diff --git a/driver-core/src/main/com/mongodb/internal/TimeoutContext.java b/driver-core/src/main/com/mongodb/internal/TimeoutContext.java
index ba3b8eb0ac5..b3bdc65a9f7 100644
--- a/driver-core/src/main/com/mongodb/internal/TimeoutContext.java
+++ b/driver-core/src/main/com/mongodb/internal/TimeoutContext.java
@@ -17,8 +17,6 @@
import com.mongodb.MongoClientException;
import com.mongodb.MongoOperationTimeoutException;
-import com.mongodb.internal.async.AsyncRunnable;
-import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.connection.CommandMessage;
import com.mongodb.internal.time.StartTime;
import com.mongodb.internal.time.Timeout;
@@ -26,19 +24,14 @@
import com.mongodb.session.ClientSession;
import java.util.Objects;
-import java.util.Optional;
import java.util.function.LongConsumer;
import static com.mongodb.assertions.Assertions.assertNotNull;
import static com.mongodb.assertions.Assertions.assertNull;
import static com.mongodb.assertions.Assertions.isTrue;
import static com.mongodb.internal.VisibleForTesting.AccessModifier.PRIVATE;
-import static com.mongodb.internal.async.AsyncRunnable.beginAsync;
import static com.mongodb.internal.time.Timeout.ZeroSemantics.ZERO_DURATION_MEANS_INFINITE;
-import static java.util.Optional.empty;
-import static java.util.Optional.ofNullable;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
-import static java.util.concurrent.TimeUnit.NANOSECONDS;
/**
* Timeout Context.
@@ -46,18 +39,15 @@
*
The context for handling timeouts in relation to the Client Side Operation Timeout specification.
*/
public class TimeoutContext {
-
- private final boolean isMaintenanceContext;
private final TimeoutSettings timeoutSettings;
-
@Nullable
- private Timeout timeout;
+ private final Timeout timeout;
@Nullable
- private Timeout computedServerSelectionTimeout;
- private long minRoundTripTimeMS = 0;
-
+ private final Timeout computedServerSelectionTimeout;
@Nullable
- private MaxTimeSupplier maxTimeSupplier = null;
+ private final MaxTimeSupplier maxTimeSupplier;
+ private final boolean isMaintenanceContext;
+ private final long minRoundTripTimeMS;
public static MongoOperationTimeoutException createMongoRoundTripTimeoutException() {
return createMongoTimeoutException("Remaining timeoutMS is less than or equal to the server's minimum round trip time.");
@@ -116,11 +106,6 @@ public static TimeoutContext createTimeoutContext(final ClientSession session, f
return new TimeoutContext(timeoutSettings);
}
- // Creates a copy of the timeout context that can be reset without resetting the original.
- public TimeoutContext copyTimeoutContext() {
- return new TimeoutContext(getTimeoutSettings(), getTimeout());
- }
-
public TimeoutContext(final TimeoutSettings timeoutSettings) {
this(false, timeoutSettings, startTimeout(timeoutSettings.getTimeoutMS()));
}
@@ -129,9 +114,41 @@ private TimeoutContext(final TimeoutSettings timeoutSettings, @Nullable final Ti
this(false, timeoutSettings, timeout);
}
- private TimeoutContext(final boolean isMaintenanceContext, final TimeoutSettings timeoutSettings, @Nullable final Timeout timeout) {
+ private TimeoutContext(final boolean isMaintenanceContext,
+ final TimeoutSettings timeoutSettings,
+ @Nullable final Timeout timeout) {
+ this(isMaintenanceContext,
+ null,
+ 0,
+ timeoutSettings,
+ null,
+ timeout);
+ }
+
+ private TimeoutContext(final boolean isMaintenanceContext,
+ @Nullable final Timeout computedServerSelectionTimeout,
+ final long minRoundTripTimeMS,
+ final TimeoutSettings timeoutSettings,
+ @Nullable final MaxTimeSupplier maxTimeSupplier) {
+ this(isMaintenanceContext,
+ computedServerSelectionTimeout,
+ minRoundTripTimeMS,
+ timeoutSettings,
+ maxTimeSupplier,
+ startTimeout(timeoutSettings.getTimeoutMS()));
+ }
+
+ private TimeoutContext(final boolean isMaintenanceContext,
+ @Nullable final Timeout computedServerSelectionTimeout,
+ final long minRoundTripTimeMS,
+ final TimeoutSettings timeoutSettings,
+ @Nullable final MaxTimeSupplier maxTimeSupplier,
+ @Nullable final Timeout timeout) {
this.isMaintenanceContext = isMaintenanceContext;
this.timeoutSettings = timeoutSettings;
+ this.computedServerSelectionTimeout = computedServerSelectionTimeout;
+ this.minRoundTripTimeMS = minRoundTripTimeMS;
+ this.maxTimeSupplier = maxTimeSupplier;
this.timeout = timeout;
}
@@ -152,17 +169,6 @@ public void onExpired(final Runnable onExpired) {
Timeout.nullAsInfinite(timeout).onExpired(onExpired);
}
- /**
- * Sets the recent min round trip time
- * @param minRoundTripTimeMS the min round trip time
- * @return this
- */
- public TimeoutContext minRoundTripTimeMS(final long minRoundTripTimeMS) {
- isTrue("'minRoundTripTimeMS' must be a positive number", minRoundTripTimeMS >= 0);
- this.minRoundTripTimeMS = minRoundTripTimeMS;
- return this;
- }
-
@Nullable
public Timeout timeoutIncludingRoundTrip() {
return timeout == null ? null : timeout.shortenBy(minRoundTripTimeMS, MILLISECONDS);
@@ -237,8 +243,19 @@ private static void runWithFixedTimeout(final long ms, final LongConsumer onRema
}
}
- public void resetToDefaultMaxTime() {
- this.maxTimeSupplier = null;
+ /**
+ * Creates a new {@link TimeoutContext} with the same settings, but with the
+ * {@link TimeoutSettings#getMaxAwaitTimeMS()} as the maxTimeMS override which will be used
+ * in {@link #runMaxTimeMS(LongConsumer)}.
+ */
+ public TimeoutContext withMaxTimeAsMaxAwaitTimeOverride() {
+ return new TimeoutContext(
+ isMaintenanceContext,
+ computedServerSelectionTimeout,
+ minRoundTripTimeMS,
+ timeoutSettings,
+ timeoutSettings::getMaxAwaitTimeMS,
+ timeout);
}
/**
@@ -253,26 +270,77 @@ public void resetToDefaultMaxTime() {
* If remaining CSOT timeout is less than this static timeout, then CSOT timeout will be used.
*
*/
- public void setMaxTimeOverride(final long maxTimeMS) {
- this.maxTimeSupplier = () -> maxTimeMS;
+ public TimeoutContext withMaxTimeOverride(final long maxTimeMS) {
+ return new TimeoutContext(
+ isMaintenanceContext,
+ computedServerSelectionTimeout,
+ minRoundTripTimeMS,
+ timeoutSettings,
+ () -> maxTimeMS,
+ timeout);
+ }
+
+ /**
+ * Creates {@link TimeoutContext} with the default maxTimeMS behaviour in {@link #runMaxTimeMS(LongConsumer)}:
+ * - if timeoutMS is set, the remaining timeoutMS will be used as the maxTimeMS.
+ * - if timeoutMS is not set, the {@link TimeoutSettings#getMaxTimeMS()} will be used.
+ */
+ public TimeoutContext withDefaultMaxTime() {
+ return new TimeoutContext(
+ isMaintenanceContext,
+ computedServerSelectionTimeout,
+ minRoundTripTimeMS,
+ timeoutSettings,
+ null,
+ timeout);
}
/**
* Disable the maxTimeMS override. This way the maxTimeMS will not
* be appended to the command in the {@link CommandMessage}.
*/
- public void disableMaxTimeOverride() {
- this.maxTimeSupplier = () -> 0;
+ public TimeoutContext withDisabledMaxTimeOverride() {
+ return new TimeoutContext(
+ isMaintenanceContext,
+ computedServerSelectionTimeout,
+ minRoundTripTimeMS,
+ timeoutSettings,
+ () -> 0,
+ timeout);
}
/**
* The override will be provided as the remaining value in
* {@link #runMaxTimeMS}, where 0 is ignored.
*/
- public void setMaxTimeOverrideToMaxCommitTime() {
- this.maxTimeSupplier = () -> getMaxCommitTimeMS();
+ public TimeoutContext withMaxTimeOverrideAsMaxCommitTime() {
+ return new TimeoutContext(
+ isMaintenanceContext,
+ computedServerSelectionTimeout,
+ minRoundTripTimeMS,
+ timeoutSettings,
+ () -> getMaxCommitTimeMS(),
+ timeout);
+ }
+
+
+ /**
+ * Creates {@link TimeoutContext} with the recent min round trip time.
+ *
+ * @param minRoundTripTimeMS the min round trip time
+ * @return this
+ */
+ public TimeoutContext withMinRoundTripTime(final long minRoundTripTimeMS) {
+ return new TimeoutContext(
+ isMaintenanceContext,
+ computedServerSelectionTimeout,
+ minRoundTripTimeMS,
+ timeoutSettings,
+ maxTimeSupplier,
+ timeout);
}
+
@VisibleForTesting(otherwise = PRIVATE)
public long getMaxCommitTimeMS() {
Long maxCommitTimeMS = timeoutSettings.getMaxCommitTimeMS();
@@ -296,65 +364,44 @@ public int getConnectTimeoutMs() {
}
/**
- * @see #hasTimeoutMS()
- * @see #doWithResetTimeout(Runnable)
- * @see #doWithResetTimeout(AsyncRunnable, SingleResultCallback)
- */
- public void resetTimeoutIfPresent() {
- getAndResetTimeoutIfPresent();
- }
-
- /**
- * @see #hasTimeoutMS()
- * @return A {@linkplain Optional#isPresent() non-empty} previous {@linkplain Timeout} iff {@link #hasTimeoutMS()},
- * i.e., iff it was reset.
+ * Resets the timeout if this timeout context is being used by pool maintenance
*/
- private Optional getAndResetTimeoutIfPresent() {
- Timeout result = timeout;
- if (hasTimeoutMS()) {
- timeout = startTimeout(timeoutSettings.getTimeoutMS());
- return ofNullable(result);
+ public TimeoutContext withNewlyStartedTimeoutMaintenanceTimeout() {
+ if (!isMaintenanceContext) {
+ return this;
}
- return empty();
+
+ return new TimeoutContext(
+ true,
+ computedServerSelectionTimeout,
+ minRoundTripTimeMS,
+ timeoutSettings,
+ maxTimeSupplier);
}
- /**
- * @see #resetTimeoutIfPresent()
- */
- public void doWithResetTimeout(final Runnable action) {
- Optional originalTimeout = getAndResetTimeoutIfPresent();
- try {
- action.run();
- } finally {
- originalTimeout.ifPresent(original -> timeout = original);
- }
+
+ public TimeoutContext withMinRoundTripTimeMS(final long minRoundTripTimeMS) {
+ isTrue("'minRoundTripTimeMS' must be a positive number", minRoundTripTimeMS >= 0);
+ return new TimeoutContext(isMaintenanceContext,
+ computedServerSelectionTimeout,
+ minRoundTripTimeMS,
+ timeoutSettings,
+ maxTimeSupplier,
+ timeout);
}
- /**
- * @see #resetTimeoutIfPresent()
- */
- public void doWithResetTimeout(final AsyncRunnable action, final SingleResultCallback callback) {
- beginAsync().thenRun(c -> {
- Optional originalTimeout = getAndResetTimeoutIfPresent();
- beginAsync().thenRun(c2 -> {
- action.finish(c2);
- }).thenAlwaysRunAndFinish(() -> {
- originalTimeout.ifPresent(original -> timeout = original);
- }, c);
- }).finish(callback);
+ // Creates a copy of the timeout context that can be reset without resetting the original.
+ public TimeoutContext copyTimeoutContext() {
+ return new TimeoutContext(getTimeoutSettings(), getTimeout());
}
- /**
- * Resets the timeout if this timeout context is being used by pool maintenance
- */
- public void resetMaintenanceTimeout() {
- if (!isMaintenanceContext) {
- return;
- }
- timeout = Timeout.nullAsInfinite(timeout).call(NANOSECONDS,
- () -> timeout,
- (ms) -> startTimeout(timeoutSettings.getTimeoutMS()),
- () -> startTimeout(timeoutSettings.getTimeoutMS()));
+ public TimeoutContext withNewlyStartedTimeout() {
+ return new TimeoutContext(
+ isMaintenanceContext,
+ computedServerSelectionTimeout,
+ minRoundTripTimeMS,
+ timeoutSettings,
+ maxTimeSupplier);
}
public TimeoutContext withAdditionalReadTimeout(final int additionalReadTimeout) {
@@ -421,20 +468,11 @@ public static Timeout startTimeout(@Nullable final Long timeoutMS) {
* @return the timeout context
*/
public Timeout computeServerSelectionTimeout() {
- Timeout serverSelectionTimeout = StartTime.now()
- .timeoutAfterOrInfiniteIfNegative(getTimeoutSettings().getServerSelectionTimeoutMS(), MILLISECONDS);
-
-
- if (isMaintenanceContext || !hasTimeoutMS()) {
- return serverSelectionTimeout;
- }
-
- if (timeout != null && Timeout.earliest(serverSelectionTimeout, timeout) == timeout) {
- return timeout;
+ if (hasTimeoutMS()) {
+ return assertNotNull(timeout);
}
- computedServerSelectionTimeout = serverSelectionTimeout;
- return computedServerSelectionTimeout;
+ return StartTime.now().timeoutAfterOrInfiniteIfNegative(getTimeoutSettings().getServerSelectionTimeoutMS(), MILLISECONDS);
}
/**
@@ -442,10 +480,16 @@ public Timeout computeServerSelectionTimeout() {
*
* @return a new timeout context with the cached computed server selection timeout if available or this
*/
- public TimeoutContext withComputedServerSelectionTimeoutContext() {
- if (this.hasTimeoutMS() && computedServerSelectionTimeout != null) {
- return new TimeoutContext(false, timeoutSettings, computedServerSelectionTimeout);
+ public TimeoutContext withComputedServerSelectionTimeoutContextNew() {
+ if (this.hasTimeoutMS()) {
+ Timeout serverSelectionTimeout = StartTime.now()
+ .timeoutAfterOrInfiniteIfNegative(getTimeoutSettings().getServerSelectionTimeoutMS(), MILLISECONDS);
+ if (isMaintenanceContext) {
+ return new TimeoutContext(false, timeoutSettings, serverSelectionTimeout);
+ }
+ return new TimeoutContext(false, timeoutSettings, Timeout.earliest(serverSelectionTimeout, timeout));
}
+
return this;
}
diff --git a/driver-core/src/main/com/mongodb/internal/async/SingleResultCallback.java b/driver-core/src/main/com/mongodb/internal/async/SingleResultCallback.java
index 632e453d0c0..11da1c97f75 100644
--- a/driver-core/src/main/com/mongodb/internal/async/SingleResultCallback.java
+++ b/driver-core/src/main/com/mongodb/internal/async/SingleResultCallback.java
@@ -29,6 +29,8 @@
*This class is not part of the public API and may be removed or changed at any time
*/
public interface SingleResultCallback {
+ SingleResultCallback THEN_DO_NOTHING = (r, t) -> {};
+
/**
* Called when the function completes. This method must not complete abruptly, see {@link AsyncCallbackFunction} for more details.
*
diff --git a/driver-core/src/main/com/mongodb/internal/async/function/AsyncCallbackTriFunction.java b/driver-core/src/main/com/mongodb/internal/async/function/AsyncCallbackTriFunction.java
new file mode 100644
index 00000000000..0df5ff8c358
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/internal/async/function/AsyncCallbackTriFunction.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * 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 com.mongodb.internal.async.function;
+
+import com.mongodb.internal.async.SingleResultCallback;
+import com.mongodb.lang.Nullable;
+
+/**
+ * An {@linkplain AsyncCallbackFunction asynchronous callback-based function} of three parameters.
+ * This class is a callback-based.
+ *
+ * This class is not part of the public API and may be removed or changed at any time
+ *
+ * @param The type of the first parameter to the function.
+ * @param The type of the second parameter to the function.
+ * @param See {@link AsyncCallbackFunction}
+ * @see AsyncCallbackFunction
+ */
+@FunctionalInterface
+public interface AsyncCallbackTriFunction {
+ /**
+ * @param p1 The first {@code @}{@link Nullable} argument of the asynchronous function.
+ * @param p2 The second {@code @}{@link Nullable} argument of the asynchronous function.
+ * @see AsyncCallbackFunction#apply(Object, SingleResultCallback)
+ */
+ void apply(P1 p1, P2 p2, P3 p3, SingleResultCallback callback);
+}
diff --git a/driver-core/src/main/com/mongodb/internal/async/function/RetryState.java b/driver-core/src/main/com/mongodb/internal/async/function/RetryState.java
index e1cecf721fc..5c9f756a94d 100644
--- a/driver-core/src/main/com/mongodb/internal/async/function/RetryState.java
+++ b/driver-core/src/main/com/mongodb/internal/async/function/RetryState.java
@@ -47,7 +47,7 @@
@NotThreadSafe
public final class RetryState {
public static final int RETRIES = 1;
- private static final int INFINITE_ATTEMPTS = Integer.MAX_VALUE;
+ public static final int INFINITE_ATTEMPTS = Integer.MAX_VALUE;
private final LoopState loopState;
private final int attempts;
@@ -67,19 +67,16 @@ public final class RetryState {
*
*
* @param retries A positive number of allowed retries. {@link Integer#MAX_VALUE} is a special value interpreted as being unlimited.
- * @param timeoutContext A timeout context that will be used to determine if the operation has timed out.
+ * @param retryUntilTimeoutThrowsException // TODO-JAVA-5640 shouldn't a timeout always stop retries?
* @see #attempts()
*/
- public static RetryState withRetryableState(final int retries, final TimeoutContext timeoutContext) {
+ public static RetryState withRetryableState(final int retries, final boolean retryUntilTimeoutThrowsException) {
assertTrue(retries > 0);
- if (timeoutContext.hasTimeoutMS()){
- return new RetryState(INFINITE_ATTEMPTS, timeoutContext);
- }
- return new RetryState(retries, null);
+ return new RetryState(retries, retryUntilTimeoutThrowsException);
}
public static RetryState withNonRetryableState() {
- return new RetryState(0, null);
+ return new RetryState(0, false);
}
/**
@@ -94,19 +91,19 @@ public static RetryState withNonRetryableState() {
* @see #attempts()
*/
public RetryState(final TimeoutContext timeoutContext) {
- this(INFINITE_ATTEMPTS, timeoutContext);
+ this(INFINITE_ATTEMPTS, timeoutContext.hasTimeoutMS());
}
/**
* @param retries A non-negative number of allowed retries. {@link Integer#MAX_VALUE} is a special value interpreted as being unlimited.
- * @param timeoutContext A timeout context that will be used to determine if the operation has timed out.
+ * @param retryUntilTimeoutThrowsException
* @see #attempts()
*/
- private RetryState(final int retries, @Nullable final TimeoutContext timeoutContext) {
+ private RetryState(final int retries, final boolean retryUntilTimeoutThrowsException) {
assertTrue(retries >= 0);
loopState = new LoopState();
attempts = retries == INFINITE_ATTEMPTS ? INFINITE_ATTEMPTS : retries + 1;
- this.retryUntilTimeoutThrowsException = timeoutContext != null && timeoutContext.hasTimeoutMS();
+ this.retryUntilTimeoutThrowsException = retryUntilTimeoutThrowsException;
}
/**
diff --git a/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterAwareReadWriteBinding.java b/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterAwareReadWriteBinding.java
index c66dc321513..5de9df43174 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterAwareReadWriteBinding.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterAwareReadWriteBinding.java
@@ -18,6 +18,7 @@
import com.mongodb.ServerAddress;
import com.mongodb.internal.async.SingleResultCallback;
+import com.mongodb.internal.connection.OperationContext;
/**
* This class is not part of the public API and may be removed or changed at any time
@@ -30,7 +31,7 @@ public interface AsyncClusterAwareReadWriteBinding extends AsyncReadWriteBinding
* @param serverAddress the server address
* @param callback the to be passed the connection source
*/
- void getConnectionSource(ServerAddress serverAddress, SingleResultCallback callback);
+ void getConnectionSource(ServerAddress serverAddress, OperationContext operationContext, SingleResultCallback callback);
@Override
AsyncClusterAwareReadWriteBinding retain();
diff --git a/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterBinding.java b/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterBinding.java
index fd46261a6df..322ef374381 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterBinding.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/AsyncClusterBinding.java
@@ -25,6 +25,7 @@
import com.mongodb.internal.connection.AsyncConnection;
import com.mongodb.internal.connection.Cluster;
import com.mongodb.internal.connection.OperationContext;
+import com.mongodb.internal.connection.ReadConcernAwareNoOpSessionContext;
import com.mongodb.internal.connection.Server;
import com.mongodb.internal.selector.ReadPreferenceServerSelector;
import com.mongodb.internal.selector.ReadPreferenceWithFallbackServerSelector;
@@ -33,7 +34,6 @@
import com.mongodb.selector.ServerSelector;
import static com.mongodb.assertions.Assertions.notNull;
-import static java.util.concurrent.TimeUnit.NANOSECONDS;
/**
* A simple ReadWriteBinding implementation that supplies write connection sources bound to a possibly different primary each time, and a
@@ -44,24 +44,17 @@
public class AsyncClusterBinding extends AbstractReferenceCounted implements AsyncClusterAwareReadWriteBinding {
private final Cluster cluster;
private final ReadPreference readPreference;
- private final ReadConcern readConcern;
- private final OperationContext operationContext;
/**
* Creates an instance.
*
* @param cluster a non-null Cluster which will be used to select a server to bind to
* @param readPreference a non-null ReadPreference for read operations
- * @param readConcern a non-null read concern
- * @param operationContext the operation context
* This class is not part of the public API and may be removed or changed at any time
*/
- public AsyncClusterBinding(final Cluster cluster, final ReadPreference readPreference, final ReadConcern readConcern,
- final OperationContext operationContext) {
+ public AsyncClusterBinding(final Cluster cluster, final ReadPreference readPreference) {
this.cluster = notNull("cluster", cluster);
this.readPreference = notNull("readPreference", readPreference);
- this.readConcern = notNull("readConcern", readConcern);
- this.operationContext = notNull("operationContext", operationContext);
}
@Override
@@ -76,21 +69,18 @@ public ReadPreference getReadPreference() {
}
@Override
- public OperationContext getOperationContext() {
- return operationContext;
- }
-
- @Override
- public void getReadConnectionSource(final SingleResultCallback callback) {
- getAsyncClusterBindingConnectionSource(new ReadPreferenceServerSelector(readPreference), callback);
+ public void getReadConnectionSource(final OperationContext operationContext,
+ final SingleResultCallback callback) {
+ getAsyncClusterBindingConnectionSource(new ReadPreferenceServerSelector(readPreference), operationContext, callback);
}
@Override
public void getReadConnectionSource(final int minWireVersion, final ReadPreference fallbackReadPreference,
+ final OperationContext operationContext,
final SingleResultCallback callback) {
// Assume 5.0+ for load-balanced mode
if (cluster.getSettings().getMode() == ClusterConnectionMode.LOAD_BALANCED) {
- getReadConnectionSource(callback);
+ getReadConnectionSource(operationContext, callback);
} else {
ReadPreferenceWithFallbackServerSelector readPreferenceWithFallbackServerSelector
= new ReadPreferenceWithFallbackServerSelector(readPreference, minWireVersion, fallbackReadPreference);
@@ -106,16 +96,19 @@ public void getReadConnectionSource(final int minWireVersion, final ReadPreferen
}
@Override
- public void getWriteConnectionSource(final SingleResultCallback callback) {
- getAsyncClusterBindingConnectionSource(new WritableServerSelector(), callback);
+ public void getWriteConnectionSource(final OperationContext operationContext,
+ final SingleResultCallback callback) {
+ getAsyncClusterBindingConnectionSource(new WritableServerSelector(), operationContext, callback);
}
@Override
- public void getConnectionSource(final ServerAddress serverAddress, final SingleResultCallback callback) {
- getAsyncClusterBindingConnectionSource(new ServerAddressSelector(serverAddress), callback);
+ public void getConnectionSource(final ServerAddress serverAddress, final OperationContext operationContext,
+ final SingleResultCallback callback) {
+ getAsyncClusterBindingConnectionSource(new ServerAddressSelector(serverAddress), operationContext, callback);
}
private void getAsyncClusterBindingConnectionSource(final ServerSelector serverSelector,
+ final OperationContext operationContext,
final SingleResultCallback callback) {
cluster.selectServerAsync(serverSelector, operationContext, (result, t) -> {
if (t != null) {
@@ -132,12 +125,14 @@ private final class AsyncClusterBindingConnectionSource extends AbstractReferenc
private final ServerDescription serverDescription;
private final ReadPreference appliedReadPreference;
- private AsyncClusterBindingConnectionSource(final Server server, final ServerDescription serverDescription,
- final ReadPreference appliedReadPreference) {
+ private AsyncClusterBindingConnectionSource(final Server server,
+ final ServerDescription serverDescription,
+ final ReadPreference appliedReadPreference) {
this.server = server;
this.serverDescription = serverDescription;
this.appliedReadPreference = appliedReadPreference;
- operationContext.getTimeoutContext().minRoundTripTimeMS(NANOSECONDS.toMillis(serverDescription.getMinRoundTripTimeNanos()));
+ // TODO should be calculated externaly
+ // operationContext.getTimeoutContext().minRoundTripTimeMS(NANOSECONDS.toMillis(serverDescription.getMinRoundTripTimeNanos()));
AsyncClusterBinding.this.retain();
}
@@ -146,19 +141,18 @@ public ServerDescription getServerDescription() {
return serverDescription;
}
- @Override
- public OperationContext getOperationContext() {
- return operationContext;
- }
-
@Override
public ReadPreference getReadPreference() {
return appliedReadPreference;
}
@Override
- public void getConnection(final SingleResultCallback callback) {
- server.getConnectionAsync(operationContext, callback);
+ public void getConnection(final OperationContext operationContext, final SingleResultCallback callback) {
+ // The first read in a causally consistent session MUST not send afterClusterTime to the server
+ // (because the operationTime has not yet been determined). Therefore, we use ReadConcernAwareNoOpSessionContext to
+ // so that we do not advance clusterTime on ClientSession in given operationContext because it might not be yet set.
+ ReadConcern readConcern = operationContext.getSessionContext().getReadConcern();
+ server.getConnectionAsync(operationContext.withSessionContext(new ReadConcernAwareNoOpSessionContext(readConcern)), callback);
}
public AsyncConnectionSource retain() {
diff --git a/driver-core/src/main/com/mongodb/internal/binding/AsyncConnectionSource.java b/driver-core/src/main/com/mongodb/internal/binding/AsyncConnectionSource.java
index 5d70faf598e..aae7d6b7419 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/AsyncConnectionSource.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/AsyncConnectionSource.java
@@ -20,6 +20,7 @@
import com.mongodb.connection.ServerDescription;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.connection.AsyncConnection;
+import com.mongodb.internal.connection.OperationContext;
/**
* A source of connections to a single MongoDB server.
@@ -42,12 +43,7 @@ public interface AsyncConnectionSource extends BindingContext, ReferenceCounted
*/
ReadPreference getReadPreference();
- /**
- * Gets a connection from this source.
- *
- * @param callback the to be passed the connection
- */
- void getConnection(SingleResultCallback callback);
+ void getConnection(OperationContext operationContext, SingleResultCallback callback);
@Override
AsyncConnectionSource retain();
diff --git a/driver-core/src/main/com/mongodb/internal/binding/AsyncReadBinding.java b/driver-core/src/main/com/mongodb/internal/binding/AsyncReadBinding.java
index 633091b3efb..e6d5cdfbbcf 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/AsyncReadBinding.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/AsyncReadBinding.java
@@ -18,6 +18,7 @@
import com.mongodb.ReadPreference;
import com.mongodb.internal.async.SingleResultCallback;
+import com.mongodb.internal.connection.OperationContext;
/**
* An asynchronous factory of connection sources to servers that can be read from and that satisfy the specified read preference.
@@ -35,7 +36,7 @@ public interface AsyncReadBinding extends BindingContext, ReferenceCounted {
* Returns a connection source to a server that satisfies the read preference with which this instance is configured.
* @param callback the to be passed the connection source
*/
- void getReadConnectionSource(SingleResultCallback callback);
+ void getReadConnectionSource(OperationContext operationContext, SingleResultCallback callback);
/**
* Return a connection source that satisfies the read preference with which this instance is configured, if all connected servers have
@@ -48,6 +49,7 @@ public interface AsyncReadBinding extends BindingContext, ReferenceCounted {
* @see com.mongodb.internal.operation.AggregateToCollectionOperation
*/
void getReadConnectionSource(int minWireVersion, ReadPreference fallbackReadPreference,
+ OperationContext operationContext,
SingleResultCallback callback);
@Override
diff --git a/driver-core/src/main/com/mongodb/internal/binding/AsyncWriteBinding.java b/driver-core/src/main/com/mongodb/internal/binding/AsyncWriteBinding.java
index 39bdf4729c2..1dba1c245e6 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/AsyncWriteBinding.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/AsyncWriteBinding.java
@@ -17,6 +17,7 @@
package com.mongodb.internal.binding;
import com.mongodb.internal.async.SingleResultCallback;
+import com.mongodb.internal.connection.OperationContext;
/**
* An asynchronous factory of connection sources to servers that can be written to, e.g, a standalone, a mongos, or a replica set primary.
@@ -30,7 +31,7 @@ public interface AsyncWriteBinding extends BindingContext, ReferenceCounted {
*
* @param callback the to be passed the connection source
*/
- void getWriteConnectionSource(SingleResultCallback callback);
+ void getWriteConnectionSource(OperationContext operationContext, SingleResultCallback callback);
@Override
AsyncWriteBinding retain();
diff --git a/driver-core/src/main/com/mongodb/internal/binding/BindingContext.java b/driver-core/src/main/com/mongodb/internal/binding/BindingContext.java
index c10f0fb16ac..289d9070c5e 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/BindingContext.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/BindingContext.java
@@ -16,9 +16,6 @@
package com.mongodb.internal.binding;
-import com.mongodb.internal.connection.OperationContext;
-
-
/**
* This class is not part of the public API and may be removed or changed at any time
*/
@@ -29,5 +26,5 @@ public interface BindingContext {
*
* @return the operation context for the binding context.
*/
- OperationContext getOperationContext();
+ // OperationContext getOperationContext();
}
diff --git a/driver-core/src/main/com/mongodb/internal/binding/ClusterAwareReadWriteBinding.java b/driver-core/src/main/com/mongodb/internal/binding/ClusterAwareReadWriteBinding.java
index 8f7552341a7..b97b22c3a06 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/ClusterAwareReadWriteBinding.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/ClusterAwareReadWriteBinding.java
@@ -17,6 +17,7 @@
package com.mongodb.internal.binding;
import com.mongodb.ServerAddress;
+import com.mongodb.internal.connection.OperationContext;
/**
* This interface is not part of the public API and may be removed or changed at any time.
@@ -27,5 +28,5 @@ public interface ClusterAwareReadWriteBinding extends ReadWriteBinding {
* Returns a connection source to the specified server address.
* @return the connection source
*/
- ConnectionSource getConnectionSource(ServerAddress serverAddress);
+ ConnectionSource getConnectionSource(ServerAddress serverAddress, OperationContext operationContext);
}
diff --git a/driver-core/src/main/com/mongodb/internal/binding/ClusterBinding.java b/driver-core/src/main/com/mongodb/internal/binding/ClusterBinding.java
index cd3f8473bbb..d6f39d308ea 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/ClusterBinding.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/ClusterBinding.java
@@ -24,6 +24,7 @@
import com.mongodb.internal.connection.Cluster;
import com.mongodb.internal.connection.Connection;
import com.mongodb.internal.connection.OperationContext;
+import com.mongodb.internal.connection.ReadConcernAwareNoOpSessionContext;
import com.mongodb.internal.connection.Server;
import com.mongodb.internal.connection.ServerTuple;
import com.mongodb.internal.selector.ReadPreferenceServerSelector;
@@ -32,7 +33,6 @@
import com.mongodb.internal.selector.WritableServerSelector;
import static com.mongodb.assertions.Assertions.notNull;
-import static java.util.concurrent.TimeUnit.NANOSECONDS;
/**
* A simple ReadWriteBinding implementation that supplies write connection sources bound to a possibly different primary each time, and a
@@ -43,22 +43,15 @@
public class ClusterBinding extends AbstractReferenceCounted implements ClusterAwareReadWriteBinding {
private final Cluster cluster;
private final ReadPreference readPreference;
- private final ReadConcern readConcern;
- private final OperationContext operationContext;
/**
* Creates an instance.
* @param cluster a non-null Cluster which will be used to select a server to bind to
* @param readPreference a non-null ReadPreference for read operations
- * @param readConcern a non-null read concern
- * @param operationContext the operation context
*/
- public ClusterBinding(final Cluster cluster, final ReadPreference readPreference, final ReadConcern readConcern,
- final OperationContext operationContext) {
+ public ClusterBinding(final Cluster cluster, final ReadPreference readPreference) {
this.cluster = notNull("cluster", cluster);
this.readPreference = notNull("readPreference", readPreference);
- this.readConcern = notNull("readConcern", readConcern);
- this.operationContext = notNull("operationContext", operationContext);
}
@Override
@@ -73,36 +66,39 @@ public ReadPreference getReadPreference() {
}
@Override
- public OperationContext getOperationContext() {
- return operationContext;
+ public ConnectionSource getReadConnectionSource(final OperationContext operationContext) {
+ return new ClusterBindingConnectionSource(
+ cluster.selectServer(new ReadPreferenceServerSelector(readPreference), operationContext),
+ readPreference);
}
@Override
- public ConnectionSource getReadConnectionSource() {
- return new ClusterBindingConnectionSource(cluster.selectServer(new ReadPreferenceServerSelector(readPreference), operationContext), readPreference);
- }
-
- @Override
- public ConnectionSource getReadConnectionSource(final int minWireVersion, final ReadPreference fallbackReadPreference) {
+ public ConnectionSource getReadConnectionSource(final int minWireVersion, final ReadPreference fallbackReadPreference,
+ final OperationContext operationContext) {
// Assume 5.0+ for load-balanced mode
if (cluster.getSettings().getMode() == ClusterConnectionMode.LOAD_BALANCED) {
- return getReadConnectionSource();
+ return getReadConnectionSource(operationContext);
} else {
ReadPreferenceWithFallbackServerSelector readPreferenceWithFallbackServerSelector
= new ReadPreferenceWithFallbackServerSelector(readPreference, minWireVersion, fallbackReadPreference);
ServerTuple serverTuple = cluster.selectServer(readPreferenceWithFallbackServerSelector, operationContext);
- return new ClusterBindingConnectionSource(serverTuple, readPreferenceWithFallbackServerSelector.getAppliedReadPreference());
+ return new ClusterBindingConnectionSource(serverTuple,
+ readPreferenceWithFallbackServerSelector.getAppliedReadPreference());
}
}
@Override
- public ConnectionSource getWriteConnectionSource() {
- return new ClusterBindingConnectionSource(cluster.selectServer(new WritableServerSelector(), operationContext), readPreference);
+ public ConnectionSource getWriteConnectionSource(final OperationContext operationContext) {
+ return new ClusterBindingConnectionSource(
+ cluster.selectServer(new WritableServerSelector(), operationContext),
+ readPreference);
}
@Override
- public ConnectionSource getConnectionSource(final ServerAddress serverAddress) {
- return new ClusterBindingConnectionSource(cluster.selectServer(new ServerAddressSelector(serverAddress), operationContext), readPreference);
+ public ConnectionSource getConnectionSource(final ServerAddress serverAddress, final OperationContext operationContext) {
+ return new ClusterBindingConnectionSource(
+ cluster.selectServer(new ServerAddressSelector(serverAddress), operationContext),
+ readPreference);
}
private final class ClusterBindingConnectionSource extends AbstractReferenceCounted implements ConnectionSource {
@@ -114,7 +110,8 @@ private ClusterBindingConnectionSource(final ServerTuple serverTuple, final Read
this.server = serverTuple.getServer();
this.serverDescription = serverTuple.getServerDescription();
this.appliedReadPreference = appliedReadPreference;
- operationContext.getTimeoutContext().minRoundTripTimeMS(NANOSECONDS.toMillis(serverDescription.getMinRoundTripTimeNanos()));
+ //TODO THis has to be moved outside of the consutructor to the place where getConnectionSource is called to create a new OperationContet to use further
+ // operationContext.getTimeoutContext().minRoundTripTimeMS(NANOSECONDS.toMillis(serverDescription.getMinRoundTripTimeNanos()));
ClusterBinding.this.retain();
}
@@ -123,19 +120,18 @@ public ServerDescription getServerDescription() {
return serverDescription;
}
- @Override
- public OperationContext getOperationContext() {
- return operationContext;
- }
-
@Override
public ReadPreference getReadPreference() {
return appliedReadPreference;
}
@Override
- public Connection getConnection() {
- return server.getConnection(operationContext);
+ public Connection getConnection(final OperationContext operationContext) {
+ // The first read in a causally consistent session MUST not send afterClusterTime to the server
+ // (because the operationTime has not yet been determined). Therefore, we use ReadConcernAwareNoOpSessionContext to
+ // so that we do not advance clusterTime on ClientSession in given operationContext because it might not be yet set.
+ ReadConcern readConcern = operationContext.getSessionContext().getReadConcern();
+ return server.getConnection(operationContext.withSessionContext(new ReadConcernAwareNoOpSessionContext(readConcern)));
}
public ConnectionSource retain() {
diff --git a/driver-core/src/main/com/mongodb/internal/binding/ConnectionSource.java b/driver-core/src/main/com/mongodb/internal/binding/ConnectionSource.java
index 90c8b85cf16..5ba3c562895 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/ConnectionSource.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/ConnectionSource.java
@@ -19,19 +19,20 @@
import com.mongodb.ReadPreference;
import com.mongodb.connection.ServerDescription;
import com.mongodb.internal.connection.Connection;
+import com.mongodb.internal.connection.OperationContext;
/**
* A source of connections to a single MongoDB server.
*
* This class is not part of the public API and may be removed or changed at any time
*/
-public interface ConnectionSource extends BindingContext, ReferenceCounted {
+public interface ConnectionSource extends ReferenceCounted {
ServerDescription getServerDescription();
ReadPreference getReadPreference();
- Connection getConnection();
+ Connection getConnection(OperationContext operationContext);
@Override
ConnectionSource retain();
diff --git a/driver-core/src/main/com/mongodb/internal/binding/ReadBinding.java b/driver-core/src/main/com/mongodb/internal/binding/ReadBinding.java
index ffdde848382..67c10ccbc5c 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/ReadBinding.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/ReadBinding.java
@@ -17,6 +17,7 @@
package com.mongodb.internal.binding;
import com.mongodb.ReadPreference;
+import com.mongodb.internal.connection.OperationContext;
/**
* A factory of connection sources to servers that can be read from and that satisfy the specified read preference.
@@ -30,7 +31,7 @@ public interface ReadBinding extends BindingContext, ReferenceCounted {
* Returns a connection source to a server that satisfies the read preference with which this instance is configured.
* @return the connection source
*/
- ConnectionSource getReadConnectionSource();
+ ConnectionSource getReadConnectionSource(OperationContext operationContext);
/**
* Return a connection source that satisfies the read preference with which this instance is configured, if all connected servers have
@@ -42,7 +43,7 @@ public interface ReadBinding extends BindingContext, ReferenceCounted {
*
* @see com.mongodb.internal.operation.AggregateToCollectionOperation
*/
- ConnectionSource getReadConnectionSource(int minWireVersion, ReadPreference fallbackReadPreference);
+ ConnectionSource getReadConnectionSource(int minWireVersion, ReadPreference fallbackReadPreference, OperationContext operationContext);
@Override
ReadBinding retain();
diff --git a/driver-core/src/main/com/mongodb/internal/binding/SingleServerBinding.java b/driver-core/src/main/com/mongodb/internal/binding/SingleServerBinding.java
index 7d7e948c344..50497ae2526 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/SingleServerBinding.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/SingleServerBinding.java
@@ -35,23 +35,21 @@
public class SingleServerBinding extends AbstractReferenceCounted implements ReadWriteBinding {
private final Cluster cluster;
private final ServerAddress serverAddress;
- private final OperationContext operationContext;
/**
* Creates an instance, defaulting to {@link com.mongodb.ReadPreference#primary()} for reads.
* @param cluster a non-null Cluster which will be used to select a server to bind to
* @param serverAddress a non-null address of the server to bind to
- * @param operationContext the operation context
*/
- public SingleServerBinding(final Cluster cluster, final ServerAddress serverAddress, final OperationContext operationContext) {
+ public SingleServerBinding(final Cluster cluster, final ServerAddress serverAddress) {
this.cluster = notNull("cluster", cluster);
this.serverAddress = notNull("serverAddress", serverAddress);
- this.operationContext = notNull("operationContext", operationContext);
}
@Override
- public ConnectionSource getWriteConnectionSource() {
- return new SingleServerBindingConnectionSource();
+ public ConnectionSource getWriteConnectionSource(final OperationContext operationContext) {
+ ServerTuple serverTuple = cluster.selectServer(new ServerAddressSelector(serverAddress), operationContext);
+ return new SingleServerBindingConnectionSource(serverTuple);
}
@Override
@@ -60,20 +58,17 @@ public ReadPreference getReadPreference() {
}
@Override
- public ConnectionSource getReadConnectionSource() {
- return new SingleServerBindingConnectionSource();
+ public ConnectionSource getReadConnectionSource(final OperationContext operationContext) {
+ ServerTuple serverTuple = cluster.selectServer(new ServerAddressSelector(serverAddress), operationContext);
+ return new SingleServerBindingConnectionSource(serverTuple);
}
@Override
- public ConnectionSource getReadConnectionSource(final int minWireVersion, final ReadPreference fallbackReadPreference) {
+ public ConnectionSource getReadConnectionSource(final int minWireVersion, final ReadPreference fallbackReadPreference,
+ final OperationContext operationContext) {
throw new UnsupportedOperationException();
}
- @Override
- public OperationContext getOperationContext() {
- return operationContext;
- }
-
@Override
public SingleServerBinding retain() {
super.retain();
@@ -83,10 +78,9 @@ public SingleServerBinding retain() {
private final class SingleServerBindingConnectionSource extends AbstractReferenceCounted implements ConnectionSource {
private final ServerDescription serverDescription;
- private SingleServerBindingConnectionSource() {
+ private SingleServerBindingConnectionSource(final ServerTuple serverTuple) {
SingleServerBinding.this.retain();
- ServerTuple serverTuple = cluster.selectServer(new ServerAddressSelector(serverAddress), operationContext);
- serverDescription = serverTuple.getServerDescription();
+ this.serverDescription = serverTuple.getServerDescription();
}
@Override
@@ -94,18 +88,13 @@ public ServerDescription getServerDescription() {
return serverDescription;
}
- @Override
- public OperationContext getOperationContext() {
- return operationContext;
- }
-
@Override
public ReadPreference getReadPreference() {
return ReadPreference.primary();
}
@Override
- public Connection getConnection() {
+ public Connection getConnection(final OperationContext operationContext) {
return cluster
.selectServer(new ServerAddressSelector(serverAddress), operationContext)
.getServer()
diff --git a/driver-core/src/main/com/mongodb/internal/binding/WriteBinding.java b/driver-core/src/main/com/mongodb/internal/binding/WriteBinding.java
index b0ac674489c..beeee4c3bf2 100644
--- a/driver-core/src/main/com/mongodb/internal/binding/WriteBinding.java
+++ b/driver-core/src/main/com/mongodb/internal/binding/WriteBinding.java
@@ -16,6 +16,8 @@
package com.mongodb.internal.binding;
+import com.mongodb.internal.connection.OperationContext;
+
/**
* A factory of connection sources to servers that can be written to, e.g, a standalone, a mongos, or a replica set primary.
*
@@ -27,7 +29,7 @@ public interface WriteBinding extends BindingContext, ReferenceCounted {
*
* @return a connection source
*/
- ConnectionSource getWriteConnectionSource();
+ ConnectionSource getWriteConnectionSource(OperationContext operationContext);
@Override
WriteBinding retain();
diff --git a/driver-core/src/main/com/mongodb/internal/connection/CommandHelper.java b/driver-core/src/main/com/mongodb/internal/connection/CommandHelper.java
index fa7c1f0739d..9670db5af2c 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/CommandHelper.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/CommandHelper.java
@@ -129,7 +129,6 @@ private static CommandMessage getCommandMessage(final String database, final Bso
public static void applyMaxTimeMS(final TimeoutContext timeoutContext, final BsonDocument command) {
if (!timeoutContext.hasTimeoutMS()) {
command.append("maxTimeMS", new BsonInt64(timeoutContext.getTimeoutSettings().getMaxTimeMS()));
- timeoutContext.disableMaxTimeOverride();
}
}
diff --git a/driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java b/driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java
index 13e7ad987b5..ec611ee7b9b 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java
@@ -451,7 +451,7 @@ void doMaintenance() {
}
private boolean shouldEnsureMinSize() {
- return settings.getMinSize() > 0;
+ return settings.getMinSize() > -1;
}
private boolean shouldPrune(final UsageTrackingInternalConnection connection) {
diff --git a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java
index bf009aa1b07..f231f795a97 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnection.java
@@ -226,13 +226,15 @@ public int getGeneration() {
public void open(final OperationContext originalOperationContext) {
isTrue("Open already called", stream == null);
stream = streamFactory.create(serverId.getAddress());
+ OperationContext operationContext = originalOperationContext;
try {
- OperationContext operationContext = originalOperationContext
- .withTimeoutContext(originalOperationContext.getTimeoutContext().withComputedServerSelectionTimeoutContext());
-
+ //COMMENT given that we already use serverSelection timeout in SyncOperationHelper, this step is not needed.
+// OperationContext operationContext = originalOperationContext
+// .withTimeoutContext(originalOperationContext.getTimeoutContext().withComputedServerSelectionTimeoutContext());
stream.open(operationContext);
-
InternalConnectionInitializationDescription initializationDescription = connectionInitializer.startHandshake(this, operationContext);
+
+ operationContext = operationContext.withTimeoutContextOverride(TimeoutContext::withNewlyStartedTimeoutMaintenanceTimeout);
initAfterHandshakeStart(initializationDescription);
initializationDescription = connectionInitializer.finishHandshake(this, initializationDescription, operationContext);
@@ -250,9 +252,11 @@ public void open(final OperationContext originalOperationContext) {
@Override
public void openAsync(final OperationContext originalOperationContext, final SingleResultCallback callback) {
assertNull(stream);
+ OperationContext operationContext = originalOperationContext;
try {
- OperationContext operationContext = originalOperationContext
- .withTimeoutContext(originalOperationContext.getTimeoutContext().withComputedServerSelectionTimeoutContext());
+ //COMMENT given that we already use serverSelection timeout in SyncOperationHelper, this step is not needed.
+// OperationContext operationContext = originalOperationContext
+// .withTimeoutContext(originalOperationContext.getTimeoutContext().withComputedServerSelectionTimeoutContext());
stream = streamFactory.create(serverId.getAddress());
stream.openAsync(operationContext, new AsyncCompletionHandler() {
@@ -268,7 +272,8 @@ public void completed(@Nullable final Void aVoid) {
assertNotNull(initialResult);
initAfterHandshakeStart(initialResult);
connectionInitializer.finishHandshakeAsync(InternalStreamConnection.this,
- initialResult, operationContext, (completedResult, completedException) -> {
+ initialResult, operationContext.withTimeoutContextOverride(TimeoutContext::withNewlyStartedTimeoutMaintenanceTimeout),
+ (completedResult, completedException) -> {
if (completedException != null) {
close();
callback.onResult(null, completedException);
diff --git a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionInitializer.java b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionInitializer.java
index 79c21f33356..574a85669d0 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionInitializer.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/InternalStreamConnectionInitializer.java
@@ -144,8 +144,6 @@ private InternalConnectionInitializationDescription initializeConnectionDescript
helloResult = executeCommand("admin", helloCommandDocument, clusterConnectionMode, serverApi, internalConnection, operationContext);
} catch (MongoException e) {
throw mapHelloException(e);
- } finally {
- operationContext.getTimeoutContext().resetMaintenanceTimeout();
}
setSpeculativeAuthenticateResponse(helloResult);
return createInitializationDescription(helloResult, internalConnection, start);
diff --git a/driver-core/src/main/com/mongodb/internal/connection/OperationContext.java b/driver-core/src/main/com/mongodb/internal/connection/OperationContext.java
index 7e0de92da1d..ecceed7c099 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/OperationContext.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/OperationContext.java
@@ -15,6 +15,7 @@
*/
package com.mongodb.internal.connection;
+import com.mongodb.Function;
import com.mongodb.MongoConnectionPoolClearedException;
import com.mongodb.RequestContext;
import com.mongodb.ServerAddress;
@@ -33,6 +34,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import static java.util.stream.Collectors.toList;
@@ -160,6 +162,19 @@ public ServerDeprioritization getServerDeprioritization() {
return serverDeprioritization;
}
+ public OperationContext withNewlyStartedTimeout() {
+ TimeoutContext tc = this.timeoutContext.withNewlyStartedTimeout();
+ return this.withTimeoutContext(tc);
+ }
+
+ public OperationContext withMinRoundTripTime(final ServerDescription serverDescription) {
+ return this.withTimeoutContext(this.timeoutContext.withMinRoundTripTime(TimeUnit.NANOSECONDS.toMillis(serverDescription.getMinRoundTripTimeNanos())));
+ }
+
+ public OperationContext withTimeoutContextOverride(final Function timeoutContextOverrideFunction) {
+ return this.withTimeoutContext(timeoutContextOverrideFunction.apply(timeoutContext));
+ }
+
public static final class ServerDeprioritization {
@Nullable
private ServerAddress candidate;
diff --git a/driver-core/src/main/com/mongodb/internal/connection/SaslAuthenticator.java b/driver-core/src/main/com/mongodb/internal/connection/SaslAuthenticator.java
index eeee3a31abd..059f810c611 100644
--- a/driver-core/src/main/com/mongodb/internal/connection/SaslAuthenticator.java
+++ b/driver-core/src/main/com/mongodb/internal/connection/SaslAuthenticator.java
@@ -27,6 +27,7 @@
import com.mongodb.SubjectProvider;
import com.mongodb.connection.ClusterConnectionMode;
import com.mongodb.connection.ConnectionDescription;
+import com.mongodb.internal.TimeoutContext;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.diagnostics.logging.Logger;
import com.mongodb.internal.diagnostics.logging.Loggers;
@@ -63,12 +64,18 @@ abstract class SaslAuthenticator extends Authenticator implements SpeculativeAut
}
public void authenticate(final InternalConnection connection, final ConnectionDescription connectionDescription,
- final OperationContext operationContext) {
+ final OperationContext originalOperationContext) {
doAsSubject(() -> {
+ OperationContext operationContext = originalOperationContext;
SaslClient saslClient = createSaslClient(connection.getDescription().getServerAddress(), operationContext);
throwIfSaslClientIsNull(saslClient);
try {
- BsonDocument responseDocument = getNextSaslResponse(saslClient, connection, operationContext);
+ BsonDocument responseDocument = connection.opened() ? null : getSpeculativeAuthenticateResponse();
+ if (responseDocument == null) {
+ responseDocument = getNextSaslResponse(saslClient, connection, operationContext);
+ operationContext = operationContext.withTimeoutContextOverride(TimeoutContext::withNewlyStartedTimeoutMaintenanceTimeout);
+ }
+
BsonInt32 conversationId = responseDocument.getInt32("conversationId");
while (!(responseDocument.getBoolean("done")).getValue()) {
@@ -81,7 +88,7 @@ public void authenticate(final InternalConnection connection, final ConnectionDe
}
responseDocument = sendSaslContinue(conversationId, response, connection, operationContext);
- operationContext.getTimeoutContext().resetMaintenanceTimeout();
+ operationContext = operationContext.withTimeoutContextOverride(TimeoutContext::withNewlyStartedTimeoutMaintenanceTimeout);
}
if (!saslClient.isComplete()) {
saslClient.evaluateChallenge((responseDocument.getBinary("payload")).getData());
@@ -117,6 +124,9 @@ void authenticateAsync(final InternalConnection connection, final ConnectionDesc
public abstract String getMechanismName();
+ /**
+ * Does not send any commands to the server
+ */
protected abstract SaslClient createSaslClient(ServerAddress serverAddress, OperationContext operationContext);
protected void appendSaslStartOptions(final BsonDocument saslStartCommand) {
@@ -131,11 +141,6 @@ private void throwIfSaslClientIsNull(@Nullable final SaslClient saslClient) {
private BsonDocument getNextSaslResponse(final SaslClient saslClient, final InternalConnection connection,
final OperationContext operationContext) {
- BsonDocument response = connection.opened() ? null : getSpeculativeAuthenticateResponse();
- if (response != null) {
- return response;
- }
-
try {
byte[] serverResponse = saslClient.hasInitialResponse() ? saslClient.evaluateChallenge(new byte[0]) : null;
return sendSaslStart(serverResponse, connection, operationContext);
@@ -160,7 +165,9 @@ private void getNextSaslResponseAsync(final SaslClient saslClient, final Interna
if (result.getBoolean("done").getValue()) {
verifySaslClientComplete(saslClient, result, errHandlingCallback);
} else {
- new Continuator(saslClient, result, connection, operationContext, errHandlingCallback).start();
+ OperationContext saslContinueOperationContext =
+ operationContext.withTimeoutContextOverride(TimeoutContext::withNewlyStartedTimeoutMaintenanceTimeout);
+ new Continuator(saslClient, result, connection, saslContinueOperationContext, errHandlingCallback).start();
}
});
} else if (response.getBoolean("done").getValue()) {
@@ -232,22 +239,14 @@ private BsonDocument sendSaslStart(@Nullable final byte[] outToken, final Intern
final OperationContext operationContext) {
BsonDocument startDocument = createSaslStartCommandDocument(outToken);
appendSaslStartOptions(startDocument);
- try {
return executeCommand(getMongoCredential().getSource(), startDocument, getClusterConnectionMode(), getServerApi(), connection,
operationContext);
- } finally {
- operationContext.getTimeoutContext().resetMaintenanceTimeout();
- }
}
private BsonDocument sendSaslContinue(final BsonInt32 conversationId, final byte[] outToken, final InternalConnection connection,
final OperationContext operationContext) {
- try {
return executeCommand(getMongoCredential().getSource(), createSaslContinueDocument(conversationId, outToken),
getClusterConnectionMode(), getServerApi(), connection, operationContext);
- } finally {
- operationContext.getTimeoutContext().resetMaintenanceTimeout();
- }
}
private void sendSaslStartAsync(@Nullable final byte[] outToken, final InternalConnection connection,
@@ -256,19 +255,13 @@ private void sendSaslStartAsync(@Nullable final byte[] outToken, final InternalC
appendSaslStartOptions(startDocument);
executeCommandAsync(getMongoCredential().getSource(), startDocument, getClusterConnectionMode(), getServerApi(), connection,
- operationContext, (r, t) -> {
- operationContext.getTimeoutContext().resetMaintenanceTimeout();
- callback.onResult(r, t);
- });
+ operationContext, callback::onResult);
}
private void sendSaslContinueAsync(final BsonInt32 conversationId, final byte[] outToken, final InternalConnection connection,
final OperationContext operationContext, final SingleResultCallback callback) {
executeCommandAsync(getMongoCredential().getSource(), createSaslContinueDocument(conversationId, outToken),
- getClusterConnectionMode(), getServerApi(), connection, operationContext, (r, t) -> {
- operationContext.getTimeoutContext().resetMaintenanceTimeout();
- callback.onResult(r, t);
- });
+ getClusterConnectionMode(), getServerApi(), connection, operationContext, callback::onResult);
}
protected BsonDocument createSaslStartCommandDocument(@Nullable final byte[] outToken) {
@@ -323,7 +316,7 @@ private final class Continuator implements SingleResultCallback {
private final SaslClient saslClient;
private final BsonDocument saslStartDocument;
private final InternalConnection connection;
- private final OperationContext operationContext;
+ private OperationContext operationContext;
private final SingleResultCallback callback;
Continuator(final SaslClient saslClient, final BsonDocument saslStartDocument, final InternalConnection connection,
@@ -347,6 +340,7 @@ public void onResult(@Nullable final BsonDocument result, @Nullable final Throwa
verifySaslClientComplete(saslClient, result, callback);
disposeOfSaslClient(saslClient);
} else {
+ operationContext = operationContext.withTimeoutContextOverride(TimeoutContext::withNewlyStartedTimeoutMaintenanceTimeout);
continueConversation(result);
}
}
diff --git a/driver-core/src/main/com/mongodb/internal/operation/AbortTransactionOperation.java b/driver-core/src/main/com/mongodb/internal/operation/AbortTransactionOperation.java
index bc7e6655bc7..c2598a9bddd 100644
--- a/driver-core/src/main/com/mongodb/internal/operation/AbortTransactionOperation.java
+++ b/driver-core/src/main/com/mongodb/internal/operation/AbortTransactionOperation.java
@@ -51,9 +51,10 @@ public String getCommandName() {
@Override
CommandCreator getCommandCreator() {
return (operationContext, serverDescription, connectionDescription) -> {
- operationContext.getTimeoutContext().resetToDefaultMaxTime();
BsonDocument command = AbortTransactionOperation.super.getCommandCreator()
- .create(operationContext, serverDescription, connectionDescription);
+ .create(operationContext.withTimeoutContextOverride(TimeoutContext::withDefaultMaxTime),
+ serverDescription,
+ connectionDescription);
putIfNotNull(command, "recoveryToken", recoveryToken);
return command;
};
diff --git a/driver-core/src/main/com/mongodb/internal/operation/AbstractWriteSearchIndexOperation.java b/driver-core/src/main/com/mongodb/internal/operation/AbstractWriteSearchIndexOperation.java
index 6ebcfda6dbe..87092c02a24 100644
--- a/driver-core/src/main/com/mongodb/internal/operation/AbstractWriteSearchIndexOperation.java
+++ b/driver-core/src/main/com/mongodb/internal/operation/AbstractWriteSearchIndexOperation.java
@@ -22,6 +22,7 @@
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.binding.AsyncWriteBinding;
import com.mongodb.internal.binding.WriteBinding;
+import com.mongodb.internal.connection.OperationContext;
import com.mongodb.lang.Nullable;
import org.bson.BsonDocument;
@@ -45,12 +46,12 @@ abstract class AbstractWriteSearchIndexOperation implements WriteOperation
}
@Override
- public Void execute(final WriteBinding binding) {
- return withConnection(binding, connection -> {
+ public Void execute(final WriteBinding binding, final OperationContext operationContext) {
+ return withConnection(binding, operationContext, (connection, operationContextWithMinRtt) -> {
try {
- executeCommand(binding, namespace.getDatabaseName(), buildCommand(),
+ executeCommand(binding, operationContextWithMinRtt, namespace.getDatabaseName(), buildCommand(),
connection,
- writeConcernErrorTransformer(binding.getOperationContext().getTimeoutContext()));
+ writeConcernErrorTransformer(operationContextWithMinRtt.getTimeoutContext()));
} catch (MongoCommandException mongoCommandException) {
swallowOrThrow(mongoCommandException);
}
@@ -59,20 +60,21 @@ public Void execute(final WriteBinding binding) {
}
@Override
- public void executeAsync(final AsyncWriteBinding binding, final SingleResultCallback callback) {
- withAsyncSourceAndConnection(binding::getWriteConnectionSource, false, callback,
- (connectionSource, connection, cb) ->
- executeCommandAsync(binding, namespace.getDatabaseName(), buildCommand(), connection,
- writeConcernErrorTransformerAsync(binding.getOperationContext().getTimeoutContext()), (result, commandExecutionError) -> {
+ public void executeAsync(final AsyncWriteBinding binding, final OperationContext operationContext, final SingleResultCallback callback) {
+ withAsyncSourceAndConnection(binding::getWriteConnectionSource, false, operationContext, callback,
+ (connectionSource, connection, operationContextWithMinRtt, cb) ->
+ executeCommandAsync(binding, operationContextWithMinRtt, namespace.getDatabaseName(), buildCommand(), connection,
+ writeConcernErrorTransformerAsync(operationContextWithMinRtt.getTimeoutContext()), (result, commandExecutionError) -> {
try {
swallowOrThrow(commandExecutionError);
+ //TODO why call callback and not cb?
callback.onResult(result, null);
} catch (Throwable mongoCommandException) {
+ //TODO why call callback and not cb?
callback.onResult(null, mongoCommandException);
}
}
- )
- );
+ ));
}
/**
diff --git a/driver-core/src/main/com/mongodb/internal/operation/AggregateOperation.java b/driver-core/src/main/com/mongodb/internal/operation/AggregateOperation.java
index 1c9abfc68ca..8bbf52fe9ce 100644
--- a/driver-core/src/main/com/mongodb/internal/operation/AggregateOperation.java
+++ b/driver-core/src/main/com/mongodb/internal/operation/AggregateOperation.java
@@ -25,6 +25,7 @@
import com.mongodb.internal.binding.AsyncReadBinding;
import com.mongodb.internal.binding.ReadBinding;
import com.mongodb.internal.client.model.AggregationLevel;
+import com.mongodb.internal.connection.OperationContext;
import com.mongodb.lang.Nullable;
import org.bson.BsonDocument;
import org.bson.BsonValue;
@@ -141,13 +142,13 @@ public String getCommandName() {
}
@Override
- public BatchCursor execute(final ReadBinding binding) {
- return wrapped.execute(binding);
+ public BatchCursor execute(final ReadBinding binding, final OperationContext operationContext) {
+ return wrapped.execute(binding, operationContext);
}
@Override
- public void executeAsync(final AsyncReadBinding binding, final SingleResultCallback> callback) {
- wrapped.executeAsync(binding, callback);
+ public void executeAsync(final AsyncReadBinding binding, final OperationContext operationContext, final SingleResultCallback> callback) {
+ wrapped.executeAsync(binding, operationContext, callback);
}
@Override
@@ -156,7 +157,7 @@ public ReadOperationSimple asExplainableOperation(@Nullable final Explain
}
CommandReadOperation createExplainableOperation(@Nullable final ExplainVerbosity verbosity, final Decoder resultDecoder) {
- return new CommandReadOperation<>(getNamespace().getDatabaseName(), wrapped.getCommandName(),
+ return new ExplainCommandOperation<>(getNamespace().getDatabaseName(), getCommandName(),
(operationContext, serverDescription, connectionDescription) -> {
BsonDocument command = wrapped.getCommand(operationContext, UNKNOWN_WIRE_VERSION);
applyMaxTimeMS(operationContext.getTimeoutContext(), command);
diff --git a/driver-core/src/main/com/mongodb/internal/operation/AggregateOperationImpl.java b/driver-core/src/main/com/mongodb/internal/operation/AggregateOperationImpl.java
index 4c9bc3828b7..91cc5581599 100644
--- a/driver-core/src/main/com/mongodb/internal/operation/AggregateOperationImpl.java
+++ b/driver-core/src/main/com/mongodb/internal/operation/AggregateOperationImpl.java
@@ -47,7 +47,7 @@
import static com.mongodb.internal.operation.AsyncOperationHelper.executeRetryableReadAsync;
import static com.mongodb.internal.operation.CommandOperationHelper.CommandCreator;
import static com.mongodb.internal.operation.OperationHelper.LOGGER;
-import static com.mongodb.internal.operation.OperationHelper.setNonTailableCursorMaxTimeSupplier;
+import static com.mongodb.internal.operation.OperationHelper.applyTimeoutModeToOperationContext;
import static com.mongodb.internal.operation.OperationReadConcernHelper.appendReadConcernToCommand;
import static com.mongodb.internal.operation.SyncOperationHelper.CommandReadTransformer;
import static com.mongodb.internal.operation.SyncOperationHelper.executeRetryableRead;
@@ -192,16 +192,16 @@ public String getCommandName() {
}
@Override
- public BatchCursor execute(final ReadBinding binding) {
- return executeRetryableRead(binding, namespace.getDatabaseName(),
+ public BatchCursor execute(final ReadBinding binding, final OperationContext operationContext) {
+ return executeRetryableRead(binding, applyTimeoutModeToOperationContext(timeoutMode, operationContext), namespace.getDatabaseName(),
getCommandCreator(), CommandResultDocumentCodec.create(decoder, FIELD_NAMES_WITH_RESULT),
transformer(), retryReads);
}
@Override
- public void executeAsync(final AsyncReadBinding binding, final SingleResultCallback> callback) {
+ public void executeAsync(final AsyncReadBinding binding, final OperationContext operationContext, final SingleResultCallback> callback) {
SingleResultCallback> errHandlingCallback = errorHandlingCallback(callback, LOGGER);
- executeRetryableReadAsync(binding, namespace.getDatabaseName(),
+ executeRetryableReadAsync(binding, applyTimeoutModeToOperationContext(timeoutMode, operationContext), namespace.getDatabaseName(),
getCommandCreator(), CommandResultDocumentCodec.create(decoder, FIELD_NAMES_WITH_RESULT),
asyncTransformer(), retryReads,
errHandlingCallback);
@@ -216,7 +216,6 @@ BsonDocument getCommand(final OperationContext operationContext, final int maxWi
BsonDocument commandDocument = new BsonDocument(getCommandName(), aggregateTarget.create());
appendReadConcernToCommand(operationContext.getSessionContext(), maxWireVersion, commandDocument);
commandDocument.put("pipeline", pipelineCreator.create());
- setNonTailableCursorMaxTimeSupplier(timeoutMode, operationContext);
BsonDocument cursor = new BsonDocument();
if (batchSize != null) {
cursor.put("batchSize", new BsonInt32(batchSize));
@@ -242,15 +241,19 @@ BsonDocument getCommand(final OperationContext operationContext, final int maxWi
}
private CommandReadTransformer> transformer() {
- return (result, source, connection) ->
- new CommandBatchCursor<>(getTimeoutMode(), result, batchSize != null ? batchSize : 0,
- getMaxTimeForCursor(source.getOperationContext().getTimeoutContext()), decoder, comment, source, connection);
+ return (result, source, connection, operationContext) ->
+ new CommandBatchCursor<>(getTimeoutMode(), getMaxTimeForCursor(operationContext.getTimeoutContext()), operationContext, new CommandCoreCursor<>(
+ result, batchSize != null ? batchSize : 0,
+ decoder, comment, source, connection
+ ));
}
private CommandReadTransformerAsync> asyncTransformer() {
- return (result, source, connection) ->
- new AsyncCommandBatchCursor<>(getTimeoutMode(), result, batchSize != null ? batchSize : 0,
- getMaxTimeForCursor(source.getOperationContext().getTimeoutContext()), decoder, comment, source, connection);
+ return (result, source, connection, operationContext) ->
+ new AsyncCommandBatchCursor<>(getTimeoutMode(), getMaxTimeForCursor(operationContext.getTimeoutContext()),
+ operationContext, new AsyncCommandCoreCursor<>(
+ result, batchSize != null ? batchSize : 0, decoder, comment, source, connection
+ ));
}
private TimeoutMode getTimeoutMode() {
diff --git a/driver-core/src/main/com/mongodb/internal/operation/AggregateToCollectionOperation.java b/driver-core/src/main/com/mongodb/internal/operation/AggregateToCollectionOperation.java
index 16f33ad45e5..36e7e1aad5f 100644
--- a/driver-core/src/main/com/mongodb/internal/operation/AggregateToCollectionOperation.java
+++ b/driver-core/src/main/com/mongodb/internal/operation/AggregateToCollectionOperation.java
@@ -26,6 +26,7 @@
import com.mongodb.internal.binding.AsyncReadBinding;
import com.mongodb.internal.binding.ReadBinding;
import com.mongodb.internal.client.model.AggregationLevel;
+import com.mongodb.internal.connection.OperationContext;
import com.mongodb.lang.Nullable;
import org.bson.BsonArray;
import org.bson.BsonBoolean;
@@ -39,8 +40,10 @@
import static com.mongodb.assertions.Assertions.isTrueArgument;
import static com.mongodb.assertions.Assertions.notNull;
+import static com.mongodb.internal.operation.AsyncOperationHelper.CommandReadTransformerAsync;
import static com.mongodb.internal.operation.AsyncOperationHelper.executeRetryableReadAsync;
import static com.mongodb.internal.operation.ServerVersionHelper.FIVE_DOT_ZERO_WIRE_VERSION;
+import static com.mongodb.internal.operation.SyncOperationHelper.CommandReadTransformer;
import static com.mongodb.internal.operation.SyncOperationHelper.executeRetryableRead;
import static com.mongodb.internal.operation.WriteConcernHelper.appendWriteConcernToCommand;
import static com.mongodb.internal.operation.WriteConcernHelper.throwOnWriteConcernError;
@@ -158,30 +161,33 @@ public String getCommandName() {
}
@Override
- public Void execute(final ReadBinding binding) {
- return executeRetryableRead(binding,
- () -> binding.getReadConnectionSource(FIVE_DOT_ZERO_WIRE_VERSION, ReadPreference.primary()),
- namespace.getDatabaseName(),
- getCommandCreator(),
- new BsonDocumentCodec(), (result, source, connection) -> {
- throwOnWriteConcernError(result, connection.getDescription().getServerAddress(),
- connection.getDescription().getMaxWireVersion(), binding.getOperationContext().getTimeoutContext());
- return null;
- }, false);
+ public Void execute(final ReadBinding binding, final OperationContext operationContext) {
+ return executeRetryableRead(
+ operationContext,
+ (serverSelectionOperationContext) ->
+ binding.getReadConnectionSource(
+ FIVE_DOT_ZERO_WIRE_VERSION,
+ ReadPreference.primary(),
+ serverSelectionOperationContext),
+ namespace.getDatabaseName(),
+ getCommandCreator(),
+ new BsonDocumentCodec(), transformer(), false);
}
@Override
- public void executeAsync(final AsyncReadBinding binding, final SingleResultCallback callback) {
- executeRetryableReadAsync(binding,
- (connectionSourceCallback) ->
- binding.getReadConnectionSource(FIVE_DOT_ZERO_WIRE_VERSION, ReadPreference.primary(), connectionSourceCallback),
- namespace.getDatabaseName(),
- getCommandCreator(),
- new BsonDocumentCodec(), (result, source, connection) -> {
- throwOnWriteConcernError(result, connection.getDescription().getServerAddress(),
- connection.getDescription().getMaxWireVersion(), binding.getOperationContext().getTimeoutContext());
- return null;
- }, false, callback);
+ public void executeAsync(final AsyncReadBinding binding, final OperationContext operationContext,
+ final SingleResultCallback callback) {
+ executeRetryableReadAsync(
+ binding,
+ operationContext,
+ (serverSelectionOperationContext, connectionSourceCallback) ->
+ binding.getReadConnectionSource(FIVE_DOT_ZERO_WIRE_VERSION, ReadPreference.primary(), serverSelectionOperationContext, connectionSourceCallback),
+ namespace.getDatabaseName(),
+ getCommandCreator(),
+ new BsonDocumentCodec(),
+ asyncTransformer(),
+ false,
+ callback);
}
private CommandOperationHelper.CommandCreator getCommandCreator() {
@@ -220,4 +226,20 @@ private CommandOperationHelper.CommandCreator getCommandCreator() {
return commandDocument;
};
}
+
+ private static CommandReadTransformer transformer() {
+ return (result, source, connection, operationContext) -> {
+ throwOnWriteConcernError(result, connection.getDescription().getServerAddress(),
+ connection.getDescription().getMaxWireVersion(), operationContext.getTimeoutContext());
+ return null;
+ };
+ }
+
+ private static CommandReadTransformerAsync asyncTransformer() {
+ return (result, source, connection, operationContext) -> {
+ throwOnWriteConcernError(result, connection.getDescription().getServerAddress(),
+ connection.getDescription().getMaxWireVersion(), operationContext.getTimeoutContext());
+ return null;
+ };
+ }
}
diff --git a/driver-core/src/main/com/mongodb/internal/operation/AsyncChangeStreamBatchCursor.java b/driver-core/src/main/com/mongodb/internal/operation/AsyncChangeStreamBatchCursor.java
index a4cfbafedb6..78b09c36539 100644
--- a/driver-core/src/main/com/mongodb/internal/operation/AsyncChangeStreamBatchCursor.java
+++ b/driver-core/src/main/com/mongodb/internal/operation/AsyncChangeStreamBatchCursor.java
@@ -1,3 +1,5 @@
+package com.mongodb.internal.operation;
+
/*
* Copyright 2008-present MongoDB, Inc.
*
@@ -14,14 +16,13 @@
* limitations under the License.
*/
-package com.mongodb.internal.operation;
import com.mongodb.MongoException;
import com.mongodb.internal.TimeoutContext;
import com.mongodb.internal.async.AsyncAggregateResponseBatchCursor;
-import com.mongodb.internal.async.AsyncBatchCursor;
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.binding.AsyncReadBinding;
+import com.mongodb.internal.connection.OperationContext;
import com.mongodb.lang.NonNull;
import com.mongodb.lang.Nullable;
import org.bson.BsonDocument;
@@ -43,7 +44,7 @@
final class AsyncChangeStreamBatchCursor implements AsyncAggregateResponseBatchCursor {
private final AsyncReadBinding binding;
- private final TimeoutContext timeoutContext;
+ private OperationContext operationContext;
private final ChangeStreamOperation changeStreamOperation;
private final int maxWireVersion;
@@ -53,40 +54,41 @@ final class AsyncChangeStreamBatchCursor implements AsyncAggregateResponseBat
* {@code wrapped} containing {@code null} and {@link #isClosed} being {@code false}.
* This represents a situation in which the wrapped object was closed by {@code this} but {@code this} remained open.
*/
- private final AtomicReference> wrapped;
+ private final AtomicReference> wrapped;
private final AtomicBoolean isClosed;
AsyncChangeStreamBatchCursor(final ChangeStreamOperation changeStreamOperation,
- final AsyncCommandBatchCursor wrapped,
+ final AsyncCoreCursor wrapped,
final AsyncReadBinding binding,
+ final OperationContext operationContext,
@Nullable final BsonDocument resumeToken,
final int maxWireVersion) {
this.changeStreamOperation = changeStreamOperation;
this.wrapped = new AtomicReference<>(assertNotNull(wrapped));
this.binding = binding;
binding.retain();
- this.timeoutContext = binding.getOperationContext().getTimeoutContext();
+ this.operationContext = operationContext.withTimeoutContextOverride(TimeoutContext::withMaxTimeAsMaxAwaitTimeOverride);
this.resumeToken = resumeToken;
this.maxWireVersion = maxWireVersion;
isClosed = new AtomicBoolean();
}
@NonNull
- AsyncCommandBatchCursor getWrapped() {
+ AsyncCoreCursor getWrapped() {
return assertNotNull(wrapped.get());
}
@Override
public void next(final SingleResultCallback> callback) {
- resumeableOperation(AsyncBatchCursor::next, callback, false);
+ OperationContext operationContext = resetTimeout();
+ resumeableOperation(AsyncCoreCursor::next, callback, operationContext, false);
}
@Override
public void close() {
- timeoutContext.resetTimeoutIfPresent();
if (isClosed.compareAndSet(false, true)) {
try {
- nullifyAndCloseWrapped();
+ nullifyAndCloseWrapped(operationContext.withNewlyStartedTimeout());
} finally {
binding.release();
}
@@ -116,7 +118,7 @@ public boolean isClosed() {
}
private boolean wrappedClosedItself() {
- AsyncAggregateResponseBatchCursor observedWrapped = wrapped.get();
+ AsyncCoreCursor observedWrapped = wrapped.get();
return observedWrapped != null && observedWrapped.isClosed();
}
@@ -125,10 +127,10 @@ private boolean wrappedClosedItself() {
* if {@link #wrappedClosedItself()} observes a {@linkplain AsyncAggregateResponseBatchCursor#isClosed() closed} wrapped object,
* then it closed itself as opposed to being closed by {@code this}.
*/
- private void nullifyAndCloseWrapped() {
- AsyncAggregateResponseBatchCursor observedWrapped = wrapped.getAndSet(null);
+ private void nullifyAndCloseWrapped(final OperationContext operationContext) {
+ AsyncCoreCursor observedWrapped = wrapped.getAndSet(null);
if (observedWrapped != null) {
- observedWrapped.close();
+ observedWrapped.close(operationContext);
}
}
@@ -137,14 +139,14 @@ private void nullifyAndCloseWrapped() {
* {@code setWrappedOrCloseIt(AsyncCommandBatchCursor)} is called concurrently with or after (in the happens-before order)
* the method {@link #close()}.
*/
- private void setWrappedOrCloseIt(final AsyncCommandBatchCursor newValue) {
+ private void setWrappedOrCloseIt(final AsyncCoreCursor newValue, final OperationContext operationContext) {
if (isClosed()) {
assertNull(wrapped.get());
- newValue.close();
+ newValue.close(operationContext);
} else {
assertNull(wrapped.getAndSet(newValue));
if (isClosed()) {
- nullifyAndCloseWrapped();
+ nullifyAndCloseWrapped(operationContext);
}
}
}
@@ -169,7 +171,7 @@ public int getMaxWireVersion() {
return maxWireVersion;
}
- private void cachePostBatchResumeToken(final AsyncCommandBatchCursor cursor) {
+ private void cachePostBatchResumeToken(final AsyncCoreCursor cursor) {
BsonDocument resumeToken = cursor.getPostBatchResumeToken();
if (resumeToken != null) {
this.resumeToken = resumeToken;
@@ -177,19 +179,23 @@ private void cachePostBatchResumeToken(final AsyncCommandBatchCursor cursor, SingleResultCallback> callback);
+ void apply(AsyncCoreCursor cursor, OperationContext operationContext,
+ SingleResultCallback> callback);
}
- private void resumeableOperation(final AsyncBlock asyncBlock, final SingleResultCallback> callback, final boolean tryNext) {
- timeoutContext.resetTimeoutIfPresent();
+ private void resumeableOperation(final AsyncBlock asyncBlock,
+ final SingleResultCallback> callback,
+ final OperationContext operationContext,
+ final boolean tryNext) {
+ //timeoutContext.resetTimeoutIfPresent(); //FIXme it was a bug, we reset timeout on retry which is against the spec. Moved to next() method.
SingleResultCallback> errHandlingCallback = errorHandlingCallback(callback, LOGGER);
if (isClosed()) {
errHandlingCallback.onResult(null, new MongoException(format("%s called after the cursor was closed.",
tryNext ? "tryNext()" : "next()")));
return;
}
- AsyncCommandBatchCursor wrappedCursor = getWrapped();
- asyncBlock.apply(wrappedCursor, (result, t) -> {
+ AsyncCoreCursor wrappedCursor = getWrapped();
+ asyncBlock.apply(wrappedCursor, operationContext, (result, t) -> {
if (t == null) {
try {
List convertedResults;
@@ -206,8 +212,8 @@ private void resumeableOperation(final AsyncBlock asyncBlock, final SingleResult
} else {
cachePostBatchResumeToken(wrappedCursor);
if (isResumableError(t, maxWireVersion)) {
- nullifyAndCloseWrapped();
- retryOperation(asyncBlock, errHandlingCallback, tryNext);
+ nullifyAndCloseWrapped(operationContext);
+ retryOperation(asyncBlock, errHandlingCallback, operationContext, tryNext);
} else {
errHandlingCallback.onResult(null, t);
}
@@ -215,26 +221,29 @@ private void resumeableOperation(final AsyncBlock asyncBlock, final SingleResult
});
}
- private void retryOperation(final AsyncBlock asyncBlock, final SingleResultCallback> callback,
+ private void retryOperation(final AsyncBlock asyncBlock,
+ final SingleResultCallback> callback,
+ final OperationContext operationContext,
final boolean tryNext) {
- withAsyncReadConnectionSource(binding, (source, t) -> {
+ withAsyncReadConnectionSource(binding, operationContext, (source, t) -> {
if (t != null) {
callback.onResult(null, t);
} else {
changeStreamOperation.setChangeStreamOptionsForResume(resumeToken,
assertNotNull(source).getServerDescription().getMaxWireVersion());
source.release();
- changeStreamOperation.executeAsync(binding, (asyncBatchCursor, t1) -> {
+ changeStreamOperation.executeAsync(binding, operationContext, (asyncBatchCursor, t1) -> {
if (t1 != null) {
callback.onResult(null, t1);
} else {
try {
- setWrappedOrCloseIt(assertNotNull((AsyncChangeStreamBatchCursor) asyncBatchCursor).getWrapped());
+ setWrappedOrCloseIt(assertNotNull((AsyncChangeStreamBatchCursor) asyncBatchCursor).getWrapped(),
+ operationContext);
} finally {
try {
binding.release(); // release the new change stream batch cursor's reference to the binding
} finally {
- resumeableOperation(asyncBlock, callback, tryNext);
+ resumeableOperation(asyncBlock, callback, operationContext, tryNext);
}
}
}
@@ -242,4 +251,10 @@ private void retryOperation(final AsyncBlock asyncBlock, final SingleResultCallb
}
});
}
+
+ private OperationContext resetTimeout() {
+ operationContext = operationContext.withNewlyStartedTimeout();
+ return operationContext;
+ }
}
+
diff --git a/driver-core/src/main/com/mongodb/internal/operation/AsyncCommandBatchCursor.java b/driver-core/src/main/com/mongodb/internal/operation/AsyncCommandBatchCursor.java
index 942721a27ad..036d91e1530 100644
--- a/driver-core/src/main/com/mongodb/internal/operation/AsyncCommandBatchCursor.java
+++ b/driver-core/src/main/com/mongodb/internal/operation/AsyncCommandBatchCursor.java
@@ -16,354 +16,94 @@
package com.mongodb.internal.operation;
-import com.mongodb.MongoCommandException;
-import com.mongodb.MongoException;
-import com.mongodb.MongoNamespace;
-import com.mongodb.MongoOperationTimeoutException;
-import com.mongodb.MongoSocketException;
-import com.mongodb.ReadPreference;
-import com.mongodb.ServerAddress;
-import com.mongodb.ServerCursor;
-import com.mongodb.annotations.ThreadSafe;
import com.mongodb.client.cursor.TimeoutMode;
-import com.mongodb.connection.ConnectionDescription;
-import com.mongodb.connection.ServerType;
-import com.mongodb.internal.TimeoutContext;
-import com.mongodb.internal.VisibleForTesting;
import com.mongodb.internal.async.AsyncAggregateResponseBatchCursor;
import com.mongodb.internal.async.SingleResultCallback;
-import com.mongodb.internal.async.function.AsyncCallbackSupplier;
-import com.mongodb.internal.binding.AsyncConnectionSource;
-import com.mongodb.internal.connection.AsyncConnection;
-import com.mongodb.internal.connection.Connection;
import com.mongodb.internal.connection.OperationContext;
-import com.mongodb.internal.operation.AsyncOperationHelper.AsyncCallableConnectionWithCallback;
-import com.mongodb.internal.validator.NoOpFieldNameValidator;
import com.mongodb.lang.Nullable;
import org.bson.BsonDocument;
import org.bson.BsonTimestamp;
-import org.bson.BsonValue;
-import org.bson.codecs.BsonDocumentCodec;
-import org.bson.codecs.Decoder;
import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import static com.mongodb.assertions.Assertions.assertNotNull;
-import static com.mongodb.assertions.Assertions.assertTrue;
-import static com.mongodb.assertions.Assertions.doesNotThrow;
-import static com.mongodb.internal.async.AsyncRunnable.beginAsync;
-import static com.mongodb.internal.operation.CommandBatchCursorHelper.FIRST_BATCH;
-import static com.mongodb.internal.operation.CommandBatchCursorHelper.MESSAGE_IF_CLOSED_AS_CURSOR;
-import static com.mongodb.internal.operation.CommandBatchCursorHelper.NEXT_BATCH;
-import static com.mongodb.internal.operation.CommandBatchCursorHelper.getKillCursorsCommand;
-import static com.mongodb.internal.operation.CommandBatchCursorHelper.getMoreCommandDocument;
-import static com.mongodb.internal.operation.CommandBatchCursorHelper.logCommandCursorResult;
-import static com.mongodb.internal.operation.CommandBatchCursorHelper.translateCommandException;
-import static java.util.Collections.emptyList;
+public class AsyncCommandBatchCursor implements AsyncAggregateResponseBatchCursor {
-class AsyncCommandBatchCursor implements AsyncAggregateResponseBatchCursor {
-
- private final MongoNamespace namespace;
- private final Decoder decoder;
- @Nullable
- private final BsonValue comment;
- private final int maxWireVersion;
- private final boolean firstBatchEmpty;
- private final ResourceManager resourceManager;
- private final OperationContext operationContext;
private final TimeoutMode timeoutMode;
- private final AtomicBoolean processedInitial = new AtomicBoolean();
- private int batchSize;
- private volatile CommandCursorResult commandCursorResult;
- private boolean resetTimeoutWhenClosing;
+ private OperationContext operationContext;
+
+ private AsyncCoreCursor wrapped;
AsyncCommandBatchCursor(
final TimeoutMode timeoutMode,
- final BsonDocument commandCursorDocument,
- final int batchSize, final long maxTimeMS,
- final Decoder decoder,
- @Nullable final BsonValue comment,
- final AsyncConnectionSource connectionSource,
- final AsyncConnection connection) {
- ConnectionDescription connectionDescription = connection.getDescription();
- this.commandCursorResult = toCommandCursorResult(connectionDescription.getServerAddress(), FIRST_BATCH, commandCursorDocument);
- this.namespace = commandCursorResult.getNamespace();
- this.batchSize = batchSize;
- this.decoder = decoder;
- this.comment = comment;
- this.maxWireVersion = connectionDescription.getMaxWireVersion();
- this.firstBatchEmpty = commandCursorResult.getResults().isEmpty();
- operationContext = connectionSource.getOperationContext();
+ final long maxTimeMs,
+ final OperationContext operationContext,
+ final AsyncCoreCursor wrapped) {
+ this.operationContext = operationContext.withTimeoutContextOverride(timeoutContext ->
+ timeoutContext.withMaxTimeOverride(maxTimeMs));
this.timeoutMode = timeoutMode;
-
- operationContext.getTimeoutContext().setMaxTimeOverride(maxTimeMS);
-
- AsyncConnection connectionToPin = connectionSource.getServerDescription().getType() == ServerType.LOAD_BALANCER
- ? connection : null;
- resourceManager = new ResourceManager(namespace, connectionSource, connectionToPin, commandCursorResult.getServerCursor());
- resetTimeoutWhenClosing = true;
+ this.wrapped = wrapped;
}
@Override
public void next(final SingleResultCallback> callback) {
- resourceManager.execute(funcCallback -> {
- checkTimeoutModeAndResetTimeoutContextIfIteration();
- ServerCursor localServerCursor = resourceManager.getServerCursor();
- boolean serverCursorIsNull = localServerCursor == null;
- List batchResults = emptyList();
- if (!processedInitial.getAndSet(true) && !firstBatchEmpty) {
- batchResults = commandCursorResult.getResults();
- }
-
- if (serverCursorIsNull || !batchResults.isEmpty()) {
- funcCallback.onResult(batchResults, null);
- } else {
- getMore(localServerCursor, funcCallback);
- }
- }, callback);
+ resetTimeout();
+ wrapped.next(operationContext, callback);
}
@Override
- public boolean isClosed() {
- return !resourceManager.operable();
+ public void setBatchSize(final int batchSize) {
+ wrapped.setBatchSize(batchSize);
}
@Override
- public void setBatchSize(final int batchSize) {
- this.batchSize = batchSize;
+ public int getBatchSize() {
+ return wrapped.getBatchSize();
}
@Override
- public int getBatchSize() {
- return batchSize;
+ public boolean isClosed() {
+ return wrapped.isClosed();
}
@Override
public void close() {
- resourceManager.close();
+ wrapped.close(operationContext
+ .withTimeoutContextOverride(timeoutContext -> timeoutContext
+ .withNewlyStartedTimeout()
+ .withDefaultMaxTime()
+ ));
}
@Nullable
- @VisibleForTesting(otherwise = VisibleForTesting.AccessModifier.PRIVATE)
- ServerCursor getServerCursor() {
- if (!resourceManager.operable()) {
- return null;
- }
- return resourceManager.getServerCursor();
- }
-
@Override
public BsonDocument getPostBatchResumeToken() {
- return commandCursorResult.getPostBatchResumeToken();
+ return wrapped.getPostBatchResumeToken();
}
+ @Nullable
@Override
public BsonTimestamp getOperationTime() {
- return commandCursorResult.getOperationTime();
+ return wrapped.getOperationTime();
}
@Override
public boolean isFirstBatchEmpty() {
- return firstBatchEmpty;
+ return wrapped.isFirstBatchEmpty();
}
@Override
public int getMaxWireVersion() {
- return maxWireVersion;
+ return wrapped.getMaxWireVersion();
}
- void checkTimeoutModeAndResetTimeoutContextIfIteration() {
+ private void resetTimeout() {
if (timeoutMode == TimeoutMode.ITERATION) {
- operationContext.getTimeoutContext().resetTimeoutIfPresent();
+ operationContext = operationContext.withNewlyStartedTimeout();
}
}
- private void getMore(final ServerCursor cursor, final SingleResultCallback> callback) {
- resourceManager.executeWithConnection((connection, wrappedCallback) ->
- getMoreLoop(assertNotNull(connection), cursor, wrappedCallback), callback);
- }
-
- private void getMoreLoop(final AsyncConnection connection, final ServerCursor serverCursor,
- final SingleResultCallback> callback) {
- connection.commandAsync(namespace.getDatabaseName(),
- getMoreCommandDocument(serverCursor.getId(), connection.getDescription(), namespace, batchSize, comment),
- NoOpFieldNameValidator.INSTANCE, ReadPreference.primary(),
- CommandResultDocumentCodec.create(decoder, NEXT_BATCH),
- assertNotNull(resourceManager.getConnectionSource()).getOperationContext(),
- (commandResult, t) -> {
- if (t != null) {
- Throwable translatedException =
- t instanceof MongoCommandException
- ? translateCommandException((MongoCommandException) t, serverCursor)
- : t;
- callback.onResult(null, translatedException);
- return;
- }
- commandCursorResult = toCommandCursorResult(
- connection.getDescription().getServerAddress(), NEXT_BATCH, assertNotNull(commandResult));
- ServerCursor nextServerCursor = commandCursorResult.getServerCursor();
- resourceManager.setServerCursor(nextServerCursor);
- List nextBatch = commandCursorResult.getResults();
- if (nextServerCursor == null || !nextBatch.isEmpty()) {
- callback.onResult(nextBatch, null);
- return;
- }
-
- if (!resourceManager.operable()) {
- callback.onResult(emptyList(), null);
- return;
- }
-
- getMoreLoop(connection, nextServerCursor, callback);
- });
- }
-
- private CommandCursorResult toCommandCursorResult(final ServerAddress serverAddress, final String fieldNameContainingBatch,
- final BsonDocument commandCursorDocument) {
- CommandCursorResult commandCursorResult = new CommandCursorResult<>(serverAddress, fieldNameContainingBatch,
- commandCursorDocument);
- logCommandCursorResult(commandCursorResult);
- return commandCursorResult;
- }
-
- /**
- * Configures the cursor to {@link #close()}
- * without {@linkplain TimeoutContext#resetTimeoutIfPresent() resetting} its {@linkplain TimeoutContext#getTimeout() timeout}.
- * This is useful when managing the {@link #close()} behavior externally.
- */
- AsyncCommandBatchCursor disableTimeoutResetWhenClosing() {
- resetTimeoutWhenClosing = false;
- return this;
- }
-
- @ThreadSafe
- private final class ResourceManager extends CursorResourceManager {
- ResourceManager(
- final MongoNamespace namespace,
- final AsyncConnectionSource connectionSource,
- @Nullable final AsyncConnection connectionToPin,
- @Nullable final ServerCursor serverCursor) {
- super(namespace, connectionSource, connectionToPin, serverCursor);
- }
-
- /**
- * Thread-safe.
- * Executes {@code operation} within the {@link #tryStartOperation()}/{@link #endOperation()} bounds.
- */
- void execute(final AsyncCallbackSupplier operation, final SingleResultCallback callback) {
- boolean canStartOperation = doesNotThrow(this::tryStartOperation);
- if (!canStartOperation) {
- callback.onResult(null, new IllegalStateException(MESSAGE_IF_CLOSED_AS_CURSOR));
- } else {
- operation.whenComplete(() -> {
- endOperation();
- if (super.getServerCursor() == null) {
- // At this point all resources have been released,
- // but `isClose` may still be returning `false` if `close` have not been called.
- // Self-close to update the state managed by `ResourceManger`, and so that `isClosed` return `true`.
- close();
- }
- }).get(callback);
- }
- }
-
- @Override
- void markAsPinned(final AsyncConnection connectionToPin, final Connection.PinningMode pinningMode) {
- connectionToPin.markAsPinned(pinningMode);
- }
-
- @Override
- void doClose() {
- TimeoutContext timeoutContext = operationContext.getTimeoutContext();
- timeoutContext.resetToDefaultMaxTime();
- SingleResultCallback thenDoNothing = (r, t) -> {};
- if (resetTimeoutWhenClosing) {
- timeoutContext.doWithResetTimeout(this::releaseResourcesAsync, thenDoNothing);
- } else {
- releaseResourcesAsync(thenDoNothing);
- }
- }
-
- private void releaseResourcesAsync(final SingleResultCallback callback) {
- beginAsync().thenRunTryCatchAsyncBlocks(c -> {
- if (isSkipReleasingServerResourcesOnClose()) {
- unsetServerCursor();
- }
- if (super.getServerCursor() != null) {
- beginAsync().thenSupply(c2 -> {
- getConnection(c2);
- }).thenConsume((connection, c3) -> {
- beginAsync().thenRun(c4 -> {
- releaseServerResourcesAsync(connection, c4);
- }).thenAlwaysRunAndFinish(() -> {
- connection.release();
- }, c3);
- }).finish(c);
- } else {
- c.complete(c);
- }
- }, MongoException.class, (e, c5) -> {
- c5.complete(c5); // ignore exceptions when releasing server resources
- }).thenAlwaysRunAndFinish(() -> {
- // guarantee that regardless of exceptions, `serverCursor` is null and client resources are released
- unsetServerCursor();
- releaseClientResources();
- }, callback);
- }
-
- void executeWithConnection(final AsyncCallableConnectionWithCallback callable, final SingleResultCallback callback) {
- getConnection((connection, t) -> {
- if (t != null) {
- callback.onResult(null, t);
- return;
- }
- callable.call(assertNotNull(connection), (result, t1) -> {
- if (t1 != null) {
- handleException(connection, t1);
- }
- connection.release();
- callback.onResult(result, t1);
- });
- });
- }
-
- private void handleException(final AsyncConnection connection, final Throwable exception) {
- if (exception instanceof MongoOperationTimeoutException && exception.getCause() instanceof MongoSocketException) {
- onCorruptedConnection(connection, (MongoSocketException) exception.getCause());
- } else if (exception instanceof MongoSocketException) {
- onCorruptedConnection(connection, (MongoSocketException) exception);
- }
- }
-
- private void getConnection(final SingleResultCallback callback) {
- assertTrue(getState() != State.IDLE);
- AsyncConnection pinnedConnection = getPinnedConnection();
- if (pinnedConnection != null) {
- callback.onResult(assertNotNull(pinnedConnection).retain(), null);
- } else {
- assertNotNull(getConnectionSource()).getConnection(callback);
- }
- }
-
- private void releaseServerResourcesAsync(final AsyncConnection connection, final SingleResultCallback callback) {
- beginAsync().thenRun((c) -> {
- ServerCursor localServerCursor = super.getServerCursor();
- if (localServerCursor != null) {
- killServerCursorAsync(getNamespace(), localServerCursor, connection, callback);
- } else {
- c.complete(c);
- }
- }).thenAlwaysRunAndFinish(() -> {
- unsetServerCursor();
- }, callback);
- }
-
- private void killServerCursorAsync(final MongoNamespace namespace, final ServerCursor localServerCursor,
- final AsyncConnection localConnection, final SingleResultCallback callback) {
- localConnection.commandAsync(namespace.getDatabaseName(), getKillCursorsCommand(namespace, localServerCursor),
- NoOpFieldNameValidator.INSTANCE, ReadPreference.primary(), new BsonDocumentCodec(),
- operationContext, (r, t) -> callback.onResult(null, null));
- }
+ AsyncCoreCursor getWrapped() {
+ return wrapped;
}
}
+
diff --git a/driver-core/src/main/com/mongodb/internal/operation/AsyncCommandCoreCursor.java b/driver-core/src/main/com/mongodb/internal/operation/AsyncCommandCoreCursor.java
new file mode 100644
index 00000000000..754c63208fd
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/internal/operation/AsyncCommandCoreCursor.java
@@ -0,0 +1,348 @@
+package com.mongodb.internal.operation;
+
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * 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.
+ */
+
+
+import com.mongodb.MongoCommandException;
+import com.mongodb.MongoException;
+import com.mongodb.MongoNamespace;
+import com.mongodb.MongoOperationTimeoutException;
+import com.mongodb.MongoSocketException;
+import com.mongodb.ReadPreference;
+import com.mongodb.ServerAddress;
+import com.mongodb.ServerCursor;
+import com.mongodb.annotations.ThreadSafe;
+import com.mongodb.connection.ConnectionDescription;
+import com.mongodb.connection.ServerType;
+import com.mongodb.internal.async.SingleResultCallback;
+import com.mongodb.internal.async.function.AsyncCallbackSupplier;
+import com.mongodb.internal.binding.AsyncConnectionSource;
+import com.mongodb.internal.connection.AsyncConnection;
+import com.mongodb.internal.connection.Connection;
+import com.mongodb.internal.connection.OperationContext;
+import com.mongodb.internal.operation.AsyncOperationHelper.AsyncCallableConnectionWithCallback;
+import com.mongodb.internal.validator.NoOpFieldNameValidator;
+import com.mongodb.lang.Nullable;
+import org.bson.BsonDocument;
+import org.bson.BsonTimestamp;
+import org.bson.BsonValue;
+import org.bson.codecs.BsonDocumentCodec;
+import org.bson.codecs.Decoder;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static com.mongodb.assertions.Assertions.assertNotNull;
+import static com.mongodb.assertions.Assertions.assertTrue;
+import static com.mongodb.assertions.Assertions.doesNotThrow;
+import static com.mongodb.internal.async.AsyncRunnable.beginAsync;
+import static com.mongodb.internal.async.SingleResultCallback.THEN_DO_NOTHING;
+import static com.mongodb.internal.operation.CommandBatchCursorHelper.FIRST_BATCH;
+import static com.mongodb.internal.operation.CommandBatchCursorHelper.MESSAGE_IF_CLOSED_AS_CURSOR;
+import static com.mongodb.internal.operation.CommandBatchCursorHelper.NEXT_BATCH;
+import static com.mongodb.internal.operation.CommandBatchCursorHelper.getKillCursorsCommand;
+import static com.mongodb.internal.operation.CommandBatchCursorHelper.getMoreCommandDocument;
+import static com.mongodb.internal.operation.CommandBatchCursorHelper.logCommandCursorResult;
+import static com.mongodb.internal.operation.CommandBatchCursorHelper.translateCommandException;
+import static java.util.Collections.emptyList;
+
+class AsyncCommandCoreCursor implements AsyncCoreCursor {
+
+ private final MongoNamespace namespace;
+ private final Decoder decoder;
+ @Nullable
+ private final BsonValue comment;
+ private final int maxWireVersion;
+ private final boolean firstBatchEmpty;
+ private final ResourceManager resourceManager;
+ private final AtomicBoolean processedInitial = new AtomicBoolean();
+ private int batchSize;
+ private volatile CommandCursorResult commandCursorResult;
+
+ AsyncCommandCoreCursor(
+ final BsonDocument commandCursorDocument,
+ final int batchSize,
+ final Decoder decoder,
+ @Nullable final BsonValue comment,
+ final AsyncConnectionSource connectionSource,
+ final AsyncConnection connection) {
+ ConnectionDescription connectionDescription = connection.getDescription();
+ this.commandCursorResult = toCommandCursorResult(connectionDescription.getServerAddress(), FIRST_BATCH, commandCursorDocument);
+ this.namespace = commandCursorResult.getNamespace();
+ this.batchSize = batchSize;
+ this.decoder = decoder;
+ this.comment = comment;
+ this.maxWireVersion = connectionDescription.getMaxWireVersion();
+ this.firstBatchEmpty = commandCursorResult.getResults().isEmpty();
+ AsyncConnection connectionToPin = connectionSource.getServerDescription().getType() == ServerType.LOAD_BALANCER
+ ? connection : null;
+ resourceManager = new ResourceManager(namespace, connectionSource, connectionToPin, commandCursorResult.getServerCursor());
+ }
+
+ @Override
+ public void next(final OperationContext operationContext, final SingleResultCallback> callback) {
+ resourceManager.execute(funcCallback -> {
+ //checkTimeoutModeAndResetTimeoutContextIfIteration(); //FIXME it was a bug? we should have reset the timeout when next was request to execute connection checkout and subsequent read wait on one timeout
+ ServerCursor localServerCursor = resourceManager.getServerCursor();
+ boolean serverCursorIsNull = localServerCursor == null;
+ List batchResults = emptyList();
+ if (!processedInitial.getAndSet(true) && !firstBatchEmpty) {
+ batchResults = commandCursorResult.getResults();
+ }
+
+ if (serverCursorIsNull || !batchResults.isEmpty()) {
+ funcCallback.onResult(batchResults, null);
+ } else {
+ getMore(localServerCursor, operationContext, funcCallback);
+ }
+ }, operationContext, callback);
+ }
+
+ @Override
+ public boolean isClosed() {
+ return !resourceManager.operable();
+ }
+
+ @Override
+ public void setBatchSize(final int batchSize) {
+ this.batchSize = batchSize;
+ }
+
+ @Override
+ public int getBatchSize() {
+ return batchSize;
+ }
+
+ @Override
+ public void close(final OperationContext operationContext) {
+ resourceManager.close(operationContext);
+ }
+
+ @Nullable
+ @Override
+ public ServerCursor getServerCursor() {
+ if (!resourceManager.operable()) {
+ return null;
+ }
+ return resourceManager.getServerCursor();
+ }
+
+ @Override
+ public BsonDocument getPostBatchResumeToken() {
+ return commandCursorResult.getPostBatchResumeToken();
+ }
+
+ @Override
+ public BsonTimestamp getOperationTime() {
+ return commandCursorResult.getOperationTime();
+ }
+
+ @Override
+ public boolean isFirstBatchEmpty() {
+ return firstBatchEmpty;
+ }
+
+ @Override
+ public int getMaxWireVersion() {
+ return maxWireVersion;
+ }
+
+ private void getMore(final ServerCursor cursor, final OperationContext operationContext, final SingleResultCallback> callback) {
+ resourceManager.executeWithConnection(operationContext, (connection, wrappedCallback) ->
+ getMoreLoop(assertNotNull(connection), cursor, operationContext, wrappedCallback), callback);
+ }
+
+ private void getMoreLoop(final AsyncConnection connection, final ServerCursor serverCursor,
+ final OperationContext operationContext,
+ final SingleResultCallback> callback) {
+ connection.commandAsync(namespace.getDatabaseName(),
+ getMoreCommandDocument(serverCursor.getId(), connection.getDescription(), namespace, batchSize, comment),
+ NoOpFieldNameValidator.INSTANCE, ReadPreference.primary(),
+ CommandResultDocumentCodec.create(decoder, NEXT_BATCH),
+ operationContext,
+ (commandResult, t) -> {
+ if (t != null) {
+ Throwable translatedException =
+ t instanceof MongoCommandException
+ ? translateCommandException((MongoCommandException) t, serverCursor)
+ : t;
+ callback.onResult(null, translatedException);
+ return;
+ }
+ commandCursorResult = toCommandCursorResult(
+ connection.getDescription().getServerAddress(), NEXT_BATCH, assertNotNull(commandResult));
+ ServerCursor nextServerCursor = commandCursorResult.getServerCursor();
+ resourceManager.setServerCursor(nextServerCursor);
+ List nextBatch = commandCursorResult.getResults();
+ if (nextServerCursor == null || !nextBatch.isEmpty()) {
+ callback.onResult(nextBatch, null);
+ return;
+ }
+
+ if (!resourceManager.operable()) {
+ callback.onResult(emptyList(), null);
+ return;
+ }
+
+ getMoreLoop(connection, nextServerCursor, operationContext, callback);
+ });
+ }
+
+ private CommandCursorResult toCommandCursorResult(final ServerAddress serverAddress, final String fieldNameContainingBatch,
+ final BsonDocument commandCursorDocument) {
+ CommandCursorResult commandCursorResult = new CommandCursorResult<>(serverAddress, fieldNameContainingBatch,
+ commandCursorDocument);
+ logCommandCursorResult(commandCursorResult);
+ return commandCursorResult;
+ }
+
+ @ThreadSafe
+ private final class ResourceManager extends CursorResourceManagerNew {
+ ResourceManager(
+ final MongoNamespace namespace,
+ final AsyncConnectionSource connectionSource,
+ @Nullable final AsyncConnection connectionToPin,
+ @Nullable final ServerCursor serverCursor) {
+ super(namespace, connectionSource, connectionToPin, serverCursor);
+ }
+
+ /**
+ * Thread-safe.
+ */
+ void execute(final AsyncCallbackSupplier operation, final OperationContext operationContext, final SingleResultCallback callback) {
+ boolean canStartOperation = doesNotThrow(this::tryStartOperation);
+ if (!canStartOperation) {
+ callback.onResult(null, new IllegalStateException(MESSAGE_IF_CLOSED_AS_CURSOR));
+ } else {
+ operation.whenComplete(() -> {
+ endOperation(operationContext);
+ if (super.getServerCursor() == null) {
+ // At this point all resources have been released,
+ // but `isClose` may still be returning `false` if `close` have not been called.
+ // Self-close to update the state managed by `ResourceManger`, and so that `isClosed` return `true`.
+ close(operationContext);
+ }
+ }).get(callback);
+ }
+ }
+
+ @Override
+ void markAsPinned(final AsyncConnection connectionToPin, final Connection.PinningMode pinningMode) {
+ connectionToPin.markAsPinned(pinningMode);
+ }
+
+ @Override
+ void doClose(final OperationContext operationContext) {
+ releaseResourcesAsync(operationContext, THEN_DO_NOTHING);
+ }
+
+ private void releaseResourcesAsync(final OperationContext operationContext, final SingleResultCallback callback) {
+ beginAsync().thenRunTryCatchAsyncBlocks(c -> {
+ if (isSkipReleasingServerResourcesOnClose()) {
+ unsetServerCursor();
+ }
+ if (super.getServerCursor() != null) {
+ beginAsync().thenSupply(c2 -> {
+ getConnection(operationContext, c2);
+ }).thenConsume((connection, c3) -> {
+ beginAsync().thenRun(c4 -> {
+ releaseServerResourcesAsync(connection, operationContext, c4);
+ }).thenAlwaysRunAndFinish(() -> {
+ connection.release();
+ }, c3);
+ }).finish(c);
+ } else {
+ c.complete(c);
+ }
+ }, MongoException.class, (e, c5) -> {
+ c5.complete(c5); // ignore exceptions when releasing server resources
+ }).thenAlwaysRunAndFinish(() -> {
+ // guarantee that regardless of exceptions, `serverCursor` is null and client resources are released
+ unsetServerCursor();
+ releaseClientResources();
+ }, callback);
+ }
+
+ void executeWithConnection(final OperationContext operationContext, final AsyncCallableConnectionWithCallback callable,
+ final SingleResultCallback callback) {
+ getConnection(operationContext, (connection, t) -> {
+ if (t != null) {
+ callback.onResult(null, t);
+ return;
+ }
+ callable.call(assertNotNull(connection), (result, t1) -> {
+ if (t1 != null) {
+ handleException(connection, t1);
+ }
+ connection.release();
+ callback.onResult(result, t1);
+ });
+ });
+ }
+
+ private void handleException(final AsyncConnection connection, final Throwable exception) {
+ if (exception instanceof MongoOperationTimeoutException && exception.getCause() instanceof MongoSocketException) {
+ onCorruptedConnection(connection, (MongoSocketException) exception.getCause());
+ } else if (exception instanceof MongoSocketException) {
+ onCorruptedConnection(connection, (MongoSocketException) exception);
+ }
+ }
+
+ private void getConnection(final OperationContext operationContext, final SingleResultCallback callback) {
+ assertTrue(getState() != State.IDLE);
+ AsyncConnection pinnedConnection = getPinnedConnection();
+ if (pinnedConnection != null) {
+ callback.onResult(assertNotNull(pinnedConnection).retain(), null);
+ } else {
+ assertNotNull(getConnectionSource()).getConnection(operationContext, callback);
+ }
+ }
+
+ private void releaseServerResourcesAsync(final AsyncConnection connection, final OperationContext operationContext,
+ final SingleResultCallback callback) {
+ beginAsync().thenRun((c) -> {
+ ServerCursor localServerCursor = super.getServerCursor();
+ if (localServerCursor != null) {
+ killServerCursorAsync(getNamespace(), localServerCursor, connection, operationContext, callback);
+ } else {
+ c.complete(c);
+ }
+ }).thenAlwaysRunAndFinish(() -> {
+ unsetServerCursor();
+ }, callback);
+ }
+
+ private void killServerCursorAsync(
+ final MongoNamespace namespace,
+ final ServerCursor localServerCursor,
+ final AsyncConnection localConnection,
+ final OperationContext operationContext,
+ final SingleResultCallback callback) {
+ beginAsync().thenRun(c -> {
+ localConnection.commandAsync(
+ namespace.getDatabaseName(),
+ getKillCursorsCommand(namespace, localServerCursor),
+ NoOpFieldNameValidator.INSTANCE,
+ ReadPreference.primary(),
+ new BsonDocumentCodec(),
+ operationContext,
+ (r, t) -> c.complete(c));
+ }).finish(callback);
+ }
+ }
+}
+
diff --git a/driver-core/src/main/com/mongodb/internal/operation/AsyncCoreCursor.java b/driver-core/src/main/com/mongodb/internal/operation/AsyncCoreCursor.java
new file mode 100644
index 00000000000..c689d48f7df
--- /dev/null
+++ b/driver-core/src/main/com/mongodb/internal/operation/AsyncCoreCursor.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2008-present MongoDB, Inc.
+ *
+ * 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 com.mongodb.internal.operation;
+
+import com.mongodb.ServerCursor;
+import com.mongodb.internal.async.AsyncBatchCursor;
+import com.mongodb.internal.async.SingleResultCallback;
+import com.mongodb.internal.connection.OperationContext;
+import com.mongodb.lang.Nullable;
+import org.bson.BsonDocument;
+import org.bson.BsonTimestamp;
+
+import java.util.List;
+
+public interface AsyncCoreCursor {
+ void close(OperationContext operationContext);
+ void next(OperationContext operationContext, SingleResultCallback> callback);
+
+ /**
+ * Sets the batch size to use when requesting the next batch. This is the number of documents to request in the next batch.
+ *
+ * @param batchSize the non-negative batch size. 0 means to use the server default.
+ */
+ void setBatchSize(int batchSize);
+
+ /**
+ * Gets the batch size to use when requesting the next batch. This is the number of documents to request in the next batch.
+ *
+ * @return the non-negative batch size. 0 means to use the server default.
+ */
+ int getBatchSize();
+
+ @Nullable
+ ServerCursor getServerCursor();
+
+
+ @Nullable
+ BsonDocument getPostBatchResumeToken();
+
+ @Nullable
+ BsonTimestamp getOperationTime();
+
+ boolean isFirstBatchEmpty();
+
+ int getMaxWireVersion();
+
+
+ /**
+ * Implementations of {@link AsyncBatchCursor} are allowed to close themselves, see {@link #close()} for more details.
+ *
+ * @return {@code true} if {@code this} has been closed or has closed itself.
+ */
+ boolean isClosed();
+}
diff --git a/driver-core/src/main/com/mongodb/internal/operation/AsyncOperationHelper.java b/driver-core/src/main/com/mongodb/internal/operation/AsyncOperationHelper.java
index f158b3944ae..7b134df2ee8 100644
--- a/driver-core/src/main/com/mongodb/internal/operation/AsyncOperationHelper.java
+++ b/driver-core/src/main/com/mongodb/internal/operation/AsyncOperationHelper.java
@@ -21,12 +21,13 @@
import com.mongodb.ReadPreference;
import com.mongodb.assertions.Assertions;
import com.mongodb.client.cursor.TimeoutMode;
+import com.mongodb.connection.ServerDescription;
import com.mongodb.internal.TimeoutContext;
import com.mongodb.internal.async.AsyncBatchCursor;
import com.mongodb.internal.async.SingleResultCallback;
-import com.mongodb.internal.async.function.AsyncCallbackBiFunction;
import com.mongodb.internal.async.function.AsyncCallbackFunction;
import com.mongodb.internal.async.function.AsyncCallbackSupplier;
+import com.mongodb.internal.async.function.AsyncCallbackTriFunction;
import com.mongodb.internal.async.function.RetryState;
import com.mongodb.internal.async.function.RetryingAsyncCallbackSupplier;
import com.mongodb.internal.binding.AsyncConnectionSource;
@@ -62,7 +63,7 @@
final class AsyncOperationHelper {
interface AsyncCallableWithConnection {
- void call(@Nullable AsyncConnection connection, @Nullable Throwable t);
+ void call(@Nullable AsyncConnection connection, OperationContext operationContext, @Nullable Throwable t);
}
interface AsyncCallableConnectionWithCallback {
@@ -94,39 +95,69 @@ interface CommandReadTransformerAsync {
* @return the function result
*/
@Nullable
- R apply(T t, AsyncConnectionSource source, AsyncConnection connection);
+ R apply(T t, AsyncConnectionSource source, AsyncConnection connection, OperationContext operationContext);
}
- static void withAsyncReadConnectionSource(final AsyncReadBinding binding, final AsyncCallableWithSource callable) {
- binding.getReadConnectionSource(errorHandlingCallback(new AsyncCallableWithSourceCallback(callable), OperationHelper.LOGGER));
+ static void withAsyncReadConnectionSource(final AsyncReadBinding binding, final OperationContext operationContext,
+ final AsyncCallableWithSource callable) {
+ binding.getReadConnectionSource(operationContext,
+ errorHandlingCallback(new AsyncCallableWithSourceCallback(callable), OperationHelper.LOGGER));
}
- static void withAsyncConnection(final AsyncWriteBinding binding, final AsyncCallableWithConnection callable) {
- binding.getWriteConnectionSource(errorHandlingCallback(new AsyncCallableWithConnectionCallback(callable), OperationHelper.LOGGER));
+ static void withAsyncConnection(final AsyncWriteBinding binding,
+ final OperationContext originalOperationContext,
+ final AsyncCallableWithConnection callable) {
+ OperationContext serverSelectionOperationContext = originalOperationContext.withTimeoutContextOverride(TimeoutContext::withComputedServerSelectionTimeoutContextNew);
+ binding.getWriteConnectionSource(
+ serverSelectionOperationContext,
+ errorHandlingCallback(
+ new AsyncCallableWithConnectionCallback(callable, serverSelectionOperationContext, originalOperationContext),
+ OperationHelper.LOGGER));
}
/**
- * @see #withAsyncSuppliedResource(AsyncCallbackSupplier, boolean, SingleResultCallback, AsyncCallbackFunction)
+ * @see #withAsyncSuppliedResource(AsyncCallbackFunction, boolean, OperationContext, SingleResultCallback, AsyncCallbackFunction)
*/
- static void withAsyncSourceAndConnection(final AsyncCallbackSupplier sourceSupplier,
- final boolean wrapConnectionSourceException, final SingleResultCallback callback,
- final AsyncCallbackBiFunction asyncFunction)
+ static void withAsyncSourceAndConnection(
+ final AsyncCallbackFunction sourceAsyncFunction,
+ final boolean wrapConnectionSourceException,
+ final OperationContext operationContext,
+ final SingleResultCallback callback,
+ final AsyncCallbackTriFunction asyncFunction)
throws OperationHelper.ResourceSupplierInternalException {
SingleResultCallback errorHandlingCallback = errorHandlingCallback(callback, OperationHelper.LOGGER);
- withAsyncSuppliedResource(sourceSupplier, wrapConnectionSourceException, errorHandlingCallback,
+
+ OperationContext serverSelectionOperationContext =
+ operationContext.withTimeoutContextOverride(TimeoutContext::withComputedServerSelectionTimeoutContextNew);
+ withAsyncSuppliedResource(
+ sourceAsyncFunction,
+ wrapConnectionSourceException,
+ serverSelectionOperationContext,
+ errorHandlingCallback,
(source, sourceReleasingCallback) ->
- withAsyncSuppliedResource(source::getConnection, wrapConnectionSourceException, sourceReleasingCallback,
+ withAsyncSuppliedResource(
+ source::getConnection,
+ wrapConnectionSourceException,
+ serverSelectionOperationContext.withMinRoundTripTime(source.getServerDescription()),
+ sourceReleasingCallback,
(connection, connectionAndSourceReleasingCallback) ->
- asyncFunction.apply(source, connection, connectionAndSourceReleasingCallback)));
+ asyncFunction.apply(
+ source,
+ connection,
+ operationContext.withMinRoundTripTime(source.getServerDescription()),
+ connectionAndSourceReleasingCallback)));
}
- static void withAsyncSuppliedResource(final AsyncCallbackSupplier resourceSupplier,
- final boolean wrapSourceConnectionException, final SingleResultCallback callback,
- final AsyncCallbackFunction function) throws OperationHelper.ResourceSupplierInternalException {
+ static void withAsyncSuppliedResource(final AsyncCallbackFunction resourceSupplier,
+ final boolean wrapSourceConnectionException,
+ final OperationContext operationContext,
+ final SingleResultCallback callback,
+ final AsyncCallbackFunction function)
+ throws OperationHelper.ResourceSupplierInternalException {
SingleResultCallback errorHandlingCallback = errorHandlingCallback(callback, OperationHelper.LOGGER);
- resourceSupplier.get((resource, supplierException) -> {
+ resourceSupplier.apply(operationContext, (resource, supplierException) -> {
if (supplierException != null) {
if (wrapSourceConnectionException) {
supplierException = new OperationHelper.ResourceSupplierInternalException(supplierException);
@@ -144,57 +175,47 @@ static void withAsyncSuppliedResource(final Asyn
});
}
- static void withAsyncConnectionSourceCallableConnection(final AsyncConnectionSource source,
- final AsyncCallableWithConnection callable) {
- source.getConnection((connection, t) -> {
- source.release();
- if (t != null) {
- callable.call(null, t);
- } else {
- callable.call(connection, null);
- }
- });
- }
-
- static void withAsyncConnectionSource(final AsyncConnectionSource source, final AsyncCallableWithSource callable) {
+ static void withAsyncConnectionSource(final AsyncConnectionSource source,
+ final AsyncCallableWithSource callable) {
callable.call(source, null);
}
static void executeRetryableReadAsync(
final AsyncReadBinding binding,
+ final OperationContext operationContext,
final String database,
final CommandCreator commandCreator,
final Decoder decoder,
final CommandReadTransformerAsync transformer,
final boolean retryReads,
final SingleResultCallback callback) {
- executeRetryableReadAsync(binding, binding::getReadConnectionSource, database, commandCreator,
+ executeRetryableReadAsync(binding, operationContext, binding::getReadConnectionSource, database, commandCreator,
decoder, transformer, retryReads, callback);
}
static void executeRetryableReadAsync(
final AsyncReadBinding binding,
- final AsyncCallbackSupplier sourceAsyncSupplier,
+ final OperationContext operationContext,
+ final AsyncCallbackFunction sourceAsyncFunction,
final String database,
final CommandCreator commandCreator,
final Decoder decoder,
final CommandReadTransformerAsync transformer,
final boolean retryReads,
final SingleResultCallback callback) {
- RetryState retryState = initialRetryState(retryReads, binding.getOperationContext().getTimeoutContext());
+ RetryState retryState = initialRetryState(retryReads, operationContext.getTimeoutContext());
binding.retain();
- OperationContext operationContext = binding.getOperationContext();
- AsyncCallbackSupplier asyncRead = decorateReadWithRetriesAsync(retryState, binding.getOperationContext(),
+ AsyncCallbackSupplier asyncRead = decorateReadWithRetriesAsync(retryState, operationContext,
(AsyncCallbackSupplier) funcCallback ->
- withAsyncSourceAndConnection(sourceAsyncSupplier, false, funcCallback,
- (source, connection, releasingCallback) -> {
+ withAsyncSourceAndConnection(sourceAsyncFunction, false, operationContext, funcCallback,
+ (source, connection, operationContextWithMinRtt, releasingCallback) -> {
if (retryState.breakAndCompleteIfRetryAnd(
() -> !OperationHelper.canRetryRead(source.getServerDescription(),
- operationContext),
+ operationContextWithMinRtt),
releasingCallback)) {
return;
}
- createReadCommandAndExecuteAsync(retryState, operationContext, source, database,
+ createReadCommandAndExecuteAsync(retryState, operationContextWithMinRtt, source, database,
commandCreator, decoder, transformer, connection, releasingCallback);
})
).whenComplete(binding::release);
@@ -203,20 +224,31 @@ static void executeRetryableReadAsync(
static void executeCommandAsync(
final AsyncWriteBinding binding,
+ final OperationContext operationContext,
final String database,
final CommandCreator commandCreator,
final CommandWriteTransformerAsync transformer,
final SingleResultCallback callback) {
Assertions.notNull("binding", binding);
- withAsyncSourceAndConnection(binding::getWriteConnectionSource, false, callback,
- (source, connection, releasingCallback) ->
- executeCommandAsync(binding, database, commandCreator.create(
- binding.getOperationContext(), source.getServerDescription(), connection.getDescription()),
- connection, transformer, releasingCallback)
- );
+ withAsyncSourceAndConnection(
+ binding::getWriteConnectionSource,
+ false,
+ operationContext,
+ callback,
+ (source, connection, operationContextWithMinRtt, releasingCallback) ->
+ executeCommandAsync(
+ binding,
+ operationContextWithMinRtt,
+ database,
+ commandCreator.create(
+ operationContextWithMinRtt, source.getServerDescription(), connection.getDescription()),
+ connection,
+ transformer,
+ releasingCallback));
}
static void executeCommandAsync(final AsyncWriteBinding binding,
+ final OperationContext operationContext,
final String database,
final BsonDocument command,
final AsyncConnection connection,
@@ -226,11 +258,12 @@ static void executeCommandAsync(final AsyncWriteBinding binding,
SingleResultCallback addingRetryableLabelCallback = addingRetryableLabelCallback(callback,
connection.getDescription().getMaxWireVersion());
connection.commandAsync(database, command, NoOpFieldNameValidator.INSTANCE, ReadPreference.primary(), new BsonDocumentCodec(),
- binding.getOperationContext(), transformingWriteCallback(transformer, connection, addingRetryableLabelCallback));
+ operationContext, transformingWriteCallback(transformer, connection, addingRetryableLabelCallback));
}
static void executeRetryableWriteAsync(
final AsyncWriteBinding binding,
+ final OperationContext operationContext,
final String database,
@Nullable final ReadPreference readPreference,
final FieldNameValidator fieldNameValidator,
@@ -240,9 +273,8 @@ static void executeRetryableWriteAsync(
final Function retryCommandModifier,
final SingleResultCallback callback) {
- RetryState retryState = initialRetryState(true, binding.getOperationContext().getTimeoutContext());
+ RetryState retryState = initialRetryState(true, operationContext.getTimeoutContext());
binding.retain();
- OperationContext operationContext = binding.getOperationContext();
AsyncCallbackSupplier asyncWrite = decorateWriteWithRetriesAsync(retryState, operationContext,
(AsyncCallbackSupplier) funcCallback -> {
@@ -250,14 +282,14 @@ static void executeRetryableWriteAsync(
if (!firstAttempt && operationContext.getSessionContext().hasActiveTransaction()) {
operationContext.getSessionContext().clearTransactionContext();
}
- withAsyncSourceAndConnection(binding::getWriteConnectionSource, true, funcCallback,
- (source, connection, releasingCallback) -> {
+ withAsyncSourceAndConnection(binding::getWriteConnectionSource, true, operationContext, funcCallback,
+ (source, connection, operationContextWithMinRtt, releasingCallback) -> {
int maxWireVersion = connection.getDescription().getMaxWireVersion();
SingleResultCallback addingRetryableLabelCallback = firstAttempt
? releasingCallback
: addingRetryableLabelCallback(releasingCallback, maxWireVersion);
if (retryState.breakAndCompleteIfRetryAnd(() ->
- !OperationHelper.canRetryWrite(connection.getDescription(), operationContext.getSessionContext()),
+ !OperationHelper.canRetryWrite(connection.getDescription(), operationContextWithMinRtt.getSessionContext()),
addingRetryableLabelCallback)) {
return;
}
@@ -268,7 +300,7 @@ static void executeRetryableWriteAsync(
Assertions.assertFalse(firstAttempt);
return retryCommandModifier.apply(previousAttemptCommand);
}).orElseGet(() -> commandCreator.create(
- operationContext,
+ operationContextWithMinRtt,
source.getServerDescription(),
connection.getDescription()));
// attach `maxWireVersion`, `retryableCommandFlag` ASAP because they are used to check whether we should retry
@@ -281,7 +313,8 @@ static void executeRetryableWriteAsync(
return;
}
connection.commandAsync(database, command, fieldNameValidator, readPreference, commandResultDecoder,
- operationContext, transformingWriteCallback(transformer, connection, addingRetryableLabelCallback));
+ operationContextWithMinRtt,
+ transformingWriteCallback(transformer, connection, addingRetryableLabelCallback));
});
}).whenComplete(binding::release);
@@ -307,7 +340,7 @@ static void createReadCommandAndExecuteAsync(
return;
}
connection.commandAsync(database, command, NoOpFieldNameValidator.INSTANCE, source.getReadPreference(), decoder,
- operationContext, transformingReadCallback(transformer, source, connection, callback));
+ operationContext, transformingReadCallback(transformer, source, connection, operationContext, callback));
}
static AsyncCallbackSupplier decorateReadWithRetriesAsync(final RetryState retryState, final OperationContext operationContext,
@@ -339,14 +372,21 @@ static CommandWriteTransformerAsync writeConcernErrorTransfo
}
static CommandReadTransformerAsync> asyncSingleBatchCursorTransformer(final String fieldName) {
- return (result, source, connection) ->
+ return (result, source, connection, operationContext) ->
new AsyncSingleBatchCursor<>(BsonDocumentWrapperHelper.toList(result, fieldName), 0);
}
- static AsyncBatchCursor cursorDocumentToAsyncBatchCursor(final TimeoutMode timeoutMode, final BsonDocument cursorDocument,
- final int batchSize, final Decoder decoder, @Nullable final BsonValue comment, final AsyncConnectionSource source,
- final AsyncConnection connection) {
- return new AsyncCommandBatchCursor<>(timeoutMode, cursorDocument, batchSize, 0, decoder, comment, source, connection);
+ static AsyncBatchCursor cursorDocumentToAsyncBatchCursor(final TimeoutMode timeoutMode,
+ final BsonDocument cursorDocument,
+ final int batchSize,
+ final Decoder decoder,
+ @Nullable final BsonValue comment,
+ final AsyncConnectionSource source,
+ final AsyncConnection connection,
+ final OperationContext operationContext) {
+ return new AsyncCommandBatchCursor<>(timeoutMode, 0, operationContext, new AsyncCommandCoreCursor<>(
+ cursorDocument, batchSize, decoder, comment, source, connection
+ ));
}
static SingleResultCallback releasingCallback(final SingleResultCallback wrapped, final AsyncConnection connection) {
@@ -388,19 +428,37 @@ private static SingleResultCallback transformingWriteCallback(final Co
private static class AsyncCallableWithConnectionCallback implements SingleResultCallback {
private final AsyncCallableWithConnection callable;
+ private final OperationContext serverSelectionOperationContext;
+ private final OperationContext originalOperationContext;
- AsyncCallableWithConnectionCallback(final AsyncCallableWithConnection callable) {
+ AsyncCallableWithConnectionCallback(final AsyncCallableWithConnection callable,
+ final OperationContext serverSelectionOperationContext,
+ final OperationContext originalOperationContext) {
this.callable = callable;
+ this.serverSelectionOperationContext = serverSelectionOperationContext;
+ this.originalOperationContext = originalOperationContext;
}
@Override
public void onResult(@Nullable final AsyncConnectionSource source, @Nullable final Throwable t) {
if (t != null) {
- callable.call(null, t);
+ callable.call(null, originalOperationContext, t);
} else {
- withAsyncConnectionSourceCallableConnection(Assertions.assertNotNull(source), callable);
+ withAsyncConnectionSourceCallableConnection(assertNotNull(source));
}
}
+
+ void withAsyncConnectionSourceCallableConnection(final AsyncConnectionSource source) {
+ source.getConnection(serverSelectionOperationContext, (connection, t) -> {
+ source.release();
+ ServerDescription serverDescription = source.getServerDescription();
+ if (t != null) {
+ callable.call(null, originalOperationContext.withMinRoundTripTime(serverDescription), t);
+ } else {
+ callable.call(connection, originalOperationContext.withMinRoundTripTime(serverDescription), null);
+ }
+ });
+ }
}
private static class AsyncCallableWithSourceCallback implements SingleResultCallback {
@@ -456,14 +514,14 @@ private static SingleResultCallback addingRetryableLabelCallback(final Si
}
private static SingleResultCallback transformingReadCallback(final CommandReadTransformerAsync transformer,
- final AsyncConnectionSource source, final AsyncConnection connection, final SingleResultCallback callback) {
+ final AsyncConnectionSource source, final AsyncConnection connection, final OperationContext operationContext, final SingleResultCallback callback) {
return (result, t) -> {
if (t != null) {
callback.onResult(null, t);
} else {
R transformedResult;
try {
- transformedResult = transformer.apply(assertNotNull(result), source, connection);
+ transformedResult = transformer.apply(assertNotNull(result), source, connection, operationContext);
} catch (Throwable e) {
callback.onResult(null, e);
return;
diff --git a/driver-core/src/main/com/mongodb/internal/operation/BaseFindAndModifyOperation.java b/driver-core/src/main/com/mongodb/internal/operation/BaseFindAndModifyOperation.java
index c5d56fda81c..0f7a7a5196d 100644
--- a/driver-core/src/main/com/mongodb/internal/operation/BaseFindAndModifyOperation.java
+++ b/driver-core/src/main/com/mongodb/internal/operation/BaseFindAndModifyOperation.java
@@ -23,6 +23,7 @@
import com.mongodb.internal.async.SingleResultCallback;
import com.mongodb.internal.binding.AsyncWriteBinding;
import com.mongodb.internal.binding.WriteBinding;
+import com.mongodb.internal.connection.OperationContext;
import com.mongodb.internal.session.SessionContext;
import com.mongodb.lang.Nullable;
import org.bson.BsonDocument;
@@ -76,8 +77,8 @@ public String getCommandName() {
@Override
- public T execute(final WriteBinding binding) {
- return executeRetryableWrite(binding, getDatabaseName(), null, getFieldNameValidator(),
+ public T execute(final WriteBinding binding, final OperationContext operationContext) {
+ return executeRetryableWrite(binding, operationContext, getDatabaseName(), null, getFieldNameValidator(),
CommandResultDocumentCodec.create(getDecoder(), "value"),
getCommandCreator(),
FindAndModifyHelper.transformer(),
@@ -85,8 +86,8 @@ public T execute(final WriteBinding binding) {
}
@Override
- public void executeAsync(final AsyncWriteBinding binding, final SingleResultCallback callback) {
- executeRetryableWriteAsync(binding, getDatabaseName(), null, getFieldNameValidator(),
+ public void executeAsync(final AsyncWriteBinding binding, final OperationContext operationContext, final SingleResultCallback callback) {
+ executeRetryableWriteAsync(binding, operationContext, getDatabaseName(), null, getFieldNameValidator(),
CommandResultDocumentCodec.create(getDecoder(), "value"),
getCommandCreator(),
FindAndModifyHelper.asyncTransformer(), cmd -> cmd, callback);
diff --git a/driver-core/src/main/com/mongodb/internal/operation/ChangeStreamBatchCursor.java b/driver-core/src/main/com/mongodb/internal/operation/ChangeStreamBatchCursor.java
index c4bd72a4775..103751e7df3 100644
--- a/driver-core/src/main/com/mongodb/internal/operation/ChangeStreamBatchCursor.java
+++ b/driver-core/src/main/com/mongodb/internal/operation/ChangeStreamBatchCursor.java
@@ -1,3 +1,5 @@
+package com.mongodb.internal.operation;
+
/*
* Copyright 2008-present MongoDB, Inc.
*
@@ -14,7 +16,6 @@
* limitations under the License.
*/
-package com.mongodb.internal.operation;
import com.mongodb.MongoChangeStreamException;
import com.mongodb.MongoException;
@@ -23,6 +24,7 @@
import com.mongodb.ServerCursor;
import com.mongodb.internal.TimeoutContext;
import com.mongodb.internal.binding.ReadBinding;
+import com.mongodb.internal.connection.OperationContext;
import com.mongodb.lang.Nullable;
import org.bson.BsonDocument;
import org.bson.BsonTimestamp;
@@ -55,11 +57,11 @@
*
*/
final class ChangeStreamBatchCursor implements AggregateResponseBatchCursor {
- private final ReadBinding binding;
+ private ReadBinding binding;
+ private OperationContext operationContext;
private final ChangeStreamOperation changeStreamOperation;
private final int maxWireVersion;
- private final TimeoutContext timeoutContext;
- private CommandBatchCursor wrapped;
+ private CoreCursor wrapped;
private BsonDocument resumeToken;
private final AtomicBoolean closed;
@@ -71,13 +73,14 @@ final class ChangeStreamBatchCursor implements AggregateResponseBatchCursor changeStreamOperation,
- final CommandBatchCursor wrapped,
+ final CoreCursor wrapped,
final ReadBinding binding,
+ final OperationContext operationContext,
@Nullable final BsonDocument resumeToken,
final int maxWireVersion) {
- this.timeoutContext = binding.getOperationContext().getTimeoutContext();
this.changeStreamOperation = changeStreamOperation;
this.binding = binding.retain();
+ this.operationContext = operationContext.withTimeoutContextOverride(TimeoutContext::withMaxTimeAsMaxAwaitTimeOverride);
this.wrapped = wrapped;
this.resumeToken = resumeToken;
this.maxWireVersion = maxWireVersion;
@@ -85,7 +88,7 @@ final class ChangeStreamBatchCursor implements AggregateResponseBatchCursor getWrapped() {
+ CoreCursor getWrapped() {
return wrapped;
}
@@ -93,7 +96,7 @@ CommandBatchCursor getWrapped() {
public boolean hasNext() {
return resumeableOperation(commandBatchCursor -> {
try {
- return commandBatchCursor.hasNext();
+ return commandBatchCursor.hasNext(operationContext);
} finally {
cachePostBatchResumeToken(commandBatchCursor);
}
@@ -104,7 +107,7 @@ public boolean hasNext() {
public List next() {
return resumeableOperation(commandBatchCursor -> {
try {
- return convertAndProduceLastId(commandBatchCursor.next(), changeStreamOperation.getDecoder(),
+ return convertAndProduceLastId(commandBatchCursor.next(operationContext), changeStreamOperation.getDecoder(),
lastId -> resumeToken = lastId);
} finally {
cachePostBatchResumeToken(commandBatchCursor);
@@ -112,6 +115,10 @@ public List next() {
});
}
+ private void restartTimeout() {
+ operationContext = operationContext.withNewlyStartedTimeout();
+ }
+
@Override
public int available() {
return wrapped.available();
@@ -121,7 +128,7 @@ public int available() {
public List tryNext() {
return resumeableOperation(commandBatchCursor -> {
try {
- List tryNext = commandBatchCursor.tryNext();
+ List tryNext = commandBatchCursor.tryNext(operationContext);
return tryNext == null ? null
: convertAndProduceLastId(tryNext, changeStreamOperation.getDecoder(), lastId -> resumeToken = lastId);
} finally {
@@ -133,8 +140,7 @@ public List tryNext() {
@Override
public void close() {
if (!closed.getAndSet(true)) {
- timeoutContext.resetTimeoutIfPresent();
- wrapped.close();
+ wrapped.close(operationContext);
binding.release();
}
}
@@ -184,7 +190,7 @@ public int getMaxWireVersion() {
return maxWireVersion;
}
- private void cachePostBatchResumeToken(final AggregateResponseBatchCursor commandBatchCursor) {
+ private void cachePostBatchResumeToken(final CoreCursor commandBatchCursor) {
if (commandBatchCursor.getPostBatchResumeToken() != null) {
resumeToken = commandBatchCursor.getPostBatchResumeToken();
}
@@ -210,8 +216,8 @@ static List convertAndProduceLastId(final List rawDocume
return results;
}
-