tablemodel

package
v1.2.7 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Overview

Package tablemodel checks nova-table against its TLA+ models.

Three checks live here, each turning the table models into an executable check of the models:

  • The suites (steps.go): the table model's contract, witness and control configurations, and the member and epoch protocol's positive checks and mutation controls, each configuration run under one TLC budget and held to the result it declares.
  • The witnesses (witnesses.go): the findings of the table model replayed as concrete calls of a table.lua in a disposable Redis, each confirmed or refuted by what the store holds.
  • The replay (replay.go): controlled calls of a table.lua in a disposable Redis, every store state captured and every committed receipt checked against the states around it, the same receipts replayed into a second fresh store, and the captured states handed to TLC as a linear harness over EpochMemberTable that must match them step by step (and must refuse a corrupted observation).

What it runs: java for TLC and redis-server for the disposable store, each found on PATH or named by the caller, never downloaded. The store listens on a Unix socket in a private directory with TCP off, keeps nothing, and is killed when the check ends. Nothing here dials a fleet address or reads a credential.

Everything that can be checked without java or a store is a pure function over recorded data (the reading of TLC's output is pkg/tlc's; the rendering of a trace as a TLA+ module, the receipt-delta check and the planning of suites are here), and the unit tests hold them to recorded fixtures.

Index

Constants

View Source
const (
	VerdictPass                    = "pass"
	VerdictExpectedCounterexample  = "expected-counterexample"
	VerdictExpectedScopeControl    = "expected-scope-control"
	VerdictExpectedMutationFailure = "expected-mutation-failure"
	VerdictFail                    = "fail"
	VerdictTimeout                 = "timeout"
)

Verdicts of a step. A met expectation is named for what was expected.

View Source
const HarnessName = "MemberReceiptReplay"

HarnessName is the module and configuration the harness is written as.

Variables

View Source
var (
	External = Cell{Phys{"external", 0}, "external", "external"}
	NoPlace  = Cell{Phys{"none", 0}, "none", "none"}
)

External is the cell the bound set outside every table lives in, and NoPlace is where a member is when it has no place in a table.

View Source
var MemberSuites = []string{"all", "member", "epoch", "small"}

MemberSuites are the suites of the member protocol check.

View Source
var TableModes = []string{"strict", "contracts", "witnesses", "controls", "all"}

TableModes are the modes of the table model check.

Functions

func ActionTLA

func ActionTLA(a Action, epoch int) (string, error)

ActionTLA renders a step as the model action that performs it at the given epoch of the step's table.

func HarnessConfig

func HarnessConfig(modelConfig string, steps int) string

HarnessConfig derives the harness configuration from the model's own: the replay specification, as many steps as the trace has, and the invariant that the model matches the observation.

func HarnessModule

func HarnessModule(observed []State, steps []string) string

HarnessModule renders the linear harness. observed holds the initial state then the state after each step; steps holds each step's model action.

func RunReplay

func RunReplay(o ReplayOptions) error

RunReplay runs the whole execution replay under one budget:

  1. Capture: the trace's calls run against the table.lua in a disposable store; every state, refusals included, is snapshotted; every committed receipt is checked against the states on either side of its call.
  2. Replay: the retained receipts (command, arguments, options) are replayed into a second fresh store, which must pass through the same states and commit the same receipts.
  3. Controls: a receipt with a revision gap and one with a wrong member delta must be refused by the checks above.
  4. TLC: the captured states become a linear harness over EpochMemberTable that must pass, and the same harness with one corrupted observation must violate MatchesExecution.

The trace, the harness modules and the TLC logs are kept in Dir.

func RunWitnesses

func RunWitnesses(ctx context.Context, source string, o ServerOptions, emit func(Finding)) error

RunWitnesses replays the table model's findings against the table.lua at source, one after another, in one disposable store. Each finding is an exact sequence of calls whose replies and stored state are asserted; a reply that is not the one the finding says is a failure, so a source that no longer has the defect fails here and the finding is retired or the model updated.

func WithStore

func WithStore(ctx context.Context, o ServerOptions, fn func(*Store)) error

WithStore starts a disposable store, hands its connection to fn, and stops the store. A failed check inside fn is returned as an error.

Types

type Action

type Action struct {
	Verb  string
	Args  []string
	Actor string
}

Step is one controlled call of the trace: a verb of table.lua, or one of the two model-only steps (read_epoch, advance) that move the epoch the writers see.

func Actions

func Actions() []Action

