Documentation
¶
Overview ¶
Package clock is the adapter behind the clock port. It is three lines because that is the whole point: the untestable part of "what time is it" is confined to one place, and everything above it takes the answer as a parameter (arc42 §8.13).
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CryptoRandom ¶
type CryptoRandom struct{}
CryptoRandom answers the RandomSource port from the operating system's entropy.
crypto/rand rather than a seeded generator, and the standard library's rejection sampling rather than a modulo of our own: security.md §8 forbids home-grown randomness, and an assignment that could be predicted from the previous one would make "who gets this task" a thing an insider can steer.
func (CryptoRandom) Bytes ¶
func (CryptoRandom) Bytes(n int) ([]byte, error)
Bytes draws a credential's worth of entropy. crypto/rand again, and the error is returned rather than panicked: a token that could not be minted is a request that fails, which is a better answer than a process that stops serving everybody else's.
func (CryptoRandom) IntN ¶
func (CryptoRandom) IntN(n int) int
IntN returns a uniform value in [0, n). n must be positive - the port's contract, and crypto/rand.Int refuses anything else.
type HybridClock ¶
type HybridClock struct {
// contains filtered or unexported fields
}
HybridClock stamps server-side changes with a hybrid logical clock (offline-sync.md §4.1).
One instance per process, because the state it keeps is what makes two changes in the same millisecond orderable: the counter advances when the wall clock has not. Two instances in one process would hand out the same reading twice and the two changes would then be ordered differently on different clients - the one failure a merge rule must not have.
func NewHybridClock ¶
func NewHybridClock(clock port.Clock, device string) (*HybridClock, error)
NewHybridClock refuses a device identifier that cannot be read back out of the stored form, so that Next never has to fail.
func (*HybridClock) Next ¶
func (h *HybridClock) Next() shared.HLC
Next returns the reading for the change being written now. Safe for concurrent use: two requests stamping at once get two readings, and the second one sorts after the first.
type System ¶
type System struct{}
System reads the machine's clock and answers in UTC. Always UTC: a comparison against a stored timestamptz in a local zone is a bug that only appears twice a year.
type UUIDv7 ¶
type UUIDv7 struct {
// Clock is the time source. A port rather than time.Now, so a test can assert on the ordering
// of the identifiers it gets.
Clock port.Clock
}
UUIDv7 mints identifiers in the application rather than in the database (arc42 §8.13).
Time-ordered, so an index stays dense and rows written together sit together on disk; and known before the insert, which is what lets one transaction write a row, an event that references it and an audit entry about it without a round trip in between.
Version 7 rather than 4 for the first reason and rather than a sequence for the second: a sequence is only unique inside one database, and identifiers travel between installations through exports, backups and offline clients that mint their own (offline-sync.md §9).
func (UUIDv7) NewID ¶
NewID returns a UUIDv7 in the canonical lower-case form (RFC 9562 §5.7): 48 bits of milliseconds since the epoch, the version, 12 random bits, the variant, and 62 more random bits.
The randomness comes from crypto/rand rather than math/rand. An identifier is a capability in several places - a media reference, a feed token's subject, an idempotency key's neighbour - and a predictable one is a guessable one (security.md §8).