// Package hlc implements a Hybrid Logical Clock for ordering observations. // // A node's HLC is monotonic even when wall clocks jump (NTP correction, suspend). // Values are packed into an int64: high bits = wall-clock milliseconds, low bits // = per-millisecond sequence. Lexicographic comparison of the packed value is a // causal order (states: causally-related events have distinct values; concurrent // events never collide because the sequence bumps on any wall-clock stall). package hlc import ( "sync" "time" ) // seqBits is the number of low bits reserved for the per-millisecond sequence, // giving 2^22 ≈ 4.2M slots per ms — far beyond ingest rates. const seqBits = 22 const seqMask = int64(1)<> wallShift seq := v & seqMask if wall > c.wallMS || (wall == c.wallMS && seq > c.seq) { c.wallMS = wall c.seq = seq } } // Now returns the next monotonic HLC value and the wall-clock time embedded in // it. The returned time is the HLC's wall component — never ahead of the local // clock beyond the current call and never rewinding across calls. func (c *Clock) Now() (int64, time.Time) { c.mu.Lock() defer c.mu.Unlock() w := time.Now().UnixMilli() if w > c.wallMS { c.wallMS = w c.seq = 0 } else { // Wall clock stalled or went backwards (NTP): keep wallMS but bump seq // so the value is still strictly increasing. if c.seq >= seqMask { // Extremely unlikely (4.2M events in one ms); jump the wall lazily. c.wallMS++ c.seq = 0 } else { c.seq++ } } v := c.wallMS<> wallShift).UTC() }