Actions is the trace: source-API calls in the model's finite universe. Every store state they lead to, refused calls included, is checked by TLC.

type Bind

type Bind struct{ Cell, Target Cell }

Bind is a cell bound to a target. JSON [cell, target].

func (Bind) MarshalJSON

func (b Bind) MarshalJSON() ([]byte, error)

func (*Bind) UnmarshalJSON

func (b *Bind) UnmarshalJSON(data []byte) error

type CannotRun

type CannotRun struct{ Err error }

CannotRun is an error of the environment, not of the check: a program that is missing or does not start, an input that cannot be read, a directory that cannot be made. A check that ran and said NO is a *Failure.

func (*CannotRun) Error

func (c *CannotRun) Error() string

func (*CannotRun) Unwrap

func (c *CannotRun) Unwrap() error

type Cell

type Cell struct {
	P        Phys
	Row, Col string
}

Cell is a cell of a physical table, or one of the two sentinels. JSON [[table, epoch], row, column].

func (Cell) MarshalJSON

func (c Cell) MarshalJSON() ([]byte, error)

func (*Cell) UnmarshalJSON

func (c *Cell) UnmarshalJSON(data []byte) error

type Datum

type Datum struct {
	Cell   Cell
	Member string
	Score  int
}

Datum is a member of a cell with its score. JSON [cell, member, score].

func (Datum) MarshalJSON

func (d Datum) MarshalJSON() ([]byte, error)

func (*Datum) UnmarshalJSON

func (d *Datum) UnmarshalJSON(data []byte) error

type Event

type Event map[string]string

Event is a committed receipt: the fields of one entry of a table's change stream, and its id.

type Failure

type Failure struct{ Msg string }

A failed check inside this package is a panic carrying a *Failure, so the long lists of assertions in the witnesses and the replay read as the statements they are. Every exported entry point recovers it into an error; no panic leaves the package.

func (*Failure) Error

func (f *Failure) Error() string

type Finding

type Finding struct {
	Name   string
	Result string
	Text   string
}

Finding is one replayed witness or control. Result is "confirmed" when the table model's finding reproduces against the table.lua under test (a defect that source still has) and "pass" when a control holds.

type Location

type Location struct {
	P    Phys
	Cell Cell
}

Location is where a member is in one physical table. JSON [[table, epoch], cell].

func (Location) MarshalJSON

func (l Location) MarshalJSON() ([]byte, error)

func (*Location) UnmarshalJSON

func (l *Location) UnmarshalJSON(data []byte) error

type Phys

type Phys struct {
	Table string
	Epoch int
}

Phys is one physical table: a table name at an epoch. JSON [table, epoch].

func (Phys) MarshalJSON

func (p Phys) MarshalJSON() ([]byte, error)

func (*Phys) UnmarshalJSON

func (p *Phys) UnmarshalJSON(data []byte) error

type Placement

type Placement struct {
	Member    string
	Locations []Location
}

Placement is the place of one member in every physical table. JSON [member, [location...]].

func (Placement) MarshalJSON

func (p Placement) MarshalJSON() ([]byte, error)

func (*Placement) UnmarshalJSON

func (p *Placement) UnmarshalJSON(data []byte) error

type ReplayEvent

type ReplayEvent struct {
	Step    string   // capture, receipt-replay, mutation-controls, tlc-execution, tlc-mutated-observation
	Verdict string   // pass, or expected-mutation-failure
	Fields  []string // key=value details
}

ReplayEvent is one completed part of the replay.

type ReplayOptions

type ReplayOptions struct {
	Source string // the table.lua under test
	Jar    string // the TLC jar
	Java   string // the java program
	Models string // the directory of the TLA+ modules
	Dir    string // output directory: trace.json, the harness and the TLC logs
	Budget time.Duration

	Server ServerOptions
	Exec   tlc.Executor // tlc.Execute when nil
	OnStep func(ReplayEvent)
}

ReplayOptions is one execution replay.

type RowAt

type RowAt struct {
	P   Phys
	Row string
}

RowAt is a row present in a physical table. JSON [[table, epoch], row].

func (RowAt) MarshalJSON

func (r RowAt) MarshalJSON() ([]byte, error)

func (*RowAt) UnmarshalJSON

func (r *RowAt) UnmarshalJSON(data []byte) error

type Server

type Server struct {
	Dir    string
	Socket string
	// contains filtered or unexported fields
}

Server is a disposable redis-server: a Unix socket in a private directory, TCP off, nothing saved. Close kills it and removes the directory.

