timefault

package
v0.0.0-...-e43023e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 27, 2026 License: Apache-2.0 Imports: 3 Imported by: 0

Documentation

Overview

Package timefault provides deterministic time-fault injectors and a clock-skew detector for testing distributed-clock robustness in HelixCluster.

Model

A shared base clock advances in discrete ticks (seconds). Each node derives its own logical clock from the base tick through a per-node Fault. The base clock represents the host / quorum reference time and is never mutated by a node-level fault.

Three fault injectors are provided:

  • SkewFault: offsets a node's clock by a fixed delta so it reads base+delta.
  • FreezeFault: stops a node's clock at the instant it was frozen; the node falls progressively behind as the base advances.
  • MonotonicViolationFault: makes a node's clock jump backward exactly once at a given base instant, violating monotonicity, then resumes tracking the base from the (lower) jumped value.

A Detector compares each node's reported time against the base reference and flags a node INCONSISTENT when |reported-base| exceeds a tolerance. It also detects monotonicity violations distinctly from skew by observing a backward step in a node's successive reported values. A lease/heartbeat decision uses the detector to deny leadership to a clock-faulty node (split-brain avoidance). Resetting a node's fault re-syncs it to the base.

The package is pure Go, deterministic and self-contained: time is modeled as int64 seconds driven by explicit base ticks, never by the wall clock.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Cluster

type Cluster struct {
	Detector *Detector
	// contains filtered or unexported fields
}

Cluster is an ordered collection of nodes sharing a base clock and detector.

func NewCluster

func NewCluster(tolSeconds int64, ids ...string) *Cluster

NewCluster builds a cluster from node IDs with the given tolerance.

func (*Cluster) EvaluateAll

func (c *Cluster) EvaluateAll(base int64) []Verdict

EvaluateAll evaluates every node at the base tick, returning verdicts keyed by node ID in sorted order.

func (*Cluster) IDs

func (c *Cluster) IDs() []string

IDs returns the sorted node IDs.

func (*Cluster) Inconsistent

func (c *Cluster) Inconsistent(base int64) []string

Inconsistent returns the sorted IDs of nodes the detector flags inconsistent at the base tick.

func (*Cluster) Inject

func (c *Cluster) Inject(id string, f Fault) error

Inject installs a fault on the named node.

func (*Cluster) Node

func (c *Cluster) Node(id string) *Node

Node returns the node by ID (nil if absent).

func (*Cluster) Reset

func (c *Cluster) Reset(id string) error

Reset clears the fault on the named node.

type Detector

type Detector struct {
	Tol Tolerance
}

Detector evaluates node reports against the base reference.

func NewDetector

func NewDetector(tolSeconds int64) *Detector

NewDetector builds a Detector with the given tolerance in seconds.

func (*Detector) EvaluateAt

func (d *Detector) EvaluateAt(n *Node, base int64) Verdict

EvaluateAt reads the node at base and judges it against the base reference.

A node is INCONSISTENT when its absolute skew exceeds the tolerance OR when a backward step (monotonicity violation) is observed relative to its previous read. The monotonicity check fires only on a strict backward step between two reads taken at non-decreasing base values, so it distinguishes a backward jump from a steady offset (skew).

func (*Detector) GrantLease

func (d *Detector) GrantLease(n *Node, base int64) LeaseDecision

GrantLease evaluates the node and grants leadership only if the detector finds it consistent. A skewed, frozen-behind or monotonicity-violating node is denied to avoid split-brain.

type Fault

type Fault interface {
	Kind() FaultKind
	// ReportedTime maps a base time (seconds) to the node's reported time.
	ReportedTime(base int64) int64
}

Fault is the per-node clock transform applied to a base tick (seconds). Given the current base time, ReportedTime returns the time the node would report. Implementations must be deterministic and pure.

type FaultKind

type FaultKind int

FaultKind enumerates the supported time faults.

const (
	// FaultNone means the node tracks the base clock exactly.
	FaultNone FaultKind = iota
	// FaultSkew offsets the node clock by a fixed delta.
	FaultSkew
	// FaultFreeze stops the node clock at the freeze instant.
	FaultFreeze
	// FaultMonotonicViolation makes the node jump backward once.
	FaultMonotonicViolation
)

func (FaultKind) String

func (k FaultKind) String() string

type FreezeFault

type FreezeFault struct {
	At int64
}

FreezeFault stops the node clock at At. For base < At the node still tracks the base (the freeze has not occurred yet); from At onward the node is stuck at At and falls progressively behind as the base advances.

func (FreezeFault) Kind

func (FreezeFault) Kind() FaultKind

Kind reports the fault kind.

func (FreezeFault) ReportedTime

func (f FreezeFault) ReportedTime(base int64) int64

ReportedTime returns min(base, At): frozen at At once the base passes it.

type LeaseDecision

type LeaseDecision struct {
	NodeID  string
	Granted bool
	Reason  string
}

LeaseDecision records whether a node may be granted leadership at a base tick.

type MonotonicViolationFault

type MonotonicViolationFault struct {
	At   int64
	Jump int64 // positive magnitude of the backward jump
}

MonotonicViolationFault makes the node jump backward by Jump seconds once the base reaches or passes At. Before At the node tracks the base; from At onward it reports base-Jump, so the reported value steps backward across the At boundary (a monotonicity violation) and then advances normally from there.

func (MonotonicViolationFault) Kind

Kind reports the fault kind.

func (MonotonicViolationFault) ReportedTime

func (f MonotonicViolationFault) ReportedTime(base int64) int64

ReportedTime returns base before At and base-Jump at/after At.

type Node

type Node struct {
	ID string
	// contains filtered or unexported fields
}

Node is a cluster member with a per-node fault transform and a record of the last reported time (used to detect backward steps / monotonicity violations).

func NewNode

func NewNode(id string) *Node

NewNode creates a healthy node that tracks the base clock.

func (*Node) FaultKind

func (n *Node) FaultKind() FaultKind

FaultKind returns the node's current fault kind.

func (*Node) Inject

func (n *Node) Inject(f Fault)

Inject installs a fault on the node and clears any monotonicity history so a freshly injected fault is evaluated from a clean state.

func (*Node) Read

func (n *Node) Read(base int64) int64

Read returns the node's reported time at the given base tick and records it for monotonicity tracking. Reads must be issued with non-decreasing base values to model a forward-advancing base clock.

func (*Node) Reset

func (n *Node) Reset()

Reset clears the node's fault (re-sync to base) and its history.

type SkewFault

type SkewFault struct {
	Delta int64
}

SkewFault offsets the node clock by Delta seconds (negative = behind).

func (SkewFault) Kind

func (SkewFault) Kind() FaultKind

Kind reports the fault kind.

func (SkewFault) ReportedTime

func (f SkewFault) ReportedTime(base int64) int64

ReportedTime returns base+Delta.

type Tolerance

type Tolerance int64

Tolerance bounds the acceptable absolute skew (seconds) before a node is flagged INCONSISTENT.

type Verdict

type Verdict struct {
	NodeID       string
	Base         int64
	Reported     int64
	Skew         int64 // reported - base
	Consistent   bool
	Monotonicity bool // true => a backward step was observed (violation)
	Kind         FaultKind
}

Verdict captures the detector's evaluation of one node at one base tick.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL