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
- Variables
- func ActionTLA(a Action, epoch int) (string, error)
- func HarnessConfig(modelConfig string, steps int) string
- func HarnessModule(observed []State, steps []string) string
- func RunReplay(o ReplayOptions) error
- func RunWitnesses(ctx context.Context, source string, o ServerOptions, emit func(Finding)) error
- func WithStore(ctx context.Context, o ServerOptions, fn func(*Store)) error
- type Action
- type Bind
- type CannotRun
- type Cell
- type Datum
- type Event
- type Failure
- type Finding
- type Location
- type Phys
- type Placement
- type ReplayEvent
- type ReplayOptions
- type RowAt
- type Server
- type ServerOptions
- type State
- type Step
- type StepOptions
- type StepResult
- type Store
- type Trace
- type TraceStep
Constants ¶
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.
const HarnessName = "MemberReceiptReplay"
HarnessName is the module and configuration the harness is written as.
Variables ¶
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.
var MemberSuites = []string{"all", "member", "epoch", "small"}
MemberSuites are the suites of the member protocol check.
var TableModes = []string{"strict", "contracts", "witnesses", "controls", "all"}
TableModes are the modes of the table model check.
Functions ¶
func ActionTLA ¶
ActionTLA renders a step as the model action that performs it at the given epoch of the step's table.
func HarnessConfig ¶
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 ¶
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:
- 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.
- 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.
- Controls: a receipt with a revision gap and one with a wrong member delta must be refused by the checks above.
- 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 ¶
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.
Types ¶
type Action ¶
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.
type Bind ¶
type Bind struct{ Cell, Target Cell }
Bind is a cell bound to a target. JSON [cell, target].
func (Bind) MarshalJSON ¶
func (*Bind) UnmarshalJSON ¶
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.
type Cell ¶
Cell is a cell of a physical table, or one of the two sentinels. JSON [[table, epoch], row, column].
func (Cell) MarshalJSON ¶
func (*Cell) UnmarshalJSON ¶
type Datum ¶
Datum is a member of a cell with its score. JSON [cell, member, score].
func (Datum) MarshalJSON ¶
func (*Datum) UnmarshalJSON ¶
type Event ¶
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.
type Finding ¶
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 ¶
Location is where a member is in one physical table. JSON [[table, epoch], cell].
func (Location) MarshalJSON ¶
func (*Location) UnmarshalJSON ¶
type Phys ¶
Phys is one physical table: a table name at an epoch. JSON [table, epoch].
func (Phys) MarshalJSON ¶
func (*Phys) UnmarshalJSON ¶
type Placement ¶
Placement is the place of one member in every physical table. JSON [member, [location...]].
func (Placement) MarshalJSON ¶
func (*Placement) UnmarshalJSON ¶
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 ¶
RowAt is a row present in a physical table. JSON [[table, epoch], row].
func (RowAt) MarshalJSON ¶
func (*RowAt) UnmarshalJSON ¶
type Server ¶
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 ¶
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.
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.
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 ¶
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 ¶
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.
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.