func StartServer

func StartServer(ctx context.Context, program, tmp string, startup time.Duration) (*Server, error)

StartServer runs the redis-server program in a new directory under tmp (the system temporary directory when empty) and connects to its socket. The server has this long to create the socket, and its output is kept in Dir/redis.log, which is what a caller reports when it does not come up.

func (*Server) Close

func (s *Server) Close()

Close stops the server (terminate, then kill after five seconds) and removes its directory.

func (*Server) Store

func (s *Server) Store(ctx context.Context) *Store

Store returns the server's connection; ctx bounds every command.

type ServerOptions

type ServerOptions struct {
	RedisServer string        // the redis-server program
	TmpDir      string        // where the store's directory goes; the system default when empty
	Startup     time.Duration // how long the store has to create its socket
}

ServerOptions names how the disposable store is started.

type State

type State struct {
	Live   []Phys         `json:"live"`
	Rows   []RowAt        `json:"rows"`
	Binds  []Bind         `json:"binds"`
	Data   []Datum        `json:"data"`
	Place  []Placement    `json:"place"`
	Active int            `json:"active"`
	Seen   map[string]int `json:"seen"`
}

State is everything the model observes of the store at one moment.

func (State) Clone

func (s State) Clone() State

Clone returns a copy that shares nothing with s.

type Step

type Step struct {
	Name     string // configuration base name: MCTableMachine
	Module   string // the module file: MCTableMachine.tla
	Workers  int    // TLC workers
	Kind     string // pass, invariant or action
	Property string // the violated name for invariant and action
	Met      string // the verdict a met expectation is called
}

Step is one configuration of a suite and the result it must reach.

func MemberSteps

func MemberSteps(suite string, workers int) ([]Step, error)

MemberSteps plans the member protocol check. The positive instances run with the given workers; every mutation control runs with one. Suite member and epoch select one module's cases; small drops the two large instances.

func TableSteps

func TableSteps(mode string) ([]Step, error)

TableSteps plans the table model check for a mode.

  • contracts: the model with its fixed point, expected to pass.
  • witnesses: the five findings, each expected to violate its invariant.
  • controls: the cross-table configuration, a scope control that is expected to violate OneTablePerMember (the model is per table by design).
  • all: the three above.
  • strict: the five findings expected to PASS. This is the desired contract, and it fails on the first finding the table code still has.

type StepOptions

type StepOptions struct {
	Jar    string        // the TLC jar
	Java   string        // the java program
	Models string        // the directory of the modules and configurations
	Dir    string        // output directory: logs and the private copy of the models
	Budget time.Duration // the whole run's limit
	Exec   tlc.Executor  // tlc.Execute when nil
	OnStep func(StepResult)
}

StepOptions is a run of steps under one budget.

type StepResult

type StepResult struct {
	Step    Step
	Verdict string
	OK      bool
	Code    int
	Seconds time.Duration
	Outcome tlc.Outcome
	Log     string
}

StepResult is what one step did.

func RunSteps

func RunSteps(steps []Step, o StepOptions) ([]StepResult, error)

RunSteps runs the steps in order and stops at the first that is not the result it declares. The models are copied to Dir/work first and TLC runs there, so the checkout gains no error-trace files. The last result is the failing one.

type Store

type Store struct {
	// contains filtered or unexported fields
}

Store is a connection to the disposable server that reads replies the way the checks want them: a bulk or status reply is a string, an integer is an int64, an array is a []any, and a nil reply is nil. An error reply, or a failure of the connection, is a failed check.

func (*Store) Cmd

func (r *Store) Cmd(args ...any) any

Cmd sends one command.

type Trace

type Trace struct {
	SourceSHA256 string            `json:"source_sha256"`
	ModelSHA256  map[string]string `json:"model_sha256"`
	Initial      State             `json:"initial"`
	Steps        []TraceStep       `json:"steps"`
}

Trace is trace.json: the source and model hashes it was taken from, the initial state and every step.

type TraceStep

type TraceStep struct {
	Verb    string   `json:"verb"`
	Args    []string `json:"args"`
	Actor   string   `json:"actor"`
	Refused *bool    `json:"refused"`
	Receipt Event    `json:"receipt"`
	Model   string   `json:"model"`
	State   State    `json:"state"`
}

TraceStep is one step of the retained trace: the call, whether it was refused, its receipt, the model action that performs it and the store state after it.

Jump to

Keyboard shortcuts

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