Documentation
¶
Overview ¶
Package flowtest provides the workload abstraction for AutoFlow and autocore testing: a Unit describes one workflow test (how to start it, how to drive it from the outside, and what outcome to expect), a Source produces Units, and the loadtest driver executes them.
Index ¶
- Constants
- type AutoFlowStart
- type AutocoreStart
- type AutocoreTarget
- type CancelOp
- type CompositeTarget
- type DriveOp
- type Driver
- type DriverConfig
- type Expectation
- type FlowcoreEngine
- type FlowcoreTarget
- type Observer
- type ObserverConfig
- type RateModel
- type SendOp
- type SessionHandle
- type SessionLedger
- type Source
- type StartSpec
- type Target
- type TerminalState
- type Unit
- type UnitBuilder
- func (b *UnitBuilder) AutoFlow(script []byte, channelArgs ...string) *UnitBuilder
- func (b *UnitBuilder) Autocore(workflow string, input proto.Message) *UnitBuilder
- func (b *UnitBuilder) Build() (*Unit, error)
- func (b *UnitBuilder) Cancel(at time.Duration) *UnitBuilder
- func (b *UnitBuilder) Description(d string) *UnitBuilder
- func (b *UnitBuilder) ExpectTerminal(states ...TerminalState) *UnitBuilder
- func (b *UnitBuilder) Send(channel string, payload proto.Message, at time.Duration) *UnitBuilder
- type WeightedSource
- type WorkflowInfoClient
Constants ¶
const ( // DefaultObserveInterval is the default pause between observer ticks. DefaultObserveInterval = 2 * time.Second // DefaultObserveBatchSize is the default cap on sessions resolved per tick. DefaultObserveBatchSize = 4096 // DefaultObserveMinAge is the default lower session age bound: younger // sessions likely still run their drive scripts, so polling them wastes // round trips on workflows that cannot be terminal yet. DefaultObserveMinAge = 5 * time.Second // DefaultObserveMaxAge is the default upper session age bound: sessions // older than this stop being polled, and deciding their fate is the // verifier's business. DefaultObserveMaxAge = time.Hour )
const DefaultMaxInFlightSessions = 8192
DefaultMaxInFlightSessions comfortably covers normal session lifetimes at northstar arrival rates while bounding goroutine growth when the target cannot keep up.
const DefaultRateSeed uint64 = 0x2545F4914F6CDD1D
DefaultRateSeed seeds the value-noise random walk. It is a fixed constant keyed together with wall-clock time, so every replica computes the same macro rate curve at the same instant -- keeping the fleet in phase so swings are visible in aggregate -- while the level sequence never repeats.
const DefaultSessionDeadline = 15 * time.Minute
DefaultSessionDeadline gives a workflow ample time to reach a terminal state after its drive script ends before the verifier treats it as overdue.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AutoFlowStart ¶
type AutoFlowStart struct {
Script []byte
// ChannelArgs names the channel-typed arguments passed with the start
// request. Only these channels are externally sendable: channel tokens
// are minted for channels the caller passes in, never for channels the
// flow creates itself.
ChannelArgs []string
}
AutoFlowStart starts a workflow through the AutoFlow engine.
type AutocoreStart ¶
AutocoreStart starts a registered autocore workflow directly.
type AutocoreTarget ¶
type AutocoreTarget struct {
Client autocore.Client
ExternalNamespaceID autocore.ExternalNamespaceID
}
AutocoreTarget drives autocore Units against an autocore client.
func (*AutocoreTarget) Start ¶
func (t *AutocoreTarget) Start(ctx context.Context, idempotencyKey string, start StartSpec) (SessionHandle, error)
type CompositeTarget ¶
CompositeTarget dispatches each Unit to the arm that supports its start kind. A nil arm rejects the Units it would serve.
func (*CompositeTarget) Start ¶
func (t *CompositeTarget) Start(ctx context.Context, idempotencyKey string, start StartSpec) (SessionHandle, error)
type DriveOp ¶
DriveOp is one timed external operation against the running workflow, as an external client (e.g. Rails) would perform it. Exactly one of Send and Cancel is set. At is the offset from the start acknowledgment.
type Driver ¶
type Driver struct {
// contains filtered or unexported fields
}
Driver executes Units: it paces session starts per the rate model, starts each Unit's workflow, performs its drive script at the configured offsets, and plays the role of the external client (e.g. Rails).
func NewDriver ¶
func NewDriver(cfg *DriverConfig) (*Driver, error)
type DriverConfig ¶
type DriverConfig struct {
Log *slog.Logger
Target Target
Sources []WeightedSource
Rate RateModel
// RNG drives source picking and unit generation. Seeded per replica so
// replicas produce independent unit streams.
RNG *rand.Rand
// MaxInFlightSessions bounds concurrently running sessions. New arrivals
// are skipped at the bound: the driver stays open-loop, so a saturated
// target surfaces as skipped sessions rather than as silently stretched
// arrival gaps. 0 means DefaultMaxInFlightSessions.
MaxInFlightSessions int
// InjectionDuration bounds how long Run produces new sessions. Zero means
// unbounded. In-flight sessions finish their drive scripts before Run
// returns; external cancellation aborts them.
InjectionDuration time.Duration
// Ledger records session intents and op outcomes. Nil disables recording.
Ledger SessionLedger
// Meter instruments the driver. Required when Ledger is set so skipped
// sessions stay visible; a noop meter is used when nil otherwise.
Meter otelmetric.Meter
// SessionDeadline is the verification deadline margin recorded per session:
// the session's ledger deadline is its start time plus its last drive
// offset plus this. 0 means DefaultSessionDeadline.
SessionDeadline time.Duration
}
DriverConfig configures a Driver.
type Expectation ¶
type Expectation struct {
// TerminalStates are the allowed outcomes. A set rather than a single
// state, to absorb legitimate races such as cancel vs. completion.
TerminalStates []TerminalState
}
Expectation is the declarative verdict spec of a Unit.
type FlowcoreEngine ¶
type FlowcoreEngine interface {
RunWorkflow(ctx context.Context, namespaceID int64, idempotencyKey string, opts *flowcore.RunWorkflowOptions) (*flowcore.RunWorkflowResult, error)
CancelWorkflow(ctx context.Context, workflowKey string) error
SendToWorkflowChannel(ctx context.Context, idempotencyKey, channelToken string, value *pkg_autoflow.Value) error
}
FlowcoreEngine is the subset of *flowcore.Engine the target drives.
type FlowcoreTarget ¶
type FlowcoreTarget struct {
Engine FlowcoreEngine
NamespaceID int64
}
FlowcoreTarget drives AutoFlow Units against a flowcore engine. Each of the Unit's channel args is passed as a channel-valued kwarg of the same name, and sends are addressed with the per-session channel tokens the start returns.
func (*FlowcoreTarget) Start ¶
func (t *FlowcoreTarget) Start(ctx context.Context, idempotencyKey string, start StartSpec) (SessionHandle, error)
type Observer ¶
type Observer struct {
// contains filtered or unexported fields
}
Observer resolves the terminal states of recorded sessions: it polls the ledger's unobserved window, asks the workflow engine for each workflow's state, and writes the observations back in one batch per tick. Running out of process from the driver's session goroutines decouples workflow lifetime from drive script lifetime.
func NewObserver ¶
func NewObserver(cfg *ObserverConfig) (*Observer, error)
type ObserverConfig ¶
type ObserverConfig struct {
Log *slog.Logger
Store *ledger.Store
Client WorkflowInfoClient
// Meter instruments the observer. Optional; nil means no metrics.
Meter otelmetric.Meter
// Interval is the pause between ticks. 0 means DefaultObserveInterval.
Interval time.Duration
// BatchSize caps sessions resolved per tick. 0 means DefaultObserveBatchSize.
BatchSize int
// MinAge excludes sessions younger than this. 0 means DefaultObserveMinAge.
MinAge time.Duration
// MaxAge excludes sessions older than this. 0 means DefaultObserveMaxAge.
MaxAge time.Duration
}
ObserverConfig configures an Observer.
type RateModel ¶
RateModel yields the target submission rate (units/sec) at a wall-clock instant. With Max <= Min the rate is constant. Otherwise it is a bounded random walk realized as value noise: an independent random level in [Min, Max] is drawn for each Period segment and smoothly interpolated (smoothstep) between consecutive segments. Keyed by wall-clock time + a shared seed, so the curve is identical across replicas (the fleet stays in phase), organic, non-repeating, and bounded -- no fixed sine rhythm.
type SessionHandle ¶
type SessionHandle interface {
// WorkflowKey identifies the started workflow in its engine's format.
WorkflowKey() string
Send(ctx context.Context, idempotencyKey string, op *SendOp) error
Cancel(ctx context.Context) error
}
SessionHandle drives one started workflow. Sends and cancellation are addressed the way the workflow's engine addresses them, which is what the handle encapsulates.
type SessionLedger ¶
type SessionLedger interface {
SessionStarted(ctx context.Context, rec *ledger.SessionRecord) error
WorkflowKeyKnown(sessionID uuid.UUID, key string)
OpDone(sessionID uuid.UUID, opIndex int32, err error)
}
SessionLedger is the subset of *ledger.Writer the Driver records sessions to.
type Source ¶
type Source interface {
Name() string
// NextUnit returns the next Unit. All randomness must come from rng so a
// run is reproducible from its seed.
NextUnit(rng *rand.Rand) (*Unit, error)
}
Source produces Units. Implementations are the generation strategies: pre-defined corpora pick units, generative sources synthesize them.
type StartSpec ¶
type StartSpec struct {
AutoFlow *AutoFlowStart
Autocore *AutocoreStart
}
StartSpec describes how a Unit's workflow starts. Exactly one arm is set.
type Target ¶
type Target interface {
Start(ctx context.Context, idempotencyKey string, start StartSpec) (SessionHandle, error)
}
Target starts a Unit's workflow and returns the handle used to drive it. A target that does not support the Unit's start kind returns an error.
type TerminalState ¶
type TerminalState int
TerminalState is a workflow outcome a Unit's Expectation can allow.
const ( TerminalCompleted TerminalState = iota + 1 TerminalFailed TerminalCanceled TerminalTimedOut TerminalSystemFailed )
type Unit ¶
type Unit struct {
Name string
Description string
Start StartSpec
// Drive holds the timed external operations performed against the
// running workflow, ordered by offset.
Drive []DriveOp
Expect Expectation
}
Unit is a complete, self-contained test definition. It is pure data with all randomness already spent: a generated Unit carries chosen values and offsets, not distributions. Construct Units with NewUnit; the driver executes each Unit as one session.
type UnitBuilder ¶
type UnitBuilder struct {
// contains filtered or unexported fields
}
UnitBuilder assembles a Unit. Build validates structural rules that would otherwise only fail at execution time, e.g. sending on a channel the driver holds no token for.
func NewUnit ¶
func NewUnit(name string) *UnitBuilder
NewUnit starts building a Unit with the given name.
func (*UnitBuilder) AutoFlow ¶
func (b *UnitBuilder) AutoFlow(script []byte, channelArgs ...string) *UnitBuilder
AutoFlow makes the Unit start an AutoFlow workflow from the given Starlark script. channelArgs declares the channel-typed arguments passed with the start request; only these channels can be targeted by Send.
func (*UnitBuilder) Autocore ¶
func (b *UnitBuilder) Autocore(workflow string, input proto.Message) *UnitBuilder
Autocore makes the Unit start the given registered autocore workflow.
func (*UnitBuilder) Build ¶
func (b *UnitBuilder) Build() (*Unit, error)
Build validates the Unit and returns it. Drive ops are ordered by offset, preserving insertion order for equal offsets.
func (*UnitBuilder) Cancel ¶
func (b *UnitBuilder) Cancel(at time.Duration) *UnitBuilder
Cancel schedules a cancellation request at the given offset from the start acknowledgment. At most one cancel per Unit.
func (*UnitBuilder) Description ¶
func (b *UnitBuilder) Description(d string) *UnitBuilder
func (*UnitBuilder) ExpectTerminal ¶
func (b *UnitBuilder) ExpectTerminal(states ...TerminalState) *UnitBuilder
ExpectTerminal declares the set of allowed terminal states.
func (*UnitBuilder) Send ¶
func (b *UnitBuilder) Send(channel string, payload proto.Message, at time.Duration) *UnitBuilder
Send schedules a value send on the named channel at the given offset from the start acknowledgment. For AutoFlow units the channel must be a declared channel arg and the payload a *autoflow.Value; for autocore units the name is engine-generated and only checked at execution.
type WeightedSource ¶
WeightedSource is a Source with its share in the Driver's unit mix.
type WorkflowInfoClient ¶
type WorkflowInfoClient interface {
GetWorkflowInfo(ctx context.Context, key autocore.WorkflowKey) (*autocore.WorkflowInfo, error)
}
WorkflowInfoClient is the subset of autocore.Client the Observer resolves workflow states through. Both autocore and AutoFlow units share the same workflow key format, so one client serves all sessions.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package corpus holds the pre-defined AutoFlow Units of the flowtest workload.
|
Package corpus holds the pre-defined AutoFlow Units of the flowtest workload. |
|
Package ledger is the loadtest driver's durable record of executed sessions: the authoritative client-side observation a later verifier consumes.
|
Package ledger is the loadtest driver's durable record of executed sessions: the authoritative client-side observation a later verifier consumes. |
|
Package loadtestmodule provides a compiled-in AutoFlow module for testing.
|
Package loadtestmodule provides a compiled-in AutoFlow module for testing. |