When time goes backwards  / field guide
Practitioner field guide · 2026-09-05

Time cannot go backwards, and other lies production believed

Every machine's opinion of the current time is wrong by an unknown, changing amount that occasionally jumps. This guide reconstructs how production systems live with that fact, from two postmortems, three Linux kernel commits, the Go and Rust standard library design records, Kubernetes leader election, and the clock architectures published by Google, Meta, AWS and CockroachDB. Afterwards you can audit a codebase for the wall-clock subtraction bug, decide when a lease needs a fencing token, and price bounded uncertainty against uncertainty restarts for your own ordering problem.

23 primary sources 12 organisations 4 incidents dissected Evidence through Sep 2026 Read: 25 min
01

The territory

The problem, stated without naming a clock API: two machines must agree which of two events came first, and one machine must know how long something took, while the instrument both questions depend on is being silently adjusted underneath them.

1–7ms
TrueTime's clock uncertainty ε in Google's production fleet, a sawtooth averaging about 4ms
500ms
CockroachDB's default maximum clock offset on commodity NTP; nodes past 80% of it shut themselves down
0.2%
of Cloudflare DNS queries failing at peak on 2017-01-01, after one second of backwards time
~30%
of time.Now calls in Russ Cox's Go corpus were measuring elapsed time with the wrong clock

A computer carries two instruments that both call themselves "the time". The wall clock (CLOCK_REALTIME) answers "what time is it", is disciplined by an outside authority through a sync daemon, and therefore jumps: forwards, backwards, or sideways at 11.6 parts per million during a leap smear. The monotonic clock (CLOCK_MONOTONIC) answers "how long has it been", counts from boot, promises only to never decrease, and means nothing outside the process that read it. Go's time package documentation compresses the whole field into one sentence: the wall clock is for telling time, the monotonic clock is for measuring time [15].

Nearly every incident in this guide is a system reaching for the wrong instrument, or assuming one instrument has the other's properties. Cloudflare subtracted two wall-clock readings and got a negative duration [1]. Azure did calendar arithmetic by incrementing a year field and manufactured a date that does not exist [2]. The Linux kernel itself, the thing that implements both clocks, deadlocked and then spin-stormed on the leap seconds of 2012 [3, 4]. And every last-write-wins datastore quietly turns "which node's clock reads later" into "whose data survives" [22].

The surprise in this record

The industry's chosen fix for the leap second is to make every clock deliberately wrong, and no two operators make them wrong the same way. Google smears the extra second linearly over 24 hours, noon to noon [18]; in 2016 it used 20 hours [17]; Meta smears over 17 hours starting at midnight [19]. During a leap event, two fleets, each perfectly synchronised to its own provider, can disagree by most of a second. That is why CockroachDB's operations guidance flatly requires all nodes to use smeared time sources from one regime [16]: the danger is not the leap second, it is mixing answers about it.

Figure 1 · Two instruments, one word

step, slew
or smear

subtracting two readings:
the outage path

Time sources
GPS, atomic, NTP pool

Sync daemon
chrony / ntpd

Wall clock
CLOCK_REALTIME
tells time, can jump

Boot counter

Monotonic clock
CLOCK_MONOTONIC
never decreases

Timestamps that leave
the process: logs, certs,
ordering, schedules

Durations inside
the process: timeouts,
leases, backoff

step, slew
or smear

subtracting two readings:
the outage path

Time sources
GPS, atomic, NTP pool

Sync daemon
chrony / ntpd

Wall clock
CLOCK_REALTIME
tells time, can jump

Boot counter

Monotonic clock
CLOCK_MONOTONIC
never decreases

Timestamps that leave
the process: logs, certs,
ordering, schedules

Durations inside
the process: timeouts,
leases, backoff

The wall clock is externally disciplined and can jump; the monotonic clock only moves forward and only means something locally. The red path, subtracting two wall readings to get a duration, is the exact path in Cloudflare's 2017 postmortem.
Diagram source

Scope. This guide covers the failure modes of trusting machine clocks and the four production answers to them: the monotonic split, smearing, leases with fencing, and bounded uncertainty. It deliberately does not cover NTP or PTP protocol engineering and security, GPS spoofing, timezone and tzdata handling, or the regulatory clock-sync mandates in finance. Those are real topics with their own records; they are not this dig.

02

How it is actually built

The common shape across Google, AWS, Meta, CockroachDB and the two language runtimes: a distribution layer that makes clocks less wrong, a runtime layer that stops you holding the wrong clock, and a coordination layer that decides how much wrongness the design absorbs.

Figure 2 · The reference clock stack

Coordination layer

Runtime layer

Distribution layer

Reference clocks
GPS + atomic

Sync daemon
chrony, PTP, smearing NTP

Kernel clocks
REALTIME + MONOTONIC

Go: one Time value,
two readings

Rust: Instant vs SystemTime,
saturating subtraction

Order by timestamp
last write wins

