Time, Ordering and Identifiers
Choose clock and identity semantics, trace causal ordering and preserve identifier uniqueness across rollback and restart.
Time, Ordering and Identifiers
A timestamp, an identifier and an ordering decision answer different questions. Time may express when a user wanted work to happen; an ID names one logical object; a protocol decides which conflicting operation wins. A time-prefixed ID can improve storage locality without proving that its numeric order matches real-time completion or causality across machines.
Choose the clock for the question
This original exercise schedules exports and records their results across regions. Users choose calendar times, workers need elapsed deadlines, and an authoritative store decides the winning result generation. Each boundary needs a different contract.
| Question | Suitable evidence | Unsafe substitution |
|---|---|---|
| When should a daily 09:00 export occur? | Calendar expression, named timezone and gap/fold policy | Add 24 elapsed hours forever |
| Has this process spent 500 ms waiting? | Local monotonic elapsed time | Subtract wall times after a clock correction |
| Is this lease still valid at its authority? | Authority's deadline/generation protocol | Compare another machine's monotonic reading |
| Did event A causally precede B? | Message/history relationships or a suitable logical protocol | Sort independently sampled wall timestamps |
| Which competing result may commit? | Atomic state/version or consensus-backed ownership | Choose the largest client timestamp |
Wall clocks can be adjusted. Monotonic readings have a local origin and contract; do not serialize them as globally comparable timestamps. Suspend/restart behavior is platform-specific. A request budget must account for actual elapsed time and propagation assumptions. Sending the original full timeout to every dependency repeatedly spends the same time budget.
Work a clock rollback
An export attempt starts at wall time 10,000 ms and local monotonic time 200 ms. A wall-clock correction occurs; at completion the wall clock reads 9,900 ms and the monotonic clock reads 700 ms. Wall subtraction produces −100 ms; elapsed work was 500 ms in this model. A deadline of 600 elapsed ms has 100 ms left, regardless of the wall correction.
That local result says nothing about how far another worker's wall clock is ahead. For absolute schedules use the selected authoritative wall-time policy; for ownership use enforceable generations and state. Neither an NTP daemon nor a monotonic timer retracts an external effect sent by an obsolete worker.
Read the diagram
Worker issues an ID at clock millisecond 500 → Wall time moves backward to 499 → Generator refuses issuance below its saved watermark → New owner restores the allocation watermark → Issuance resumes at a unique eligible position
Establish causality with a message history
For a scalar Lamport-style clock, increment on a local/send event; on receiving a message with timestamp M, assign max(local, M) + 1. Original trace: A sends at logical time 1, B receives at 2, B sends at 3 and A receives at 4. The send event precedes its receive event in this history. Two independent processes can both emit 1; a (logical_time, process_id) tie-break produces a deterministic total order consistent with this clock discipline, but does not discover a causal connection between those independent events.
The implication is one-way: causal precedence implies increasing logical time under the stated algorithm, while a smaller scalar value does not prove causal precedence. Vector clocks can represent more causal information, at state/metadata cost. Hybrid logical clocks combine physical-time information and logical advancement under their protocol. Neither is a generic substitute for a database's supported transaction/commit ordering or a consensus decision.
For a chat conversation, a server sequence can define one authoritative conversation order. It does not automatically order all chats, nor prove that every device displayed the event. Keep stream position, application acknowledgement and user-visible read state separate.
Select an identifier from its required properties
| Option | Useful property | Cost or limit |
|---|---|---|
| Database sequence | Simple uniqueness within its authoritative allocation contract | Coordination/availability scope; gaps are normal; allocation is not commit order |
| Random UUIDv4 | Decentralized identity with collision probability under good randomness | Larger keys and weaker natural insertion locality; no event order |
| UUIDv7 | Standard time-prefixed layout helpful for time-oriented sorting/locality | Clock and implementation behavior matter; not a global commit-order guarantee |
| Time/worker/counter layout | Compact sortable values under a controlled allocation protocol | Clock rollback, worker reuse, counter overflow and recovery must be enforced |
Use a well-tested standards-compliant library for actual UUIDs. RFC 9562 defines UUID versions and implementation considerations. A UUIDv4 has 122 random bits after fixed version/variant fields; with independent uniform draws, the birthday approximation for a billion IDs is about n(n−1)/2^123 ≈ 9.4×10^-20. This is a probability under assumptions, not a proof that broken entropy, copied generator state or software bugs cannot collide. Treat uniqueness constraints and collision handling as part of the design where appropriate. IDs do not grant access and should not be used as bearer secrets merely because they are hard to guess.
UUIDv7 uses a millisecond time prefix; its remaining fields and optional monotonic generation methods must follow the standard. Do not invent one custom layout and label it UUIDv7. Equal-millisecond ordering and clock rollback behavior depend on the implementation's supported contract.
Make a custom allocation contract explicit
For an illustrative 64-bit scheme, reserve one sign bit, 41 timestamp bits relative to an epoch, 10 worker bits and 12 counter bits. This permits 1,024 worker codes and 4,096 positions per millisecond per worker. Those are field capacities, not measured throughput. The timestamp range is about 69.7 years, so epoch exhaustion is an operational concern too.
require trusted current ownership of worker code W
load persisted last_timestamp and sequence for W
if clock_ms < last_timestamp: refuse/wait under bounded policy
if same millisecond: increment sequence only within its field capacity
if later millisecond: reset sequence under the allocation protocol
persist/reserve the issued position before acknowledging it
encode(timestamp, W, sequence)Persisting every ID may defeat the desired throughput. An implementation can reserve disjoint ranges ahead of time, with safe recovery that never reissues a possibly used range. That creates gaps after a crash, which is usually better than duplicate identity. Reusing a worker code while an old owner still issues IDs also requires fencing or disjoint generation/range rules; worker names in configuration are insufficient.
If a same-tick counter is full, wait for an eligible later tick or reject. Wrapping to zero creates duplicates. If a process restarts, loading just the worker code while forgetting its watermark/sequence can do the same. The repository model scales the field widths down to make exhaustion easy to test; it is not a production Snowflake or UUID implementation.
Separate ID order from commit order
Transaction A allocates ID 100, pauses; B allocates 101 and commits; A commits later. Ascending IDs now disagree with completion order. Across workers, a fast wall clock can also issue a larger time prefix before another operation occurs. If the product needs a trustworthy auction-close winner, use the auction's authoritative admission/close decision, not the lexical maximum of client-generated IDs.
For pagination, (created_at, unique_id) can give deterministic tie-breaking, but concurrent inserts and edits still need a defined snapshot/keyset policy. An identifier cannot recover a missing history segment. For replay, preserve operation IDs rather than minting a new ID for each attempt.
Handle calendars without inventing elapsed guarantees
Store the original named timezone and a rule for nonexistent/repeated local times. Materialize eligible UTC instants into versioned occurrence identities. A UTC instant is unambiguous once chosen; it does not explain whether a repeated local 01:30 should execute once or twice. Timezone database changes and schedule edits need an effective-time/version policy.
The Job Scheduler deep dive works concrete gap/fold fixtures and misfire recovery. Do not interpret “daily” as always 86,400 seconds, or a due timestamp as proof that execution starts immediately under overload.
Check your understanding
Question: Wall time decreased by 100 ms during work, while local monotonic time advanced 500 ms. Was the operation's duration negative?
Show answer and explanation
Answer: No. Under this local elapsed-time model it took 500 ms. Investigate clock/correlation issues instead of treating a negative wall subtraction as useful latency evidence.
Question: A receives B's logical timestamp 7 while A's local clock is 4. What receive timestamp does the stated rule assign, and does scalar order alone prove causality for unrelated events?
Show answer and explanation
Answer: It assigns 8. A send/receive edge supplies causal evidence, but arbitrary scalar ordering between independent events does not establish such an edge.
Question: A worker's counter overflows within the same millisecond. May it wrap to zero because its worker code is unique?
Show answer and explanation
Answer: No. The timestamp/worker/counter tuple would repeat. Wait, reject or use a documented safe allocation strategy; preserve uniqueness across restarts and ownership changes too.
Transfer exercise and scoring
For Job Scheduler, Online Auction and WhatsApp, name the time semantics, identity scope, authoritative ordering decision, restart/rollback policy and replay identity. Score one point per justified choice. Give a counterexample in which sorting timestamps or IDs chooses the wrong business result.
Prerequisites: Consensus, Leader Election and Fencing and Idempotency and Delivery Semantics.
Primary references and scope
- Lamport: Time, Clocks and the Ordering of Events for the causal-order/logical-clock distinction.
- RFC 9562 for UUID formats and generation considerations.
The clock readings, custom bit layout, message trace and allocation policy above are original exercises. Model tests do not synchronize real clocks, implement a distributed identifier service or prove a production ordering contract.