Clockseasy0-2 years

A retry-backoff implementation computes the delay as `Instant.now().minus(startedAt)`. What can go wrong with that specifically, and what should it use instead?

Instant.now() reads the wall clock, and a wall clock doesn't just drift slowly — NTP corrects large errors by stepping it, jumping the clock backward or forward outright rather than gently slewing it. If that step happens during the measured interval, Instant.now().minus(startedAt) can come out negative (the timeout never fires) or far too large (it fires 31 minutes early). System.nanoTime() reads a different clock entirely: one that only ever moves forward at a steady rate and is completely unaffected by NTP corrections, but whose value only means anything as a difference from another nanoTime() reading on the same JVM. The rule is two clocks for two jobs — wall time answers "when did this happen, for a person"; monotonic time answers "how long did this take, for a program" — and every timeout, retry backoff, latency measurement or cache TTL belongs to the second job.

The lesson behind it →
More on Clocks