Files
knox/internal/hlc/hlc.go
T
david d6d2a24ddc style: gofmt all packages (#4)
Applies gofmt to the 18 files that were already unformatted at HEAD (pre-existing debt — 122 insertions / 122 deletions, whitespace plus import-block reorderings only; `git diff -w` confirms no semantic changes).

Kept on its own branch so the functional change set (see the review-hardening PR) stays reviewable without formatting noise.

Verified: go build, go vet, go test ./... pass on this branch; a merge simulation with the functional branch produces a clean 3-way merge with all tests green.
Reviewed-on: #4
Co-authored-by: David Gwilliam <dhgwilliam@gmail.com>
Co-committed-by: David Gwilliam <dhgwilliam@gmail.com>
2026-09-17 09:05:57 +00:00

61 lines
1.9 KiB
Go

// 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)<<seqBits - 1
const wallShift = seqBits
// Clock is a single-writer HLC. It is safe for concurrent use.
type Clock struct {
mu sync.Mutex
wallMS int64 // last observed wall-clock millis
seq int64 // sequence within the current wallMillis bucket
}
func New() *Clock { return &Clock{} }
// 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 | c.seq
return v, time.UnixMilli(c.wallMS).UTC()
}
// WallTime extracts the wall-clock component embedded in a packed HLC value.
func WallTime(v int64) time.Time {
return time.UnixMilli(v >> wallShift).UTC()
}