23
23
import com .mongodb .MongoSecurityException ;
24
24
import com .mongodb .MongoSocketException ;
25
25
import com .mongodb .MongoSocketReadTimeoutException ;
26
- import com .mongodb .internal .async .SingleResultCallback ;
27
26
import com .mongodb .connection .ClusterConnectionMode ;
28
27
import com .mongodb .connection .ServerDescription ;
29
28
import com .mongodb .connection .ServerId ;
30
29
import com .mongodb .connection .ServerType ;
30
+ import com .mongodb .connection .TopologyVersion ;
31
31
import com .mongodb .diagnostics .logging .Logger ;
32
32
import com .mongodb .diagnostics .logging .Loggers ;
33
33
import com .mongodb .event .CommandListener ;
34
34
import com .mongodb .event .ServerClosedEvent ;
35
35
import com .mongodb .event .ServerDescriptionChangedEvent ;
36
36
import com .mongodb .event .ServerListener ;
37
37
import com .mongodb .event .ServerOpeningEvent ;
38
+ import com .mongodb .internal .async .SingleResultCallback ;
38
39
import com .mongodb .internal .session .SessionContext ;
39
40
40
41
import java .util .List ;
@@ -92,7 +93,7 @@ public Connection getConnection() {
92
93
connectionPool .invalidate ();
93
94
throw e ;
94
95
} catch (MongoSocketException e ) {
95
- invalidate ();
96
+ invalidate (connectionPool . getGeneration () );
96
97
throw e ;
97
98
}
98
99
}
@@ -106,7 +107,7 @@ public void onResult(final InternalConnection result, final Throwable t) {
106
107
if (t instanceof MongoSecurityException ) {
107
108
connectionPool .invalidate ();
108
109
} else if (t instanceof MongoSocketException ) {
109
- invalidate ();
110
+ invalidate (connectionPool . getGeneration () );
110
111
}
111
112
if (t != null ) {
112
113
callback .onResult (null , t );
@@ -126,23 +127,43 @@ public ServerDescription getDescription() {
126
127
127
128
@ Override
128
129
public void invalidate () {
130
+ invalidate (connectionPool .getGeneration ());
131
+ }
132
+
133
+ @ Override
134
+ public void invalidate (final int connectionGeneration ) {
129
135
if (!isClosed ()) {
136
+ if (connectionGeneration < connectionPool .getGeneration ()) {
137
+ return ;
138
+ }
130
139
serverStateListener .stateChanged (new ChangeEvent <ServerDescription >(description , ServerDescription .builder ()
131
140
.state (CONNECTING )
132
141
.address (serverId .getAddress ())
133
142
.build ()));
134
143
connectionPool .invalidate ();
144
+ // TODO: in streaming protocol, we want to close the current connection and start over
135
145
connect ();
136
146
}
137
147
}
138
148
139
149
@ Override
140
- public void invalidate (final Throwable t ) {
150
+ public void invalidate (final Throwable reason ) {
151
+ invalidate (reason , connectionPool .getGeneration (), description .getMaxWireVersion ());
152
+ }
153
+
154
+ @ Override
155
+ public void invalidate (final Throwable t , final int connectionGeneration , final int maxWireVersion ) {
141
156
if (!isClosed ()) {
157
+ if (connectionGeneration < connectionPool .getGeneration ()) {
158
+ return ;
159
+ }
142
160
if ((t instanceof MongoSocketException && !(t instanceof MongoSocketReadTimeoutException ))) {
143
161
invalidate ();
144
162
} else if (t instanceof MongoNotPrimaryException || t instanceof MongoNodeIsRecoveringException ) {
145
- if (description .getMaxWireVersion () < FOUR_DOT_TWO_WIRE_VERSION ) {
163
+ if (isStale (((MongoCommandException ) t ))) {
164
+ return ;
165
+ }
166
+ if (maxWireVersion < FOUR_DOT_TWO_WIRE_VERSION ) {
146
167
invalidate ();
147
168
} else if (SHUTDOWN_CODES .contains (((MongoCommandException ) t ).getErrorCode ())) {
148
169
invalidate ();
@@ -156,9 +177,33 @@ public void invalidate(final Throwable t) {
156
177
}
157
178
}
158
179
159
- public void invalidate (final Throwable t , final SessionContext sessionContext ) {
180
+ private boolean isStale (final MongoCommandException t ) {
181
+ if (!t .getResponse ().containsKey ("topologyVersion" )) {
182
+ return false ;
183
+ }
184
+ return isStale (description .getTopologyVersion (), new TopologyVersion (t .getResponse ().getDocument ("topologyVersion" )));
185
+ }
186
+
187
+ private boolean isStale (final TopologyVersion currentTopologyVersion , final TopologyVersion candidateTopologyVersion ) {
188
+ if (candidateTopologyVersion == null || currentTopologyVersion == null ) {
189
+ return false ;
190
+ }
191
+
192
+ if (!candidateTopologyVersion .getProcessId ().equals (currentTopologyVersion .getProcessId ())) {
193
+ return false ;
194
+ }
195
+
196
+ if (candidateTopologyVersion .getCounter () <= currentTopologyVersion .getCounter ()) {
197
+ return true ;
198
+ }
199
+
200
+ return false ;
201
+ }
202
+
203
+ public void invalidate (final Throwable t , final int connectionGeneration , final int maxWireVersion ,
204
+ final SessionContext sessionContext ) {
160
205
notNull ("sessionContext" , sessionContext );
161
- invalidate (t );
206
+ invalidate (t , connectionGeneration , maxWireVersion );
162
207
if (t instanceof MongoSocketException && sessionContext .hasSession ()) {
163
208
sessionContext .markSessionDirty ();
164
209
}
@@ -195,7 +240,7 @@ public <T> T execute(final LegacyProtocol<T> protocol, final InternalConnection
195
240
protocol .setCommandListener (commandListener );
196
241
return protocol .execute (connection );
197
242
} catch (MongoException e ) {
198
- invalidate (e );
243
+ invalidate (e , connection . getGeneration (), connection . getDescription (). getMaxWireVersion () );
199
244
throw e ;
200
245
}
201
246
}
@@ -208,7 +253,7 @@ public <T> void executeAsync(final LegacyProtocol<T> protocol, final InternalCon
208
253
@ Override
209
254
public void onResult (final T result , final Throwable t ) {
210
255
if (t != null ) {
211
- invalidate (t );
256
+ invalidate (t , connection . getGeneration (), connection . getDescription (). getMaxWireVersion () );
212
257
}
213
258
callback .onResult (result , t );
214
259
}
@@ -226,7 +271,7 @@ public <T> T execute(final CommandProtocol<T> protocol, final InternalConnection
226
271
invalidate ();
227
272
return (T ) e .getResponse ();
228
273
} catch (MongoException e ) {
229
- invalidate (e , sessionContext );
274
+ invalidate (e , connection . getGeneration (), connection . getDescription (). getMaxWireVersion (), sessionContext );
230
275
throw e ;
231
276
}
232
277
}
@@ -244,7 +289,7 @@ public void onResult(final T result, final Throwable t) {
244
289
invalidate ();
245
290
callback .onResult ((T ) ((MongoWriteConcernWithResponseException ) t ).getResponse (), null );
246
291
} else {
247
- invalidate (t , sessionContext );
292
+ invalidate (t , connection . getGeneration (), connection . getDescription (). getMaxWireVersion (), sessionContext );
248
293
callback .onResult (null , t );
249
294
}
250
295
} else {
@@ -259,8 +304,28 @@ private final class DefaultServerStateListener implements ChangeListener<ServerD
259
304
@ Override
260
305
public void stateChanged (final ChangeEvent <ServerDescription > event ) {
261
306
ServerDescription oldDescription = description ;
262
- description = event .getNewValue ();
263
- serverListener .serverDescriptionChanged (new ServerDescriptionChangedEvent (serverId , description , oldDescription ));
307
+ if (shouldReplace (oldDescription , event .getNewValue ())) {
308
+ description = event .getNewValue ();
309
+ serverListener .serverDescriptionChanged (new ServerDescriptionChangedEvent (serverId , description , oldDescription ));
310
+ }
311
+ }
312
+
313
+ private boolean shouldReplace (final ServerDescription oldDescription , final ServerDescription newDescription ) {
314
+ TopologyVersion oldTopologyVersion = oldDescription .getTopologyVersion ();
315
+ TopologyVersion newTopologyVersion = newDescription .getTopologyVersion ();
316
+ if (newTopologyVersion == null || oldTopologyVersion == null ) {
317
+ return true ;
318
+ }
319
+
320
+ if (!newTopologyVersion .getProcessId ().equals (oldTopologyVersion .getProcessId ())) {
321
+ return true ;
322
+ }
323
+
324
+ if (newTopologyVersion .getCounter () >= oldTopologyVersion .getCounter ()) {
325
+ return true ;
326
+ }
327
+
328
+ return false ;
264
329
}
265
330
}
266
331
}
0 commit comments