bench

package
v0.0.0-...-9bc0499 Latest Latest
Warning

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

Go to latest
Published: Jul 14, 2026 License: MIT Imports: 29 Imported by: 0

Documentation

Index

Constants

View Source
const (
	CompressionIdentity = "identity"
	CompressionGzip     = "gzip"
	CompressionSnappy   = "snappy"
	CompressionZstd     = "zstd"
)
View Source
const (
	OutputFormatHuman = "human"
	OutputFormatJSON  = "json"
)

OutputFormat values accepted by --output. The human form is the default and matches the existing stdout layout; the json form emits a single pretty-printed object.

View Source
const (
	ConnModelPerWorker  = "per-worker"
	ConnModelShared     = "shared"
	ConnModelPerRequest = "per-request"
)

Connection-model selectors. The flag and its semantics mirror NDB-004: per-worker reuses one ClientConn per worker, shared multiplexes one ClientConn across all workers, and per-request forces a fresh handshake per RPC.

Variables

View Source
var (
	ErrEmptyMix       = errors.New("empty mix")
	ErrUnknownShape   = errors.New("unknown shape")
	ErrInvalidWeight  = errors.New("invalid weight")
	ErrMalformedToken = errors.New("malformed token")
	ErrDuplicateShape = errors.New("duplicate shape")
	ErrMixedWeighting = errors.New("mixed weighted and unweighted entries")
)

Sentinel errors returned by ParsePayloadMix. Tests assert via errors.Is.

Functions

func BuildEchoRequest

func BuildEchoRequest(shape echov1.PayloadShape, sizes PayloadSizes) *echov1.EchoRequest

BuildEchoRequest constructs an EchoRequest for the given shape, sized per the passed-in sizes. Slice and string contents are zero-valued; the shape and length are what matter for benchmarking proto-decode and wire-size effects. embedding-bytes is sized at 4 * EmbeddingDim so its wire size matches embedding-float at the same --embedding-dim. The mixed shape places StringLen into Name and BytesSize into Blob; other MixedPayload fields are left zero. An unknown or unspecified shape returns a request with Shape set and no Payload, so the caller can decide whether to treat it as an error.

Types

type BackendSkew

type BackendSkew struct {
	CountRatio float64
	P99Ratio   float64
}

BackendSkew captures imbalance across backends. CountRatio is max-count/min-count across every backend that received traffic; one slow or one overloaded replica shows up as a large ratio. P99Ratio is max-p99/min-p99 across backends that recorded at least one successful sample, so a fully broken backend does not divide by zero. Either ratio is zero when fewer than two backends qualify; the report renders that as n/a.

type BackendStats

type BackendStats struct {
	Key            string
	Source         string
	Count          int
	ErrorCount     int
	PercentOfTotal float64
	P50            time.Duration
	P99            time.Duration
}

BackendStats summarises the requests sent to a single backend, identified by the fallback chain in backendKey. Source records which step in the chain produced the key so the report can render its provenance.

type Config

type Config struct {
	Target                string
	Plaintext             bool
	TLSInsecureSkipVerify bool
	Concurrency           int
	Duration              time.Duration
	Payload               PayloadMix
	EmbeddingDim          int
	BytesSize             int
	StringLen             int
	Compression           string
	ConnModel             string
	OutputFormat          string
	Headers               map[string]string
	Labels                map[string]string
	Output                io.Writer
	// contains filtered or unexported fields
}

Config is the full input surface of one bench run. Every flag on `netdebug bench` maps to a field here. Headers carry per-RPC gRPC metadata; Labels are run-tagging key/value pairs that are surfaced in reports and the JSON summary but never sent over the wire. Output defaults to os.Stdout when nil. dialOpts is reserved for tests that need to inject a custom dialer such as bufconn; library callers should leave it zero.

func New

func New() *Config

New returns a Config populated with the bench command's defaults: plaintext localhost target, single worker for ten seconds, an embedding-float payload, identity compression, per-worker connection model, and human-formatted output to stdout. Callers override fields before invoking Run.

func (*Config) Run

func (c *Config) Run(ctx context.Context) error

Run validates the config, drives one bench run, and writes the configured report to c.Output. ctx cancellation stops the workers early; even without cancellation the run bounds itself to c.Duration via an internal timeout, so the call always returns once that elapses. A non-nil error means the run could not start or could not be written; per-RPC failures are aggregated into the report instead.

func (*Config) Validate

func (c *Config) Validate() error

Validate checks every field that the worker pool and report writers rely on. It returns on the first invalid field so the command-line surfaces one diagnostic at a time, in roughly the order the flags appear in --help.

type ConnPool

type ConnPool interface {
	NewSource() (ConnSource, error)
	Close() error
}

ConnPool owns the long-lived gRPC client resources for a bench run and hands out per-worker ConnSources whose behavior depends on the selected connection model.

type ConnSource

type ConnSource interface {
	Acquire(ctx context.Context) (*grpc.ClientConn, ReleaseFunc, error)
	Close() error
}

ConnSource is the per-worker view of a ConnPool. Each worker calls NewSource once at startup and Acquire once per RPC.

type ErrorMessageStat

type ErrorMessageStat struct {
	Message string
	Count   int
}

ErrorMessageStat is one distinct error message within a status-code bucket, along with how many results produced it.

type LatencyStats

type LatencyStats struct {
	Count  int
	Min    time.Duration
	Mean   time.Duration
	Stddev time.Duration
	Max    time.Duration
	P50    time.Duration
	P90    time.Duration
	P99    time.Duration
}

LatencyStats is the aggregate latency block used four times in Summary, one each for total, server-reported, network-derived, and upstream-header time. Count is the number of samples that contributed; zero means no valid samples, in which case the other fields are zero rather than undefined.

