No, UUIDv7 IDs do not sort reliably, and the gap matters precisely where teams rely on it most: keyset pagination, merge logic, and event ordering. An analysis by alexsci argues that RFC-compliant UUIDv7 generators can emit out-of-order IDs after a backwards clock jump followed by a process restart, because none of the implementations its author surveyed persists the last-used timestamp. Cross-node ordering fails too, for reasons older than UUID itself: system clocks cannot be synchronized tightly enough.
The practical read is narrower than “stop using UUIDv7.” The only property the format strictly provides is uniqueness, per the same analysis: “The only thing you can really expect is strong collision avoidance and ID uniqueness.” Rough time-clustering for index locality still works. What breaks is any code that treats sort order as a contract. If your cursor pagination assumes id > last_seen_id returns exactly the next page in creation order, you are depending on a property no surveyed library guarantees.
What UUIDv7 actually guarantees: uniqueness, not monotonicity
The alexsci analysis reads RFC 9562 and reports that nothing in the specification ensures strictly increasing IDs. Its framing is blunt: “So no, there’s nothing in the UUIDv7 specification that would ensure the IDs are strictly monotonically increasing. Specific implementations may do better than others (with sub-millisecond timestamps, counters, well-synchronized clocks, etc), but we’ve known since the 1970s that this problem is not strictly solvable.” Note the word “may.” Implementations can be better than the spec floor, but the spec floor is what you get from a compliant generator you have not audited.
The analysis goes further: “But even on a single node, using a single implementation, UUIDv7 IDs are not strictly monotonic.” This matters because a single-node, single-generator setup is the best case most teams imagine they are operating in. If monotonicity fails there, it fails everywhere downstream.
One caveat before going deeper: the spec text here is quoted secondhand via the alexsci post, which cites RFC 9562 §6.1, so the spec reading is one author’s interpretation. The RFC reading is the load-bearing claim, and you can check it against the spec text directly before making architecture decisions. What follows still holds as a decision framework even if you find the RFC’s counter provisions more forgiving than reported, because the library-level failure mode stands on its own.
Failure mode 1: backwards clock jump plus process restart
The concrete, reproducible-sounding failure is this: a system clock jumps backwards, the generator keeps producing IDs keyed to the (now lower) wall-clock time, and then the process restarts. Any in-memory record of the highest timestamp already used is gone. After restart, the generator reads the clock fresh and can emit IDs with timestamps earlier than IDs already stored. Your append-ordered key space now interleaves.
The mechanism hinges on persistence. The fix is known and stated plainly in the analysis: “To do this safely you’ll need to write the last used timestamp to persistent storage, such that you can read it again after a system reboot or a process restart.” The problem is that nobody seems to do it: “I’ve looked at several UUIDv7 implementations I couldn’t find one that writes timestamps to persistent storage. So implementers seems disinterested in addressing the issue and they generate out-of-order IDs when a process restart follows a clock jumping backwards.”
Two honesty markers on this claim. First, the survey is informal and unnamed; the analysis does not say which implementations were checked, so you cannot use it to conclude that your specific library is vulnerable, only that persistence is not a safe default assumption. Second, this “no implementation persists timestamps” statement reflects a snapshot in time. Libraries change. Treat it as a question to ask of your dependency, not a permanent fact about the ecosystem.
Why no popular library saves you: the missing persisted timestamp
It is worth pausing on why this gap exists, because it shapes the remedy. Persisting a timestamp per ID generation is a write to durable storage on the hot path of an operation people expect to be memory-cheap. That is a real cost, and it is presumably why implementers skip it. The consequence, though, is that the burden of monotonicity shifts to you. If you need strict ordering, the monotonic state becomes application-owned: a persisted last-used timestamp or counter that your service maintains across restarts, since the generator will not.
This is an inference from the survey result, not a measured finding: the alexsci post contains no measurements of how often backwards-jump-plus-restart sequences occur in production. NTP corrections, VM migrations, and manual clock adjustments are all plausible triggers, but frequency is author-reported concern, never measured. What is established is the mechanism and the absence of a guard, not the incident rate.
Failure mode 2: cross-node ordering and the physics of clock skew
Even a perfect, persistent, single-node generator does not get you cross-node ordering, and here the limit is physical rather than implementation choice. The alexsci analysis puts it memorably: “Timestamps are only time-ordered if you know what time it is and you don’t know what time it is.” The argument traces to distributed-systems results from the 1970s: ordering events by timestamp requires clock precision that real systems do not have.
The clock literature backs the physical basis. Hardware clocks tick at distinct rates because of crystal variation, and per research on clock skew in controller area networks, “Two clocks are said to be synchronized at a particular moment if both relative offset and relative skew are zero”, a condition that holds only instantaneously, since skew means the clocks drift apart immediately after.
Could precision synchronization protocols close the gap enough? The bounds are discouraging. Work on IEEE 1588 clock estimators describes how offset and skew are estimated from two-way message exchanges whose delays include “the random queuing delays at each such node.” Queuing delay is statistical noise, so even well-run PTP deployments estimate time rather than know it. And the compensation machinery itself carries correctness pitfalls: a separate analysis of skew-compensated clocks finds that guaranteeing bounds on compensated clocks requires systematic floating-point error analysis. The tooling used to fight skew has its own error budget.
The conclusion for multi-writer systems: no timestamp-based identifier format, UUIDv7, ULID, or anything else keyed to wall-clock time, can deliver a cross-service total order without a sequencing authority. That is the article’s strongest claim and also its best-supported one, because it rests on physics rather than a library survey.
There is a direct counter-claim worth acknowledging. A single-author arXiv preprint comparing UUIDv4, UUIDv7, and ULID states that ULIDs’ structure “allows them to be generated in a distributed manner while maintaining order.” Read carefully, this describes lexicographic sortability of the format, not a guarantee that two nodes’ clocks agree; two ULIDs from skewed clocks sort in clock-timestamp order, not true event order. The preprint reports an 83.7% reduction in network transmission overhead and 97.32% faster generation versus UUIDv4 and UUIDv7, plus a modeled 98.42% lower collision risk versus UUIDv7 in the same study; the figures come from an unreviewed Java Spring Boot, Kafka, and PostgreSQL benchmark, and should be treated as directional at most.
When the fix bites back: stuck future timestamps and counter rollover
Persisting the last-used timestamp solves the restart problem and introduces a subtler one. Suppose a clock is erroneously set to a future date, IDs are generated, and then the clock is corrected backwards to actual time. A last-used-timestamp guard now forces new IDs to keep using the incorrect future timestamp until the real clock catches up. The analysis describes this directly: “a last-used-timestamp guard would force timestamps to use keep using that incorrect timestamp until the system clock catches up.”
During a long catch-up, all IDs pile into the same frozen millisecond, and the per-millisecond counter or random suffix has to absorb them, risking counter rollover. So the mitigation trades one failure mode for another: restart-induced disorder versus a stuck-future-timestamp stall with rollover pressure. Neither is fatal, but a team adopting the persisted-timestamp pattern should decide its rollover policy (block, error, extend the counter) rather than discover it in production.
Decision table: ordering strictness versus identifier scheme
The choice depends on how strict your ordering requirement actually is. A warning on the table below: rows for UUIDv7 and ULID-style schemes are grounded in the cited sources except where a cell says otherwise, while the snowflake and sequence-backed rows are inference from general distributed-systems practice. No source cited here documents snowflake implementations or database sequences, so verify those cells against vendor and database docs before committing.
| Ordering requirement | Plain UUIDv7 | UUIDv7 + persisted counter | ULID-style (same-ms increment) | Snowflake-style | Sequence-backed keys |
|---|---|---|---|---|---|
| Uniqueness + rough index locality | Yes; this is its real strength | Yes | Yes | Yes | Yes |
| Strict single-node order across restarts | No; restart after backwards jump breaks it | Yes, if persistence is correct; rollover policy needed | Not addressed by the cited sources; depends on implementation | Yes if epoch/state persisted (unverified here) | Yes |
| Strict order within one millisecond | Not guaranteed; counter behavior varies by implementation | Yes, by construction | Yes; 1-bit random increment per the preprint | Yes, via sequence bits (unverified here) | N/A |
| Cross-node / cross-service total order | No | No | No | Only with coordination; clock-dependent | Yes, via the database as sequencing authority |
| Distributed generation without coordination | Yes | Yes | Yes | Yes (node IDs) | No |
Two rows deserve prose rather than cells. First, ULID’s same-millisecond monotonicity is a genuine format-level mechanism: “if two ULIDs are generated at the same time, they will still be unique. This is achieved by incrementing the random component just by 1 bit.” But it fixes intra-millisecond ordering only. Cross-node order runs into the same clock-skew limit as every other timestamp-keyed format, and the cited sources say nothing about how ULID implementations behave across restarts. Second, the index-locality motivation for all time-ordered keys is real: the preprint notes that random UUIDv4 IDs created in succession may not be stored close together in the database, causing inefficient indexing and increased disk I/O. Rough time-clustering delivers most of that benefit without any strict-monotonicity guarantee, which is why “UUIDv7 is fine for locality, not for order” is the defensible position rather than “rip it out.” The analysis itself concedes: “In UUIDv7’s favor, roughly ordered IDs are still quite useful. So don’t rip UUIDv7 out of your systems just yet.”
Audit checklist: questions to ask your generator
Because the alexsci post does not name the implementations it surveyed, you need to audit your own dependency. The questions that matter, drawn from the failure modes above:
- Does the implementation persist the last-used timestamp or counter to durable storage, and does it read that state on startup before generating?
- What happens when the system clock moves backwards, and separately, what happens when it was previously set far into the future and corrected?
- How does it order IDs within the same millisecond: monotonic counter, random bits, or sub-millisecond timestamp extension?
- What is the counter-rollover behavior when many IDs land in one millisecond, especially during a stuck-timestamp catch-up?
- Do the README’s “monotonically increasing” claims describe spec guarantees or implementation extras, and are those extras restart-safe?
If your library answers the first question with “no” (and the survey suggests most do), then any strict-ordering requirement in your application is yours to implement.
What the evidence doesn’t yet show
Two gaps should temper how far you run with this. The alexsci post names none of the implementations it surveyed, so you cannot map its findings to pg_uuidv7 or any other named package without checking yourself. And it reports no measurement of real-world out-of-order emission frequency; the problem is demonstrated as a mechanism, not quantified as an incident rate.
My recommendation, conditional on those gaps: keep UUIDv7 where you want uniqueness and B-tree locality, but do not let sort order carry correctness. If keyset pagination or merge logic needs strict order, add application-owned monotonic state (a persisted counter or a database sequence) and audit your generator against the checklist above. If you need cross-service total order, stop looking at identifier formats entirely; you need a sequencing authority, because the physics of clock skew has already decided that question.
Frequently Asked Questions
Does the UUIDv7 specification guarantee that IDs are strictly monotonically increasing?
So no, there’s nothing in the UUIDv7 specification that would ensure the IDs are strictly monotonically increasing. Specific implementations may do better than others (with sub-millisecond timestamps, counters, well-synchronized clocks, etc), but we’ve known since the 1970s that this problem is not strictly solvable.
How can a UUIDv7 generator safely handle a process restart after a backwards clock jump?
To do this safely you’ll need to write the last used timestamp to persistent storage, such that you can read it again after a system reboot or a process restart.
Can UUIDv7 or ULID provide a cross-service total order without a sequencing authority?
no timestamp-based identifier format, UUIDv7, ULID, or anything else keyed to wall-clock time, can deliver a cross-service total order without a sequencing authority.

Join the discussion
Share a useful perspective or ask a question about this article.