1616
1717package com .mongodb .client ;
1818
19+ import com .mongodb .ClusterFixture ;
1920import com .mongodb .MongoClientException ;
2021import com .mongodb .MongoClientSettings ;
2122import com .mongodb .MongoCommandException ;
2526import com .mongodb .client .model .Updates ;
2627import com .mongodb .event .CommandListener ;
2728import com .mongodb .event .CommandStartedEvent ;
29+ import com .mongodb .event .ServerHeartbeatStartedEvent ;
30+ import com .mongodb .event .ServerHeartbeatSucceededEvent ;
31+ import com .mongodb .event .TestServerMonitorListener ;
32+ import com .mongodb .internal .connection .TestCommandListener ;
2833import org .bson .BsonDocument ;
2934import org .bson .Document ;
3035import org .junit .jupiter .api .AfterAll ;
3338
3439import java .io .File ;
3540import java .io .IOException ;
41+ import java .time .Duration ;
3642import java .util .List ;
3743import java .util .Set ;
3844import java .util .concurrent .ConcurrentHashMap ;
3945import java .util .concurrent .ExecutorService ;
4046import java .util .concurrent .Executors ;
4147import java .util .concurrent .TimeUnit ;
48+ import java .util .concurrent .TimeoutException ;
4249import java .util .concurrent .atomic .AtomicBoolean ;
4350
4451import static com .mongodb .ClusterFixture .getDefaultDatabaseName ;
52+ import static com .mongodb .ClusterFixture .isStandalone ;
4553import static com .mongodb .client .Fixture .getMongoClientSettingsBuilder ;
4654import static java .util .Arrays .asList ;
4755import static java .util .Collections .singletonList ;
56+ import static java .util .concurrent .TimeUnit .MILLISECONDS ;
4857import static org .bson .assertions .Assertions .fail ;
4958import static org .junit .jupiter .api .Assertions .assertEquals ;
5059import static org .junit .jupiter .api .Assertions .assertFalse ;
5160import static org .junit .jupiter .api .Assertions .assertTrue ;
61+ import static org .junit .jupiter .api .Assumptions .assumeTrue ;
5262
5363// Prose tests for Sessions specification: https://github.com/mongodb/specifications/tree/master/source/sessions
5464// Prose test README: https://github.com/mongodb/specifications/tree/master/source/sessions/tests/README.md
@@ -194,6 +204,61 @@ public void shouldThrowOnExplicitSessionIfConnectionDoesNotSupportSessions() thr
194204 }
195205 }
196206
207+ /* Test 20 from #20-drivers-do-not-gossip-clustertime-on-sdam-commands
208+ In this test, we check that the cluster time has not been advanced on client1 through the server monitors, after client2 advanced
209+ the cluster time on the deployment/cluster.
210+ */
211+ @ Test
212+ public void shouldNotGossipClusterTimeInServerMonitors () throws InterruptedException , TimeoutException {
213+ assumeTrue (!isStandalone ());
214+
215+ //given
216+ TestServerMonitorListener serverMonitorListener =
217+ new TestServerMonitorListener (asList ("serverHeartbeatStartedEvent" , "serverHeartbeatSucceededEvent" ,
218+ "serverHeartbeatFailedEvent" ));
219+ TestCommandListener commandListener = new TestCommandListener ();
220+ try (MongoClient client1 = getMongoClient (
221+ getDirectPrimaryMongoClientSettingsBuilder ()
222+ .addCommandListener (commandListener )
223+ .applyToServerSettings (builder -> builder
224+ .heartbeatFrequency (10 , MILLISECONDS )
225+ .addServerMonitorListener (serverMonitorListener ))
226+ .build ());
227+ MongoClient client2 = getMongoClient (getDirectPrimaryMongoClientSettingsBuilder ()
228+ .build ())) {
229+
230+ Document clusterTime = executePing (client1 )
231+ .get ("$clusterTime" , Document .class );
232+
233+ //when
234+ client2 .getDatabase ("test" )
235+ .getCollection ("test" )
236+ .insertOne (new Document ("advance" , "$clusterTime" ));
237+
238+ // wait until the client1 processes the next pair of SDAM heartbeat started + succeeded events.
239+ serverMonitorListener .reset ();
240+ serverMonitorListener .waitForEvents (ServerHeartbeatStartedEvent .class , serverHeartbeatStartedEvent -> true ,
241+ 1 , Duration .ofMillis (20 + ClusterFixture .getPrimaryRTT ()));
242+ serverMonitorListener .waitForEvents (ServerHeartbeatSucceededEvent .class , serverHeartbeatSucceededEvent -> true ,
243+ 1 , Duration .ofMillis (20 + ClusterFixture .getPrimaryRTT ()));
244+
245+ commandListener .reset ();
246+ executePing (client1 );
247+
248+ //then
249+ List <CommandStartedEvent > pingStartedEvents = commandListener .getCommandStartedEvents ("ping" );
250+ assertEquals (1 , pingStartedEvents .size ());
251+ BsonDocument sentClusterTime = pingStartedEvents .get (0 ).getCommand ().getDocument ("$clusterTime" );
252+
253+ assertEquals (clusterTime .toBsonDocument (), sentClusterTime , "Cluster time should not have advanced after the first ping" );
254+ }
255+ }
256+
257+ private static MongoClientSettings .Builder getDirectPrimaryMongoClientSettingsBuilder () {
258+ return getMongoClientSettingsBuilder ()
259+ .applyToClusterSettings (ClusterFixture ::setDirectConnection );
260+ }
261+
197262 private static MongoClientSettings .Builder getMongocryptdMongoClientSettingsBuilder () {
198263 return MongoClientSettings .builder ()
199264 .applyToClusterSettings (builder ->
@@ -209,5 +274,10 @@ private static Process startMongocryptdProcess() throws IOException {
209274 processBuilder .redirectOutput (new File ("/tmp/mongocryptd.log" ));
210275 return processBuilder .start ();
211276 }
277+
278+ private static Document executePing (final MongoClient client1 ) {
279+ return client1 .getDatabase ("admin" )
280+ .runCommand (new Document ("ping" , 1 ));
281+ }
212282}
213283
0 commit comments