17
17
package com .mongodb .internal .operation ;
18
18
19
19
import com .mongodb .MongoCommandException ;
20
+ import com .mongodb .MongoException ;
20
21
import com .mongodb .MongoNamespace ;
21
22
import com .mongodb .MongoOperationTimeoutException ;
22
23
import com .mongodb .MongoSocketException ;
51
52
import static com .mongodb .assertions .Assertions .assertNotNull ;
52
53
import static com .mongodb .assertions .Assertions .assertTrue ;
53
54
import static com .mongodb .assertions .Assertions .doesNotThrow ;
55
+ import static com .mongodb .internal .async .AsyncRunnable .beginAsync ;
54
56
import static com .mongodb .internal .operation .CommandBatchCursorHelper .FIRST_BATCH ;
55
57
import static com .mongodb .internal .operation .CommandBatchCursorHelper .MESSAGE_IF_CLOSED_AS_CURSOR ;
56
58
import static com .mongodb .internal .operation .CommandBatchCursorHelper .NEXT_BATCH ;
63
65
class AsyncCommandBatchCursor <T > implements AsyncAggregateResponseBatchCursor <T > {
64
66
65
67
private final MongoNamespace namespace ;
66
- private final long maxTimeMS ;
67
68
private final Decoder <T > decoder ;
68
69
@ Nullable
69
70
private final BsonValue comment ;
70
71
private final int maxWireVersion ;
71
72
private final boolean firstBatchEmpty ;
72
73
private final ResourceManager resourceManager ;
74
+ private final OperationContext operationContext ;
75
+ private final TimeoutMode timeoutMode ;
73
76
private final AtomicBoolean processedInitial = new AtomicBoolean ();
74
77
private int batchSize ;
75
78
private volatile CommandCursorResult <T > commandCursorResult ;
79
+ private boolean resetTimeoutWhenClosing ;
76
80
77
81
AsyncCommandBatchCursor (
78
82
final TimeoutMode timeoutMode ,
@@ -86,24 +90,25 @@ class AsyncCommandBatchCursor<T> implements AsyncAggregateResponseBatchCursor<T>
86
90
this .commandCursorResult = toCommandCursorResult (connectionDescription .getServerAddress (), FIRST_BATCH , commandCursorDocument );
87
91
this .namespace = commandCursorResult .getNamespace ();
88
92
this .batchSize = batchSize ;
89
- this .maxTimeMS = maxTimeMS ;
90
93
this .decoder = decoder ;
91
94
this .comment = comment ;
92
95
this .maxWireVersion = connectionDescription .getMaxWireVersion ();
93
96
this .firstBatchEmpty = commandCursorResult .getResults ().isEmpty ();
97
+ operationContext = connectionSource .getOperationContext ();
98
+ this .timeoutMode = timeoutMode ;
94
99
95
- connectionSource . getOperationContext () .getTimeoutContext ().setMaxTimeOverride (maxTimeMS );
100
+ operationContext .getTimeoutContext ().setMaxTimeOverride (maxTimeMS );
96
101
97
102
AsyncConnection connectionToPin = connectionSource .getServerDescription ().getType () == ServerType .LOAD_BALANCER
98
103
? connection : null ;
99
- resourceManager = new ResourceManager (timeoutMode , namespace , connectionSource , connectionToPin ,
100
- commandCursorResult . getServerCursor ()) ;
104
+ resourceManager = new ResourceManager (namespace , connectionSource , connectionToPin , commandCursorResult . getServerCursor ());
105
+ resetTimeoutWhenClosing = true ;
101
106
}
102
107
103
108
@ Override
104
109
public void next (final SingleResultCallback <List <T >> callback ) {
105
110
resourceManager .execute (funcCallback -> {
106
- resourceManager . checkTimeoutModeAndResetTimeoutContextIfIteration ();
111
+ checkTimeoutModeAndResetTimeoutContextIfIteration ();
107
112
ServerCursor localServerCursor = resourceManager .getServerCursor ();
108
113
boolean serverCursorIsNull = localServerCursor == null ;
109
114
List <T > batchResults = emptyList ();
@@ -168,6 +173,12 @@ public int getMaxWireVersion() {
168
173
return maxWireVersion ;
169
174
}
170
175
176
+ void checkTimeoutModeAndResetTimeoutContextIfIteration () {
177
+ if (timeoutMode == TimeoutMode .ITERATION ) {
178
+ operationContext .getTimeoutContext ().resetTimeoutIfPresent ();
179
+ }
180
+ }
181
+
171
182
private void getMore (final ServerCursor cursor , final SingleResultCallback <List <T >> callback ) {
172
183
resourceManager .executeWithConnection ((connection , wrappedCallback ) ->
173
184
getMoreLoop (assertNotNull (connection ), cursor , wrappedCallback ), callback );
@@ -216,21 +227,24 @@ private CommandCursorResult<T> toCommandCursorResult(final ServerAddress serverA
216
227
return commandCursorResult ;
217
228
}
218
229
219
- void setCloseWithoutTimeoutReset (final boolean closeWithoutTimeoutReset ) {
220
- this .resourceManager .setCloseWithoutTimeoutReset (closeWithoutTimeoutReset );
230
+ /**
231
+ * Configures the cursor to {@link #close()}
232
+ * without {@linkplain TimeoutContext#resetTimeoutIfPresent() resetting} its {@linkplain TimeoutContext#getTimeout() timeout}.
233
+ * This is useful when managing the {@link #close()} behavior externally.
234
+ */
235
+ AsyncCommandBatchCursor <T > disableTimeoutResetWhenClosing () {
236
+ resetTimeoutWhenClosing = false ;
237
+ return this ;
221
238
}
222
239
223
240
@ ThreadSafe
224
- private static final class ResourceManager extends CursorResourceManager <AsyncConnectionSource , AsyncConnection > {
225
-
241
+ private final class ResourceManager extends CursorResourceManager <AsyncConnectionSource , AsyncConnection > {
226
242
ResourceManager (
227
- final TimeoutMode timeoutMode ,
228
243
final MongoNamespace namespace ,
229
244
final AsyncConnectionSource connectionSource ,
230
245
@ Nullable final AsyncConnection connectionToPin ,
231
246
@ Nullable final ServerCursor serverCursor ) {
232
- super (connectionSource .getOperationContext ().getTimeoutContext (), timeoutMode , namespace , connectionSource , connectionToPin ,
233
- serverCursor );
247
+ super (namespace , connectionSource , connectionToPin , serverCursor );
234
248
}
235
249
236
250
/**
@@ -244,7 +258,7 @@ <R> void execute(final AsyncCallbackSupplier<R> operation, final SingleResultCal
244
258
} else {
245
259
operation .whenComplete (() -> {
246
260
endOperation ();
247
- if (getServerCursor () == null ) {
261
+ if (super . getServerCursor () == null ) {
248
262
// At this point all resources have been released,
249
263
// but `isClose` may still be returning `false` if `close` have not been called.
250
264
// Self-close to update the state managed by `ResourceManger`, and so that `isClosed` return `true`.
@@ -261,23 +275,41 @@ void markAsPinned(final AsyncConnection connectionToPin, final Connection.Pinnin
261
275
262
276
@ Override
263
277
void doClose () {
264
- if (isSkipReleasingServerResourcesOnClose ()) {
265
- unsetServerCursor ();
278
+ TimeoutContext timeoutContext = operationContext .getTimeoutContext ();
279
+ timeoutContext .resetToDefaultMaxTime ();
280
+ SingleResultCallback <Void > thenDoNothing = (r , t ) -> {};
281
+ if (resetTimeoutWhenClosing ) {
282
+ timeoutContext .doWithResetTimeout (this ::releaseResourcesAsync , thenDoNothing );
283
+ } else {
284
+ releaseResourcesAsync (thenDoNothing );
266
285
}
286
+ }
267
287
268
- resetTimeout ();
269
- if (getServerCursor () != null ) {
270
- getConnection ((connection , t ) -> {
271
- if (connection != null ) {
272
- releaseServerAndClientResources (connection );
273
- } else {
274
- unsetServerCursor ();
275
- releaseClientResources ();
276
- }
277
- });
278
- } else {
288
+ private void releaseResourcesAsync (final SingleResultCallback <Void > callback ) {
289
+ beginAsync ().thenRunTryCatchAsyncBlocks (c -> {
290
+ if (isSkipReleasingServerResourcesOnClose ()) {
291
+ unsetServerCursor ();
292
+ }
293
+ if (super .getServerCursor () != null ) {
294
+ beginAsync ().<AsyncConnection >thenSupply (c2 -> {
295
+ getConnection (c2 );
296
+ }).thenConsume ((connection , c3 ) -> {
297
+ beginAsync ().thenRun (c4 -> {
298
+ releaseServerResourcesAsync (connection , c4 );
299
+ }).thenAlwaysRunAndFinish (() -> {
300
+ connection .release ();
301
+ }, c3 );
302
+ }).finish (c );
303
+ } else {
304
+ c .complete (c );
305
+ }
306
+ }, MongoException .class , (e , c5 ) -> {
307
+ c5 .complete (c5 ); // ignore exceptions when releasing server resources
308
+ }).thenAlwaysRunAndFinish (() -> {
309
+ // guarantee that regardless of exceptions, `serverCursor` is null and client resources are released
310
+ unsetServerCursor ();
279
311
releaseClientResources ();
280
- }
312
+ }, callback );
281
313
}
282
314
283
315
<R > void executeWithConnection (final AsyncCallableConnectionWithCallback <R > callable , final SingleResultCallback <R > callback ) {
@@ -314,25 +346,21 @@ private void getConnection(final SingleResultCallback<AsyncConnection> callback)
314
346
}
315
347
}
316
348
317
- private void releaseServerAndClientResources (final AsyncConnection connection ) {
318
- AsyncCallbackSupplier < Void > callbackSupplier = funcCallback -> {
319
- ServerCursor localServerCursor = getServerCursor ();
349
+ private void releaseServerResourcesAsync (final AsyncConnection connection , final SingleResultCallback < Void > callback ) {
350
+ beginAsync (). thenRun (( c ) -> {
351
+ ServerCursor localServerCursor = super . getServerCursor ();
320
352
if (localServerCursor != null ) {
321
- killServerCursor (getNamespace (), localServerCursor , connection , funcCallback );
353
+ killServerCursorAsync (getNamespace (), localServerCursor , connection , callback );
354
+ } else {
355
+ c .complete (c );
322
356
}
323
- };
324
- callbackSupplier .whenComplete (() -> {
357
+ }).thenAlwaysRunAndFinish (() -> {
325
358
unsetServerCursor ();
326
- releaseClientResources ();
327
- }).whenComplete (connection ::release ).get ((r , t ) -> { /* do nothing */ });
359
+ }, callback );
328
360
}
329
361
330
- private void killServerCursor (final MongoNamespace namespace , final ServerCursor localServerCursor ,
362
+ private void killServerCursorAsync (final MongoNamespace namespace , final ServerCursor localServerCursor ,
331
363
final AsyncConnection localConnection , final SingleResultCallback <Void > callback ) {
332
- OperationContext operationContext = assertNotNull (getConnectionSource ()).getOperationContext ();
333
- TimeoutContext timeoutContext = operationContext .getTimeoutContext ();
334
- timeoutContext .resetToDefaultMaxTime ();
335
-
336
364
localConnection .commandAsync (namespace .getDatabaseName (), getKillCursorsCommand (namespace , localServerCursor ),
337
365
NoOpFieldNameValidator .INSTANCE , ReadPreference .primary (), new BsonDocumentCodec (),
338
366
operationContext , (r , t ) -> callback .onResult (null , null ));
0 commit comments