Gauge

@Serializable
class Gauge : Quilted<Gauge>

A gauge: the current level of something — temperature, queue depth, players online, memory in use. Where a counter answers "how many so far?", a gauge answers "what is it right now?" — the reading that a Sum metric cannot express, because levels go up and down and only the latest reading matters.

As a CRDT. The mergeable form of "latest reading" is last-writer-wins: the state is a single observation tagged (timestamp, replicaId), and piece keeps the larger tag — literally the LWWRegister join, which this type wraps. Ties on timestamp break lexicographically on replicaId, so the merge is deterministic regardless of arrival order; the lattice laws (idempotent, commutative, associative) are inherited from LWWRegister. Many peers can observe independently and merge in any order, with any duplication, and converge on the newest observation.

Time is a dependency. observe takes the timestamp as a parameter — this type never reads a wall clock. Use whatever monotonic source the rest of your pipeline uses, and keep (replica, timestamp) pairs unique per write (the LWWRegister.set tag-uniqueness contract). As with any LWW type, clock skew between peers silently favours the faster clock; pair with a hybrid logical clock above this layer if that matters.

Mutator shape. observe returns a full new state — for an LWW type the whole state is the minimal delta (one tagged cell). Apply locally with gauge = gauge.piece(gauge.observe(replica, ts, v)) so a belated older-timestamp observation can never regress a newer one.

OTel interop. The state is structurally an OTLP NumberDataPoint under a Gauge metric: value plus timestamp. The OTLP mapping itself lives with the metrics exporter, not in this module.

Samples

val phone = ReplicaId("phone")
val laptop = ReplicaId("laptop")

// Each device observes the players-online level at its own time.
var onPhone = Gauge.empty()
var onLaptop = Gauge.empty()
onPhone = onPhone.piece(onPhone.observe(phone, timestamp = 100L, value = 4.0))
onLaptop = onLaptop.piece(onLaptop.observe(laptop, timestamp = 250L, value = 7.0))

// Merge: the observation with the larger (timestamp, replicaId) tag wins.
val merged = onPhone.piece(onLaptop)
check(merged.value == 7.0)
check(merged.timestamp == 250L)

// Commutative and idempotent: any merge order, any duplication, same answer.
check(onLaptop.piece(onPhone) == merged)
check(merged.piece(onPhone) == merged)

Types

Link copied to clipboard
object Companion

Properties

Link copied to clipboard

The winning observation's timestamp, or null if nothing has been observed yet.

Link copied to clipboard

The latest observed value, or null if nothing has been observed yet.

Functions

Link copied to clipboard
open fun causalDots(): Set<Dot>

The causal Dots this state has delivered — (author, author-seq) per op.

Link copied to clipboard

The per-author high-water of dots this state delivered and has since compacted away without retaining their identities.

Link copied to clipboard
open operator override fun equals(other: Any?): Boolean
Link copied to clipboard
open override fun hashCode(): Int
Link copied to clipboard
fun observe(replica: ReplicaId, timestamp: Long, value: Double): Gauge

Record that replica observed value at timestamp. Returns a new state carrying just this tagged observation; absorb it with piece (gauge = gauge.piece(gauge.observe(replica, ts, v))) so an older-timestamp observation never overwrites a newer one.

Link copied to clipboard
open override fun piece(other: Gauge): Gauge

The join: the observation with the larger (timestamp, replicaId) tag wins.

Link copied to clipboard
open override fun toString(): String