Documentation
¶
Index ¶
- Constants
- Variables
- func BuildEchoRequest(shape echov1.PayloadShape, sizes PayloadSizes) *echov1.EchoRequest
- type BackendSkew
- type BackendStats
- type Config
- type ConnPool
- type ConnSource
- type ErrorMessageStat
- type LatencyStats
- type PayloadEntry
- type PayloadMix
- type PayloadSelector
- type PayloadSizes
- type ReleaseFunc
- type Result
- type StatusCodeStats
- type Summary
Constants ¶
const ( CompressionIdentity = "identity" CompressionGzip = "gzip" CompressionSnappy = "snappy" CompressionZstd = "zstd" )
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.
const ( ConnModelPerWorker = "per-worker" 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 ¶
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 ¶
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 ¶
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.
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 ¶
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 ¶
func (s *PayloadSelector) Pick(r *rand.Rand) echov1.PayloadShape
Pick returns a shape sampled from the configured weight distribution. The single-shape mix is a fast path that avoids touching r.
type PayloadSizes ¶
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.