Leases + fencing tokens

Uncertainty interval ε
commit wait or restart

Coordination layer

Runtime layer

Distribution layer

Reference clocks
GPS + atomic

Sync daemon
chrony, PTP, smearing NTP

Kernel clocks
REALTIME + MONOTONIC

Go: one Time value,
two readings

Rust: Instant vs SystemTime,
saturating subtraction

Order by timestamp
last write wins

Leases + fencing tokens

Uncertainty interval ε
commit wait or restart

Every production system in this corpus has all three layers; they differ only in how far right they push the coordination column. Reconstructed from Spanner, AWS ClockBound, CockroachDB's design doc and the Go time package.
Diagram source

The three layers, and who runs them how

Distribution. Google's TrueTime feeds every datacenter from GPS receivers plus a minority of atomic-clock "Armageddon masters", chosen because the two reference types fail in uncorrelated ways; daemons poll a mix of nearby and far masters every 30 seconds and advertise a worst-case error, assuming local drift of at most 200 microseconds per second between polls [14]. AWS ships the same idea as a managed commodity: the Time Sync Service claims synchronisation "within microseconds of UTC" on supported EC2 instances (a vendor claim; no independent measurement appears in this corpus) [21], and the open-source ClockBound daemon exposes it as a pair of timestamps, earliest and latest, "within which true time exists", a bound that visibly grows between clock updates [20]. CockroachDB's operations guidance shows the floor of this layer: chrony against smeared sources, a configured maximum offset of 500 ms, and a node that finds itself more than 80% of that away from a majority of its peers shuts down rather than serve inconsistent reads [16].

Runtime. This layer exists because application programmers will otherwise subtract wall-clock readings; the Go corpus analysis put that mistake in roughly 30% of time.Now call sites [10]. Go's answer, shipped in Go 1.9 after the design record discussed in section 3, is to hide both readings inside one value: time.Now captures wall and monotonic together, time-telling operations use the wall reading, and subtraction or comparison silently use the monotonic one [13, 15]. Rust keeps two visible types, SystemTime and Instant, and spent 2018 to 2022 learning what to do when the operating system's "monotonic" clock is not [6, 7, 8].

Coordination. Three positions, in increasing order of engineering and latency cost. Order by raw timestamp and accept that concurrent or skewed writes are silently discarded; this is last-write-wins, and Kyle Kingsbury's Jepsen work demonstrated acknowledged-write loss under it years ago [22]. Grant time-limited leadership with leases, and either accept the fencing gap or make the protected resource check a monotonically increasing token, the repair Martin Kleppmann argued for in 2016 [12]; Kubernetes ships the un-fenced version with the caveat written into the package comment [11]. Or carry the clock error as data: Spanner assigns a commit timestamp and then waits out the uncertainty (about 5 ms measured in the paper's benchmarks) so the timestamp is guaranteed past before anyone sees it, while CockroachDB, on 500 ms commodity bounds where waiting is unaffordable, instead restarts any transaction that reads a value inside its uncertainty interval [14, 13].

The monotonic split

One clock for meaning, one for arithmetic. The runtime either hides the split (Go) or makes it a type distinction (Rust). Either way, a duration must never be computed from two wall readings.

Recorded at: Go proposal, rust-lang #56612

The smear layer

Leap seconds are absorbed by lying smoothly: run every clock slightly slow for hours instead of stepping once. The lie is bounded, published, and different per provider, which makes provider mixing the new hazard.

Recorded at: Google, Meta

The uncertainty bound

Instead of one timestamp, an interval [earliest, latest] the truth is inside. Systems then buy correctness by waiting the interval out, or by retrying anything that lands in it.

Recorded at: ClockBound, CockroachDB design.md

03

The decisions that matter

Four forks in the road, each with a recorded argument behind it, and the condition that flips the answer.

One time API carrying two clocks, or two APIs the programmer chooses between?

Chosen
  • Go: a single time.Time holding both a wall and a monotonic reading; subtraction and comparison silently use the monotonic one [10]
  • Fixed 29% of corpus call sites with zero code changes [10]
Rejected
  • Separate wall and monotonic APIs, the design of nearly every other platform
  • Cox's stated reason: misuse "occurs simultaneously on all systems", so "all the copies of the program across the entire distributed system fail simultaneously, defeating any redundancy the system might have had" [10]
Flips when
  • The value leaves the process. Serialisation strips the monotonic reading because it has "no meaning outside the current process" [15], so cross-machine maths is back on the wall clock and needs section 3's third decision instead

When the OS monotonic clock is itself buggy: enforce, panic, or saturate?

Chosen (eventually)
  • Rust 2022: make Instant subtraction saturate to zero and delete the enforcement machinery [8]
  • Debug builds still panic, so tests catch genuinely reversed operands
Tried first, then reversed
  • Rust 2019: force monotonicity in std with a global watermark, because "we tried relying on OS/hardware/clock implementations, but those seem buggy enough that we can't rely on them in practice" [7]
  • Reversed when the cost surfaced: "we must choose between two poisons", with worst-case synchronisation overhead above 100x [8]
