-
Type: Bug
-
Resolution: Unresolved
-
Priority: Minor - P4
-
None
-
Affects Version/s: 4.3.0
-
Component/s: Connection Management
-
Fully Compatible
-
Not Needed
General problem explanation
System.currentTimeMillis represents a wall-clock time and therefore is not monotonic, i.e., it is allowed to go backwards: return t1 and after that (in the happens-before order) return t2 < t1. While the lack of monotonicity is not explicitly expressed in the method specification, the fact that it returns wall-clock time and one can set machine time to a past instant (NTP implementations may also do that) implies the lack of monotonicity. As a result, naive attempts to calculate, e.g. timeout expiration, may fail: the expression (currentTime = System.currentTimeMillis()) - startTime > timeout, where timeout is 1000 ms may evaluate to false for about an hour if the machine time is shifted back 1 hour, which misses the timeout for no good reason.
System.nanoTime on the other hand does not represent a wall-clock time (this is explicitly expressed in the specification) and is explicitly allowed to be used for measuring elapsed time.
The Go standard library tries to prevent users from using a non-monotonic timer when measuring elapsed time by hiding both timers behind a single API: https://golang.org/pkg/time/#hdr-Monotonic_Clocks.
Specific problem
Relevant places in the codebase
- https://github.com/mongodb/mongo-java-driver/blob/91e670f86303121c7afe68145d3ca5d341079506/driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java#L357
- https://github.com/mongodb/mongo-java-driver/blob/91e670f86303121c7afe68145d3ca5d341079506/driver-core/src/main/com/mongodb/internal/connection/DefaultConnectionPool.java#L357
- https://github.com/mongodb/mongo-java-driver/blob/91e670f86303121c7afe68145d3ca5d341079506/driver-core/src/main/com/mongodb/internal/connection/UsageTrackingInternalConnection.java#L52
- https://github.com/mongodb/mongo-java-driver/blob/91e670f86303121c7afe68145d3ca5d341079506/driver-core/src/main/com/mongodb/internal/connection/UsageTrackingInternalConnection.java#L95
- ...
Proposed solution
UsageTrackingInternalConnection does not need to track wall-clock time in openedAt/lastUsedAt. We can and should use System.nanoTime to detect if maxConnectionIdleTimeMS/maxConnectionLifeTimeMS is exceeded.
- is related to
-
JAVA-4070 ClientSessionClock uses non-monotonic timer, which is then used to implement ClientSessionImpl.MAX_RETRY_TIME_LIMIT_MS
- Backlog
-
JAVA-4071 ServerSessionPool uses non-monotonic timer to decide when to prune a server session
- Backlog
-
JAVA-4072 BenchmarkSuite uses non-monotonic timer to measure the run time of a benchmark
- Backlog