2020import com .mongodb .MongoClientException ;
2121import com .mongodb .MongoException ;
2222import com .mongodb .TransactionOptions ;
23- import com .mongodb .client .internal .ClientSessionClock ;
2423import com .mongodb .client .model .Sorts ;
2524import com .mongodb .internal .time .ExponentialBackoff ;
25+ import com .mongodb .internal .time .StartTime ;
26+ import com .mongodb .internal .time .SystemNanoTime ;
2627import org .bson .BsonDocument ;
2728import org .bson .Document ;
2829import org .junit .jupiter .api .BeforeEach ;
2930import org .junit .jupiter .api .DisplayName ;
3031import org .junit .jupiter .api .Test ;
32+ import org .mockito .MockedStatic ;
33+ import org .mockito .Mockito ;
3134
35+ import java .time .Duration ;
3236import java .util .concurrent .TimeUnit ;
3337import java .util .concurrent .atomic .AtomicInteger ;
38+ import java .util .function .Consumer ;
3439
3540import static com .mongodb .ClusterFixture .TIMEOUT ;
3641import static com .mongodb .ClusterFixture .isDiscoverableReplicaSet ;
4449
4550// See https://github.com/mongodb/specifications/blob/master/source/transactions-convenient-api/tests/README.md#prose-tests
4651public class WithTransactionProseTest extends DatabaseTestCase {
47- private static final long START_TIME_MS = 1L ;
48- private static final long ERROR_GENERATING_INTERVAL = 121000L ;
52+ private static final Duration ERROR_GENERATING_INTERVAL = Duration .ofSeconds (120 );
4953
5054 @ BeforeEach
5155 @ Override
@@ -66,7 +70,7 @@ public void setUp() {
6670 public void testCallbackRaisesCustomError () {
6771 final String exceptionMessage = "NotTransientOrUnknownError" ;
6872 try (ClientSession session = client .startSession ()) {
69- session .withTransaction ((TransactionBody < Void >) ( ) -> {
73+ session .withTransaction (() -> {
7074 throw new MongoException (exceptionMessage );
7175 });
7276 // should not get here
@@ -101,13 +105,13 @@ public void testRetryTimeoutEnforcedTransientTransactionError() {
101105 final String errorMessage = "transient transaction error" ;
102106
103107 try (ClientSession session = client .startSession ()) {
104- ClientSessionClock . INSTANCE . setTime ( START_TIME_MS );
105- session .withTransaction (( TransactionBody < Void >) () -> {
106- ClientSessionClock . INSTANCE . setTime (ERROR_GENERATING_INTERVAL );
107- MongoException e = new MongoException (112 , errorMessage );
108- e .addLabel (MongoException .TRANSIENT_TRANSACTION_ERROR_LABEL );
109- throw e ;
110- } );
108+ doWithSystemNanoTimeHandle ( systemNanoTimeHandle ->
109+ session .withTransaction (() -> {
110+ systemNanoTimeHandle . setRelativeToStart (ERROR_GENERATING_INTERVAL );
111+ MongoException e = new MongoException (112 , errorMessage );
112+ e .addLabel (MongoException .TRANSIENT_TRANSACTION_ERROR_LABEL );
113+ throw e ;
114+ }) );
111115 fail ("Test should have thrown an exception." );
112116 } catch (Exception e ) {
113117 assertEquals (errorMessage , e .getMessage ());
@@ -127,12 +131,12 @@ public void testRetryTimeoutEnforcedUnknownTransactionCommit() {
127131 + "'data': {'failCommands': ['commitTransaction'], 'errorCode': 91, 'closeConnection': false}}" ));
128132
129133 try (ClientSession session = client .startSession ()) {
130- ClientSessionClock . INSTANCE . setTime ( START_TIME_MS );
131- session .withTransaction (( TransactionBody < Void >) () -> {
132- ClientSessionClock . INSTANCE . setTime (ERROR_GENERATING_INTERVAL );
133- collection .insertOne (session , new Document ("_id" , 2 ));
134- return null ;
135- } );
134+ doWithSystemNanoTimeHandle ( systemNanoTimeHandle ->
135+ session .withTransaction (() -> {
136+ systemNanoTimeHandle . setRelativeToStart (ERROR_GENERATING_INTERVAL );
137+ collection .insertOne (session , new Document ("_id" , 2 ));
138+ return null ;
139+ }) );
136140 fail ("Test should have thrown an exception." );
137141 } catch (Exception e ) {
138142 assertEquals (91 , ((MongoException ) e ).getCode ());
@@ -156,12 +160,12 @@ public void testRetryTimeoutEnforcedTransientTransactionErrorOnCommit() {
156160 + "'errmsg': 'Transaction 0 has been aborted', 'closeConnection': false}}" ));
157161
158162 try (ClientSession session = client .startSession ()) {
159- ClientSessionClock . INSTANCE . setTime ( START_TIME_MS );
160- session .withTransaction (( TransactionBody < Void >) () -> {
161- ClientSessionClock . INSTANCE . setTime (ERROR_GENERATING_INTERVAL );
162- collection .insertOne (session , Document .parse ("{ _id : 1 }" ));
163- return null ;
164- } );
163+ doWithSystemNanoTimeHandle ( systemNanoTimeHandle ->
164+ session .withTransaction (() -> {
165+ systemNanoTimeHandle . setRelativeToStart (ERROR_GENERATING_INTERVAL );
166+ collection .insertOne (session , Document .parse ("{ _id : 1 }" ));
167+ return null ;
168+ }) );
165169 fail ("Test should have thrown an exception." );
166170 } catch (Exception e ) {
167171 assertEquals (251 , ((MongoException ) e ).getCode ());
@@ -224,9 +228,9 @@ public void testRetryBackoffIsEnforced() throws InterruptedException {
224228 long noBackoffTime ;
225229 try (ClientSession session = client .startSession ();
226230 FailPoint ignored = FailPoint .enable (failPointDocument , getPrimary ())) {
227- long startNanos = System . nanoTime ();
231+ StartTime startTime = StartTime . now ();
228232 session .withTransaction (() -> collection .insertOne (session , Document .parse ("{}" )));
229- noBackoffTime = TimeUnit . NANOSECONDS .toMillis (System . nanoTime () - startNanos );
233+ noBackoffTime = startTime . elapsed () .toMillis ();
230234 } finally {
231235 // Clear the test jitter supplier to avoid affecting other tests
232236 ExponentialBackoff .clearTestJitterSupplier ();
@@ -241,9 +245,9 @@ public void testRetryBackoffIsEnforced() throws InterruptedException {
241245 long withBackoffTime ;
242246 try (ClientSession session = client .startSession ();
243247 FailPoint ignored = FailPoint .enable (failPointDocument , getPrimary ())) {
244- long startNanos = System . nanoTime ();
248+ StartTime startTime = StartTime . now ();
245249 session .withTransaction (() -> collection .insertOne (session , Document .parse ("{}" )));
246- withBackoffTime = TimeUnit . NANOSECONDS .toMillis (System . nanoTime () - startNanos );
250+ withBackoffTime = startTime . elapsed () .toMillis ();
247251 } finally {
248252 ExponentialBackoff .clearTestJitterSupplier ();
249253 }
@@ -278,7 +282,18 @@ public void testExponentialBackoffOnTransientError() throws InterruptedException
278282 }
279283 }
280284
281- private boolean canRunTests () {
285+ private static boolean canRunTests () {
282286 return isSharded () || isDiscoverableReplicaSet ();
283287 }
288+
289+ private static void doWithSystemNanoTimeHandle (final Consumer <SystemNanoTimeHandle > action ) {
290+ long startNanos = SystemNanoTime .get ();
291+ try (MockedStatic <SystemNanoTime > mockedStaticSystem = Mockito .mockStatic (SystemNanoTime .class )) {
292+ action .accept (change -> mockedStaticSystem .when (SystemNanoTime ::get ).thenReturn (startNanos + change .toNanos ()));
293+ }
294+ }
295+
296+ private interface SystemNanoTimeHandle {
297+ void setRelativeToStart (Duration change );
298+ }
284299}
0 commit comments