Flips when
  • Your telemetry shows real platform bugs (the panics were tracking hypervisor and firmware defects, not swapped operands); saturation is the right default only because a zero duration is survivable where a panic in production is not

Leap second: step the clock, or smear it?

Chosen
  • Smear: Google runs clocks 0.0014% slow for 20 hours (2016) [17], later standardising 24 hours noon-to-noon at about 11.6 ppm, which AWS also uses [18]; Meta smears 17 hours from midnight [19]
Rejected
  • Stepping, the POSIX-correct behaviour, because the kernel's own step path deadlocked in one leap second era and fired timers a second early in the next [3, 4]
Flips when
  • You need true UTC during the window (metrology, some regulated capture), or when any node in a consistency domain uses a different smear; CockroachDB requires one smeared regime across all nodes for exactly this reason [16]

Ordering across machines: timestamps, leases, or paid-for uncertainty?

Chosen, by cost tier
  • Spanner: commit wait, about 5 ms measured, affordable because ε averages 4 ms on GPS and atomic hardware [14]
  • CockroachDB: hybrid logical clocks plus uncertainty restarts, because waiting out 500 ms per commit is absurd [13, 16]
Rejected
  • Raw timestamp ordering (last write wins): loses acknowledged writes under concurrency and skew, per Jepsen's analyses [22]
  • Un-fenced leases for anything that writes: the Kubernetes package comment concedes the gap openly [11]
Flips when
  • ε changes hands: with cloud microsecond-class sync and a ClockBound-style API [20, 21], commit wait becomes affordable on rented hardware, which is exactly the future CockroachDB's own design doc reserves for itself [13]

Figure 3 · Choosing a clock discipline for one value

no

yes

no

yes

yes, small

yes, large

no

yes

no

Does the value leave
the process?

Use the monotonic clock.
Never subtract wall readings.

Is it deciding order
or exclusivity?

Wall clock is fine.
Timestamp it, note the sync regime.

Is a bounded clock
error ε available?

Commit wait:
wait out ε before visibility

Uncertainty restarts:
retry reads inside the interval

Can the resource
verify a token?

Lease + fencing token

Use consensus for the decision.
Do not order by timestamp.

no

yes

no

yes

yes, small

yes, large

no

yes

no

Does the value leave
the process?

Use the monotonic clock.
Never subtract wall readings.

Is it deciding order
or exclusivity?

Wall clock is fine.
Timestamp it, note the sync regime.

Is a bounded clock
error ε available?

Commit wait:
wait out ε before visibility

Uncertainty restarts:
retry reads inside the interval

Can the resource
verify a token?

Lease + fencing token

Use consensus for the decision.
Do not order by timestamp.

Terminal nodes are actions. The dangerous default is the leftmost branch taken unknowingly: a wall-clock subtraction that never leaves the process it breaks. Distilled from the decision records above.
Diagram source
DecisionChosenRejectedBecauseEvidence
Go time APITwo readings in one valueTwo visible APIsMisuse fails everywhere at once, on a leap secondGo proposal, 2017
Rust Instant on buggy platformsSaturate to zeroGlobal enforcement, panicsEnforcement cost >100x worst case; panics tracked platform bugsPR #89926, 2022
Leap second handlingSmear over hoursStep at midnightThe step path broke the kernel twice in 2012kernel commit 4873fa07
Cross-node orderingε-aware timestamps or fenced leasesRaw last write winsLWW converts clock skew into silent data lossKingsbury, 2013
Wait or retry on εSpanner waits; CockroachDB retriesThe other one, respectivelyPurchasable ε of 4 ms vs commodity 500 msdesign.md
04

What broke in production

Three failure classes account for the public record: the backwards subtraction, the impossible date, and the kernel's own time step. The fourth class, silent last-write-wins loss, has no postmortem, and that absence is a finding.

Figure 4 · The Cloudflare failure path, 2017-01-01 00:00 UTC

Upstream resolverWall clockRRDNS (Go process)Upstream resolverWall clockRRDNS (Go process)leap second insertedclock reads 1s earlierpanic on every affectedmachine, the same instantt1 = time.Now()health probereplyt2 = time.Now()latency = t2 - t1,negativestore negative weightlater lookup callsrand.Int63n(weight)
Upstream resolverWall clockRRDNS (Go process)Upstream resolverWall clockRRDNS (Go process)leap second insertedclock reads 1s earlierpanic on every affectedmachine, the same instantt1 = time.Now()health probereplyt2 = time.Now()latency = t2 - t1,negativestore negative weightlater lookup callsrand.Int63n(weight)
The panic is three steps removed from the clock: a stored negative "latency" poisons the resolver-selection state, and the crash fires on later lookups. Sequence reconstructed from the postmortem.
Diagram source
Postmortem