type PayloadEntry

type PayloadEntry struct {
	Shape  echov1.PayloadShape
	Weight int
}

PayloadEntry is a single shape in a weighted payload mix.

type PayloadMix

type PayloadMix []PayloadEntry

PayloadMix is an ordered list of weighted payload shapes parsed from a --payload flag value. Entries preserve input order. When the input omits weights for every entry, every entry's weight is 1 (default-equal).

func ParsePayloadMix

func ParsePayloadMix(s string) (PayloadMix, error)

ParsePayloadMix parses a --payload flag value. Accepted forms:

  • "shape": single shape, weight 1
  • "shape,shape,...": default-equal weights, weight 1 each
  • "shape:N,shape:N,...": explicit non-negative integer weights

Either every entry carries a weight or none of them do; mixed forms are rejected. Surrounding whitespace is trimmed, but whitespace inside a token is not. A weight of zero is accepted and recorded verbatim; the consumer decides whether to skip such entries during weighted selection.

func (*PayloadMix) Set

func (m *PayloadMix) Set(s string) error

Set parses s through ParsePayloadMix and replaces the receiver on success. On error the receiver is left unmodified so cobra/pflag's flag-parsing diagnostics report a clean before/after.

func (*PayloadMix) String

func (m *PayloadMix) String() string

String renders the mix in a form ParsePayloadMix can re-parse. A single-entry mix with weight 1 is rendered as a bare shape name; any other mix is rendered as "shape:weight,shape:weight". An empty mix renders as the empty string.

func (*PayloadMix) Type

func (m *PayloadMix) Type() string

Type satisfies pflag.Value and shows up as the placeholder in --help.

type PayloadSelector

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

PayloadSelector picks shapes from a payload mix according to weight. Zero-weight entries are dropped at construction time. A selector is safe for concurrent Pick calls as long as each caller supplies its own *rand.Rand; the selector itself is read-only after NewPayloadSelector.

func NewPayloadSelector

func NewPayloadSelector(mix PayloadMix) *PayloadSelector

NewPayloadSelector precomputes a weighted picker over the positive-weight entries of mix. Returns nil if no entry has weight > 0; callers that have already run Config.Validate will not see that case.

func (*PayloadSelector) Pick

Pick returns a shape sampled from the configured weight distribution. The single-shape mix is a fast path that avoids touching r.

type PayloadSizes

type PayloadSizes struct {
	EmbeddingDim int
	BytesSize    int
	StringLen    int
}

PayloadSizes controls how large each payload shape is when built. Units differ by shape: EmbeddingDim is the number of float32 dimensions for both embedding-float and embedding-bytes, BytesSize is bytes for the bytes shape, and StringLen is character count for the string shape.

type ReleaseFunc

type ReleaseFunc func()

ReleaseFunc is returned by Acquire and must be invoked when the caller is done with the ClientConn. For per-worker and shared sources it is a no-op; for per-request sources it closes the just-dialed connection.

type Result

type Result struct {
	Start                     time.Time
	End                       time.Time
	TotalDuration             time.Duration
	ServerDurationNs          int64
	UpstreamDurationNs        int64
	HasUpstreamTime           bool
	BytesSentUncompressed     int64
	BytesSentWire             int64
	BytesReceivedUncompressed int64
	BytesReceivedWire         int64
	PodName                   string
	PodHostname               string
	PeerAddr                  string
	Err                       error
}

Result records one completed RPC. TotalDuration is wall-clock as observed by the worker; ServerDurationNs is the value the echo server reports in its response, so TotalDuration - ServerDurationNs approximates network plus framing time. UpstreamDurationNs comes from the x-envoy-upstream-service-time response header and is only meaningful when HasUpstreamTime is true; a sidecar that does not emit the header leaves UpstreamDurationNs at zero and the flag false, distinguishing "no measurement" from a genuine zero. BytesSent* and BytesReceived* are filled by the wire-length stats handler in compression.go: the Uncompressed counters are the marshaled message length, the Wire counters include any compression and gRPC framing. PodName, PodHostname, and PeerAddr form the backend-identification fallback chain consumed by aggregator.backendKey, in that order of preference. Err is non-nil when the RPC failed; it is the raw grpc status error so status.FromError on it recovers the code.

type StatusCodeStats

type StatusCodeStats struct {
	Code        codes.Code
	CodeName    string
	Count       int
	TopMessages []ErrorMessageStat
}

StatusCodeStats groups errored requests by gRPC status code. CodeName is the human-friendly rendering of Code, suitable for both the human report and a future JSON summary. TopMessages is at most topErrorMessages long, sorted by Count descending, with each Message truncated to maxErrorMessageLen runes with a trailing "..." when cut.

type Summary

type Summary struct {
	Count      int
	ErrorCount int
	Elapsed    time.Duration
	Throughput float64
	ConnModel  string

	Total    LatencyStats
	Server   LatencyStats
	Network  LatencyStats
	Upstream LatencyStats

	Backends    []BackendStats
	BackendSkew BackendSkew

	Errors []StatusCodeStats
}

Summary is the aggregated output of aggregate, one per bench run. Count is the number of completed RPCs including errors; Throughput is Count / Elapsed in RPS. ConnModel is the run's connection model, captured here so reports can label the result without re-reading the originating Config. The four LatencyStats fields correspond to wall-clock total, server-reported processing time, the total-minus-server difference that approximates network plus framing, and the x-envoy-upstream-service-time header. Upstream's Count is the subset of results with HasUpstreamTime set, so a Linkerd run shows Count=0 there. Backends and BackendSkew break the run down by backend pod when identification is possible. Errors groups failed RPCs by gRPC status code.

Jump to

Keyboard shortcuts

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