The backwards subtraction: Cloudflare DNS, 2017

Assumption"The belief that time cannot go backwards", in the postmortem's own words: elapsed time computed from two wall readings would be at worst zero.
What happenedThe leap second stepped the wall clock back; RRDNS stored a negative upstream latency; the weighted resolver selection fed it to Go's rand.Int63n, which panics on negative input.
Blast radiusAt peak about 0.2% of DNS queries, under 1% of HTTP requests, CNAME customers only; most-affected machines patched in 90 minutes, worldwide fix by 06:45 UTC.
FixPatch the arithmetic, then structurally: Go 1.9's transparent monotonic tracking, proposed 25 days after the outage, made this class of subtraction safe language-wide.
Design ruleCorrelated inputs defeat redundancy. A clock reset happens on every machine in the same instant, so a time bug is a fleet-wide simultaneous fault, not a rolling one. Test with time warps, not just load.
Postmortem

The impossible date: Azure leap day, 2012

AssumptionAdding one to the year field of today's date always yields a valid date.
What happenedOn Feb 29 the guest agent minted transfer certificates valid to "February 29, 2013", which does not exist; certificate creation failed and VMs stalled in initialisation.
Blast radiusThe health machinery amplified it: three straight VM failures marked hosts as suspected hardware faults ("human investigate"), pulling healthy servers from service across multiple subregions. Detection took 2 hours 38 minutes.
FixDate arithmetic through a calendar library, plus changes to stop the fault-escalation loop treating a software bug as fleet-wide hardware failure.
Design ruleA time bug plus an automated remediation loop is the actual outage. Cap how many hosts any health verdict can condemn per window; Azure's bug was minutes of damage, the escalation made it a day.
Source

The kernel's own second: Linux leap handling, 2012

AssumptionThe code that adjusts the clock can safely run under the locks that everything reading the clock also takes.
What happenedTwo distinct defects: a livelock between the NTP lock and the leap-second hrtimer, and, on the June 30 leap second itself, hrtimers seeing new time with stale offsets so CLOCK_REALTIME timers fired a second early; applications in timed futex waits woke instantly, looped, and pinned CPUs across the industry.
Blast radiusWidely reported outages that night (contemporaneous press names Reddit, Mozilla's Hadoop infrastructure, LinkedIn and others; corroborated, not first-party). The commit message even records the folk remedy operators used mid-incident: date -s "`date`".
FixMove leap processing out of the hrtimer into the tick path, and update hrtimer base offsets atomically with the time change so "timers are not expired early".
Design ruleThe layer that implements time is not exempt from time bugs. If your platform steps clocks, your platform is part of your failure model; this is the strongest argument the smear camp has.
Blog

The silent class: last write wins, no postmortem exists

AssumptionThe write with the later timestamp is the later write.
What happenedUnder skewed clocks, whichever node reads later wins conflicts it should lose; Jepsen demonstrated acknowledged-write loss in LWW stores in controlled tests as far back as 2013. No operator postmortem in the public record attributes a production data loss to it.
Blast radiusOpen. The failure leaves availability, latency and error rates untouched; the record's silence is more plausibly a detection gap than an absence of events. This is the reader's risk, stated plainly.
FixNone observed in the wild post-hoc. Preventively: fenced writes, causal or hybrid logical clocks, or an ε-aware protocol.
Design ruleIf a failure mode is invisible to every dashboard you own, the absence of incidents is not evidence of absence. Build a reconciliation check that would notice a lost write before deciding LWW is fine.
The most common clock reset in a well-run production setting is the leap second, which occurs simultaneously on all systems. When it does, all the copies of the program across the entire distributed system fail simultaneously, defeating any redundancy the system might have had. Russ Cox, Go monotonic time proposal, January 2017

Figure 5 · A decade of learning the same lesson

2012June 30 leap second,kernel livelock andearly-firing timersStultz patches land,operators run date -sworkaroundFeb 29, Azure leapday certificate outage2015October, Go issue12914 asks for amonotonic clock API2016Google publishes its20-hour leap smear2017Jan 1, Cloudflare DNSoutage from anegative durationJan 26, Go monotonicproposalGo 1.9 ships tworeadings in one value2018December, Rust issue56612, Instant goesbackward2019January, Rust forcesmonotonicity in std2022February, Rustreverses to saturatingarithmeticJuly, Meta calls forabolishing the leapsecondWall clocks vs production, 2012 to 2022
2012June 30 leap second,kernel livelock andearly-firing timersStultz patches land,operators run date -sworkaroundFeb 29, Azure leapday certificate outage2015October, Go issue12914 asks for amonotonic clock API2016Google publishes its20-hour leap smear2017Jan 1, Cloudflare DNSoutage from anegative durationJan 26, Go monotonicproposalGo 1.9 ships tworeadings in one value2018December, Rust issue56612, Instant goesbackward2019January, Rust forcesmonotonicity in std2022February, Rustreverses to saturatingarithmeticJuly, Meta calls forabolishing the leapsecondWall clocks vs production, 2012 to 2022
The Go API gap was on file for 15 months before the outage; the proposal followed the outage by 25 days. Rust then walked the same road in the other direction. Dates from the linked issues, commits and posts in the references.
Diagram source
05

Numbers you can plan against

Everything quantitative in the corpus, dated and attributed. Measured unless marked as a vendor claim.

MetricValueAtContextAs ofSource
Clock uncertainty ε1–7 ms, avg ~4 msGoogleSawtooth per 30 s poll, GPS + atomic references; measured2012Spanner paper
Assumed worst-case local drift200 µs/sGoogleThe bound ε is derived from; "bad CPUs are 6 times more likely than bad clocks"2012Spanner paper
Commit wait~5 msGoogleMeasured, 1-replica microbenchmark; the price of external consistency2012Spanner paper
Default max clock offset500 msCockroachDBCommodity NTP assumption; consistency depends on staying inside it2026runbook
Self-termination threshold80% of max offsetCockroachDBNode shuts down when drifted vs a majority of peers2026runbook
Leap smear, 2016 event0.0014% for 20 hGoogleClocks run slow either side of the leap second2016Google blog
Standard smear24 h, ~11.6 ppmGoogle, AWSLinear, noon to noon; within quartz thermal errorcurrentsmear doc
Meta smear17 h from 00:00 UTCMetatzdata-driven; a third, different regime2022Meta blog
Cost of enforced monotonicity>100× worst caseRust stdSynchronised Instant::now overhead and jitter; why it was removed2021PR #89926
Wrong-clock call sites~30% of time.NowGo corpusBy source appearance; 29% of uses auto-fixed by the proposal2017Go proposal
Cloudflare impact0.2% DNS, <1% HTTPCloudflarePeak error rates; 90 min to patch worst machines2017postmortem
Azure detection gap2 h 38 minMicrosoftFirst trigger to bug identification, leap day 20122012postmortem
Cloud clock sync"within microseconds"AWSVendor claim for Time Sync on supported instances; not independently measured here2023AWS announcement
Read these carefully

The Spanner figures are 2012 measurements of purpose-built hardware; treat them as a floor for what money can buy, not what your fleet has. The AWS microsecond figure is a vendor claim with no independent measurement in this corpus. The CockroachDB 500 ms is a default, not a measurement of your NTP; the runbook's whole point is that you must measure. And the 30% Go figure counts call sites, not call volume.

06

The evidence wall

Every source behind this page, graded. This corpus is unusually strong in source-code records (the arguments happened in public issue trackers) and carries no talk tier: the relevant talks exist but sit on hosts this research session could not reach, so they are pointed to in section 8 rather than cited as read.

Postmortem Cloudflare2017-01

How and why the leap second affected Cloudflare DNS

The canonical backwards-time incident: a negative wall-clock subtraction stored as state, a panic three steps later, and a fleet failing simultaneously. Names its own root cause as a belief, not a bug.

Carry forwardGrep for durations computed from wall-clock reads; each one is a fleet-wide simultaneous fault waiting for a clock step.
blog.cloudflare.com
Postmortem Microsoft2012-03

Summary of Windows Azure Service Disruption on Feb 29th, 2012

Year+1 date arithmetic met February 29. The lasting lesson is the amplifier: health automation read a software bug as mass hardware failure and quarantined healthy hosts.

Carry forwardBound the blast radius of automated health verdicts; a time bug fires everywhere at once and looks exactly like fleet-wide hardware death.
azure.microsoft.com
Source Linux kernel2012

commit 6b43ae8a: ntp: Fix leap-second hrtimer livelock

The leap-second insertion path could deadlock against the timekeeping locks. The subsystem that implements clocks is not immune to clock bugs.

Carry forwardIf your platform steps time, the step itself is in your failure model; smearing exists to retire this whole class.
github.com/torvalds/linux
Source Linux kernel2012-07

commit 4873fa07: timekeeping: Fix leapsecond triggered load spike issue

After the June 2012 leap second, CLOCK_REALTIME hrtimers fired a second early and applications spun. The commit message preserves the industry-wide workaround, date -s "`date`", as a fossil of the night.

Carry forwardTimers armed against the wall clock inherit every wall-clock discontinuity; absolute deadlines belong on the monotonic clock.
github.com/torvalds/linux
Source Go project2015-10

Issue #12914: time: use monotonic clock to measure elapsed time

The request that sat for 15 months: Go offered no monotonic source, so measuring an operation reliably required platform-specific code. Milestoned to Go 1.9 only after the 2017 outage.

Carry forwardA known API gap costs little until the day it costs a fleet; the issue tracker dates let you show a review exactly how that timeline runs.
github.com/golang/go
ADR Go project2017-01

Proposal: Monotonic Elapsed Time Measurements in Go

Russ Cox's design record: cites the Cloudflare outage as motivation, quantifies the corpus (30% of call sites measure elapsed time), and rejects the two-API design because its failures are simultaneous fleet-wide, not random.

Carry forwardJudge API designs by the correlation of their failure modes, not just their frequency; rare-but-simultaneous is worse than common-but-independent.
github.com/golang/proposal
Vendor Go project2017

Go 1.9 release notes: transparent monotonic time

The shipped outcome: "the time package now transparently tracks monotonic time in each Time value, making computing durations between two Time values a safe operation in the presence of wall clock adjustments."

Carry forwardLanguage-level fixes retire bug classes; if your platform predates one, the class is still open in your codebase.
github.com/golang/go
Vendor Go projectcurrent

package time, Monotonic Clocks section

The operating rule and its boundary: subtraction and comparison use the monotonic reading; serialisation strips it because it has no meaning outside the process.

Carry forwardThe protection ends at the process boundary; any timestamp that crosses a wire is wall-clock again, with everything that implies.
pkg.go.dev/time
Source Rust project2018-12

Issue #56612: Instant::now can go backward

The monotonic clock itself lying: QueryPerformanceCounter regressing on some Windows multi-core systems, panicking Rust programs with "specified instant was later than self".

Carry forward"Monotonic" is a promise the OS sometimes breaks via hardware and hypervisors; decide before shipping what your code does when it happens.
github.com/rust-lang/rust
Source Rust project2019-01

PR #56988: std: Force Instant::now() to be monotonic

The first answer: a global watermark in std, mirroring Firefox, because platform clocks "seem buggy enough that we can't rely on them in practice".

Carry forwardEnforcement in a hot path has a price you have not measured yet; record the option to retreat when you take this road.
github.com/rust-lang/rust
Source Rust project2022-02

PR #89926: make Instant arithmetic saturating, remove workarounds

The reversal, three years later: "we must choose between two poisons", enforcement costing over 100x worst-case, versus rare panics from platform bugs. Rust chose the quiet poison and saturates to zero.

Carry forwardWhen both options are poisons, pick the one whose failure is survivable in production and loud in tests; saturate in release, panic in debug.
github.com/rust-lang/rust
Source Kubernetescurrent

client-go leaderelection.go package comment

Production leader election that says the quiet part in its doc comment: "this implementation does not guarantee that only one client is acting as a leader (a.k.a. fencing)". Tolerates arbitrary skew, is sensitive to skew rate.

Carry forwardRead the package comment before betting exclusivity on a lease; if the comment says no fencing, your storage layer needs the token check.
github.com/kubernetes/client-go
Blog Martin Kleppmann2016-02

How to do distributed locking

The GC-pause argument: a client can acquire a lease, stall past expiry, and resume convinced it still holds the lock. The repair is a fencing token, a monotonically increasing number the protected resource itself checks. Also the source of the efficiency-versus-correctness framing in the Redlock debate.

Carry forwardA lease bounds how long you wait for a dead leader, not who may write; only the resource can enforce exclusivity, via a token it verifies.
martin.kleppmann.com
ADR CockroachDB2026 (maintained)

docs/design.md: hybrid logical clocks and uncertainty intervals

The public design record of ordering on commodity clocks: per-node HLCs, transaction reads carrying an interval up to t+ε, conflicts inside it forcing a retry, and an explicit note that with better clocks it would commit-wait like Spanner instead.

Carry forwardε is a design input, not a constant; write the flip condition into your design doc the way this one does.
github.com/cockroachdb/cockroach
Vendor Cockroach Labs2026 (maintained)

Runbook: clock management

The operational contract behind the design: 500 ms default max offset, node suicide at 80% drift versus a majority, chrony recommended, and only smeared or slewed leap second sources permitted across a cluster.

Carry forwardA consistency guarantee conditioned on clock bounds needs an enforcement mechanism for the bounds themselves; self-termination is that mechanism.
github.com/cockroachlabs
Paper Google2012-10

Spanner: Google's Globally-Distributed Database (OSDI 2012)

TrueTime measured in production: ε a sawtooth of 1 to 7 ms, 4 ms typical, derived from an assumed 200 µs/s worst-case drift; commit wait around 5 ms; GPS and atomic references chosen for uncorrelated failure. Read via the Papers We Love mirror of the OSDI publication.

Carry forwardExternal consistency is purchasable: its price is ε per commit, so every dollar spent shrinking ε is latency bought back.
papers-we-love mirror
Paper Kulkarni, Demirbas et al.2014-05

Logical Physical Clocks and Consistent Snapshots

The hybrid logical clock: causality tracking that stays close to physical NTP time, so one timestamp orders related events and cuts consistent snapshots. The construction CockroachDB's design doc cites and implements. (Paper host unreachable in this session; mechanism corroborated through the fetched design doc.)

Carry forwardWhen you cannot bound the clock, bound the causality: HLC gives LWW-style convenience without ordering unrelated writes by skew.
cse.buffalo.edu
Blog Google2016-11

Making every (leap) second count with our new public NTP servers

The smear, first-party: clocks 0.0014% slower for ten hours either side of the leap second, so "December 31 will seem like any other day".

Carry forwardSmearing converts a discontinuity into a bounded rate error; your monotonic-derived measurements during the window are off by that rate, knowingly.
cloud.google.com
Vendor Googlecurrent

Leap Smear documentation (developers.google.com/time)

The proposed standard: 24-hour linear smear, noon to noon, roughly 11.6 ppm, adopted by AWS; Google itself moved from 20 hours to align. Standardisation is explicitly the goal because divergent smears disagree.

Carry forwardBefore a leap event, inventory every time source your estate consumes and confirm they share one smear regime; the disagreement window is hours long.
developers.google.com/time/smear
Blog Meta2022-07

It's time to leave the leap second in the past

Meta's position paper: 27 leap seconds so far, each one an industry incident; Meta smears over 17 hours from midnight UTC; "introducing new leap seconds is a risky practice that does more harm than good".

Carry forwardThe mechanism is scheduled for retirement politically as well as technically; design for the smear era, not for heroic step handling.
engineering.fb.com
Source AWS2026 (maintained)

ClockBound: daemon and library for bounded timestamps

Bounded uncertainty as an open-source commodity: every reading is (earliest, latest) "within which true time exists", and the bound honestly grows between clock updates.

Carry forwardIf your ordering logic cannot articulate what it does with an interval instead of an instant, it is not ready for ε-aware design.
github.com/aws/clock-bound
Vendor AWS2023-11

Amazon Time Sync: microsecond-accurate time

The claim that changes the economics: Nitro-based, GPS-disciplined clocks synchronised "within microseconds of UTC" on supported instances, at no extra charge. Unverified independently in this corpus.

Carry forwardCommodity ε in microseconds makes commit-wait designs rentable; re-run the wait-versus-retry decision if you last made it on 500 ms assumptions.
aws.amazon.com
Blog Kyle Kingsbury2013-10

The trouble with timestamps

Why last-write-wins plus wall clocks is a data-loss design: timestamps decide conflicts, clocks decide timestamps, and skew decides clocks. Jepsen's controlled tests made the loss visible; production dashboards do not.

Carry forwardTreat any LWW store as "loses concurrent writes by design" and make the business sign off on that sentence, not on "eventually consistent".
aphyr.com
07

Build a miniature, then productionise it

Six rungs from demonstrating the lie to operating under it. The crossing from toy to real is rung four.

Make time go backwards on purpose

Write a 30-line service that timestamps request pairs with the wall clock and computes latency. Run it under libfaketime or step the clock in a VM with date -s, and watch negative durations appear. Port it to a monotonic source and repeat.

Done when: you have logged a negative duration from the wall-clock version and cannot from the monotonic one.  Teaches: the two-instruments split as an experience, not a rule.

Audit a real codebase

Pick a service you own. Find every duration computed from wall-clock reads: in pre-1.9-style Go, System.currentTimeMillis() deltas on the JVM, new Date() arithmetic in Node, time.time() subtraction in Python. Classify each call site as telling or measuring, the way Cox's corpus analysis did.

Done when: you have a count and a fix list, and know your codebase's version of the 30% number.  Teaches: how invisible this class is in review.

Break a lease with a pause

Implement a lock with a TTL in any store. Two clients contend; pause the holder (SIGSTOP, or a debugger breakpoint) past expiry, then resume it and let it write. Watch both clients act as leader, exactly as the Kubernetes package comment warns.

Done when: you have two "leaders" writing interleaved output on demand.  Teaches: a lease is a clock bet, and the process cannot referee its own pause.

Add the fencing token

Issue a monotonically increasing token with each lock grant and make the protected resource reject writes bearing a token at or below the highest seen. Re-run rung three's pause attack.

Done when: the stale leader's write is refused by the resource, not by the leader's own good behaviour.  Teaches: exclusivity is enforced at the resource or not at all. This is the toy-to-real crossing.

Build a toy hybrid logical clock

Two nodes with deliberately skewed clocks exchange messages, each carrying an HLC timestamp per the Kulkarni construction. Verify that message order is respected in HLC order despite the skew, and that HLC time stays near wall time.

Done when: a causally-later write always carries a larger HLC timestamp under 10 s of injected skew.  Teaches: causality is orderable without trusting either clock.

Operate under an uncertainty bound

On EC2, install ClockBound and log (earliest, latest) across instances; elsewhere, simulate a bound from NTP offset stats. Implement one commit-wait: hold a write invisible until latest-at-write has passed everywhere. Measure the added latency and compare it to your ε.

Done when: injected skew inside the bound never produces an out-of-order read, and you can state the latency bill.  Teaches: the Spanner trade as a number on your own hardware.

08

Keep hunting

The queries that found this material, copyable. The last block names the talks this session could verify exist but not watch; they are the natural next layer.

Incidents and postmortems

  • cloudflare leap second postmortem "time went backwards"
  • azure "leap day" 2012 "service disruption" guest agent certificate
  • leap second outage 2012 linux futex reddit mozilla
  • "clock skew" postmortem "we" -tutorial

The design arguments, in trackers

  • golang issue 12914 monotonic clock proposal
  • rust "Instant::now can go backward" saturating
  • torvalds commit "leap-second hrtimer livelock"
  • leaderelection.go "a.k.a. fencing" kubernetes

Mechanisms and operations

  • "leap smear" site:developers.google.com OR site:cloud.google.com
  • cockroachdb runbook "clock management" max offset chrony
  • spanner truetime epsilon "commit wait" osdi pdf
  • aws clockbound github "earliest" "latest" bound
  • "hybrid logical clock" kulkarni demirbas consistent snapshots

The layer this session could not reach

  • "keeping time in real systems" kavya joshi strange loop 2017
  • "how to do distributed locking" kleppmann fencing token redlock antirez reply
  • sreconf OR usenix "precision time protocol" meta oleg obleukhov talk
  • jepsen cassandra "last write wins" acknowledged writes lost
09

References

This page was researched from a network environment with a restricted egress allowlist. Sources marked † sit on hosts that allowlist blocked; their content was read through multiple independent search-index extractions, cross-checked against each other and against the fetchable documents that quote them, and every quotation from them in this page appears in at least two of those extractions. The full per-claim ledger, including access notes, ships beside this file as sources.md.

  1. Cloudflare, How and why the leap second affected Cloudflare DNS blog.cloudflare.com, 2017-01-01. Checked 2026-09-05. †
  2. Microsoft, Summary of Windows Azure Service Disruption on Feb 29th, 2012 azure.microsoft.com, 2012-03-09. Checked 2026-09-05. †
  3. John Stultz, ntp: Fix leap-second hrtimer livelock (Linux commit 6b43ae8a) github.com, merged 2012. Checked 2026-09-05.
  4. John Stultz, timekeeping: Fix leapsecond triggered load spike issue (Linux commit 4873fa07) github.com, merged July 2012. Checked 2026-09-05.
  5. John Stultz, hrtimer: Update hrtimer base offsets each hrtimer_interrupt (Linux commit 5baefd6d) github.com, merged July 2012. Checked 2026-09-05.
  6. Rust issue #56612, Instant::now can go backward github.com, opened 2018-12-07. Checked 2026-09-05.
  7. Rust PR #56988, std: Force Instant::now() to be monotonic github.com, merged 2019-01-08. Checked 2026-09-05.
  8. Rust PR #89926, Make Instant::{duration_since, elapsed, sub} saturating github.com, merged 2022-02-13. Checked 2026-09-05.
  9. Go issue #12914, time: use monotonic clock to measure elapsed time github.com, opened 2015-10-13. Checked 2026-09-05.
  10. Russ Cox, Proposal: Monotonic Elapsed Time Measurements in Go github.com/golang/proposal, updated 2017-01-26. Checked 2026-09-05.
  11. Kubernetes client-go, tools/leaderelection/leaderelection.go github.com, current master. Checked 2026-09-05.
  12. Martin Kleppmann, How to do distributed locking martin.kleppmann.com, 2016-02-08. Checked 2026-09-05. †
  13. CockroachDB, docs/design.md (HLC and clock offset sections) github.com, maintained; read 2026-09-05.
  14. Corbett et al., Spanner: Google's Globally-Distributed Database, OSDI 2012 Papers We Love mirror of the USENIX publication. Checked 2026-09-05.
  15. Go project, package time documentation, Monotonic Clocks pkg.go.dev, current. Checked 2026-09-05.
  16. Cockroach Labs, runbook template: clock management github.com, maintained; read 2026-09-05.
  17. Michael Shields, Making every (leap) second count with our new public NTP servers cloud.google.com, 2016-11-30. Checked 2026-09-05.
  18. Google, Leap Smear (Public NTP documentation) developers.google.com, current. Checked 2026-09-05. †
  19. Obleukhov and Byagowi, It's time to leave the leap second in the past engineering.fb.com, 2022-07-25. Checked 2026-09-05. †
  20. AWS, ClockBound github.com, maintained; read 2026-09-05.
  21. AWS, Amazon Time Sync Service now supports microsecond-accurate time aws.amazon.com, 2023-11. Checked 2026-09-05. †
  22. Kyle Kingsbury, The trouble with timestamps aphyr.com, 2013-10. Checked 2026-09-05. †
  23. Kulkarni, Demirbas, Madeppa, Avva, Leone, Logical Physical Clocks and Consistent Snapshots in Globally Distributed Databases SUNY Buffalo tech report 2014-04, May 2014. Checked 2026-09-05. †
  24. Go project, Go 1.9 release notes github.com, 2017. Checked 2026-09-05.