Releases: algoryn-io/pulse
Release list
Pulse v0.7.0 — WebSocket & multipart, YAML checks, TTFB/throughput, stress mode, and data-driven feeders
Pulse v0.7.0 is a robustness, observability, and ergonomics release. It rounds out the load-testing surface with new transports, richer metrics, capacity discovery, and a more capable YAML config — while keeping the library-first, CI-friendly design.
Highlights
New transports
- WebSocket —
transport.NewWebSocketClient(Go API) and aws:///wss://target.urlin YAML (message,expectReply,subprotocol); message bytes feed throughput metrics and context deadlines bound each exchange. - Multipart uploads —
transport.BuildMultipart+HTTPClient.DoMultipart(Go API) and atarget.multipartblock in YAML (text fields + files) for file-upload load tests.
Metrics & observability
- Time-to-first-byte (TTFB) — measured per request via
httptracein its own HDR histogram, independent of total latency. - Throughput / byte counters —
bytes_in/bytes_outand per-second throughput. - TTFB and throughput now surface in CLI text/JSON, every reporter (Prometheus, OpenTelemetry, InfluxDB, Datadog, CSV), and the live dashboard (P99 TTFB + Throughput cards).
- CLI error summary — errors are listed by frequency with percentages;
--quietprints a one-line top error.
Load shaping & data
- Stress / ramp-to-failure — climb the arrival rate until error rate or P99 breaches a threshold, then stop and report the sustained capacity.
- Data-driven runs — CSV/JSONL feeders with
{{variable}}substitution in the URL and body (round-robin or seeded random).
YAML config
- Response checks —
target.checks(status / header / body-contains / JSON field), counted under a newcheck_failedcategory and forwarded to distributed workers. - Query parameters and a per-request timeout (applied as a context deadline).
Reliability & taxonomy
user_errorcategory +pulse.UserError(err)to separate test-side failures from target-side ones.- Distributed worker test coverage raised to ~97%.
Compatibility
- JSON output stays at
schema_version: 1(all additions are additive). - WebSocket/multipart and feeders are local-only (not forwarded to distributed workers).
Full changelog: see CHANGELOG.md under v0.7.0.
Pulse v0.6.0 — Response assertions, data injection, env interpolation, OTel, gRPC, HAR import, and scenario chaining
Description:
This release adds eight developer-facing features that bring Pulse to production-grade ergonomics for real-world load testing scenarios.
Response assertions — transport.HTTPClient.DoWithResponse returns a *Response with the pre-read body. Use AssertStatus, AssertBodyContains, AssertBodyJSON, and
AssertHeader to validate responses inside scenarios without extra boilerplate.
Data injection — pulse.NewFeederT supplies parameterized values (user IDs, tokens, payloads) to concurrent scenario invocations round-robin.
pulse.NewFeederFuncT covers generated or random data. Both are generic and allocation-free in the hot path.
Env var interpolation in YAML — config.Load now expands ${VAR} and ${VAR:-default} placeholders before parsing, keeping secrets and environment-specific values out
of config files.
OpenTelemetry reporter — reporter.NewOTelReporter(provider) exports Pulse metrics as OTEL gauges (pulse.rps, pulse.error_rate, pulse.latency.p50/p90/p95/p99,
pulse.requests.total/failed). The caller owns the provider and exporter — OTLP, stdout, or any SDK backend.
gRPC support — transport.NewGRPCClient dials a gRPC server (insecure, system TLS, or custom TLS). transport.CallGRPC(fn) maps all 17 gRPC status codes to
HTTP-equivalent integers so Pulse thresholds and error metrics work across both transports without changes.
HAR import — har.LoadFile(path, cfg) turns a browser DevTools session export into a pulse.Scenario that replays all recorded requests in sequence. Hop-by-hop
headers are stripped automatically. A Filter function skips static assets and third-party requests.
Scenario chaining — pulse.Sequence(steps...) and pulse.Flow(steps...) compose multiple scenario functions into sequential user journeys. Flow wraps errors with the
step name ("login: unauthorized") for easy diagnosis in the result error map.
▎ HAR files contain session tokens — do not commit them to version control.
Pulse v0.5.0 — Distributed execution, live dashboard, adaptive load shaping, chaos injection, and plugin reporters
This release marks a major capability milestone for Pulse, adding five production-grade features on top of the existing multi-phase load engine.
Distributed execution — fan out a single test to multiple worker nodes via HTTP. The coordinator splits arrival rate across workers and merges their results into a
single unified report. Start workers with pulse worker --addr host:port and configure them in Config.Workers.
Live dashboard — pass --dashboard :9090 (or Config.DashboardAddr) to stream real-time RPS, latency percentile charts, and error rate to a browser via SSE. The page
reconnects automatically and shows a completion banner when the run finishes.
Adaptive load shaping — set Config.Adaptive to let the engine auto-tune the arrival rate during constant phases. The rate steps down when error rate or P99 latency
exceeds configured thresholds and recovers gradually when conditions improve.
Chaos / fault injection — wrap any http.RoundTripper with transport.NewChaosRoundTripper to inject synthetic errors and latency at the transport layer without
modifying scenario code.
Plugin reporters — implement pulse.Reporter to export metrics to any backend. Three built-in reporters ship in the reporter package: Prometheus (/metrics endpoint),
InfluxDB v2 (line protocol over HTTP), and Datadog (DogStatsD UDP).
Additional improvements include interval snapshots, saturation policies (drop / block), load-fidelity result fields, SSRF policy, --dry-run, and reliability fixes
across the scheduler, middleware, and CLI.
v0.4.0 — Reliability, Security & DX improvements
v0.4.0 — Reliability, Security & DX improvements
What's changed
Reliability
- Scheduler timer leak fixed —
time.Afterin the poll loop replaced withtime.NewTimer+Reset, eliminating one channel allocation per tick at high RPS WithRetryrespects context cancellation — aborts immediately when the context is cancelled instead of sleeping through the retry delay- Circuit breaker half-open is bounded — only one probe request is admitted at a time; previously concurrent requests could all pass through during the half-open window, causing thundering-herd re-admission
Security
- Atomic
--outwrites — output file is written to a temp file and renamed into place, preventing partial writes and symlink attacks on the destination path - Opt-in SSRF policy (
internal/ssrf) — allowlist/denylist of hosts and CIDR ranges checked before each outbound request; blocks RFC 1918, loopback, link-local, and cloud-metadata ranges (AWS/GCP169.254.169.254, Azure168.63.129.16) by default when enabled
New features
--dry-runCLI flag — validates the config file and prints a phase summary without sending any traffic--seed <n>CLI flag +Config.Seed+seedYAML field — sets a deterministic seed for middleware randomness (WithErrorRate,WithJitter,WithLatency,WithStatusCode); two runs with the same seed and sequential call order produce identical fault-injection patternspulse.ValidateConfigexported — shared validation logic is now callable programmatically, not just from the CLI- Connection pool config in
HTTPClientConfig— newMaxIdleConns,MaxIdleConnsPerHost, andDisableKeepAlivesfields with YAML support (maxIdleConns,maxIdleConnsPerHost,disableKeepAlives) mockservermodes —slow(--slow-delay <duration>),flaky(--flaky-rate <0-1>), anddownmodes available for local test targets
Internals & DX
- Middleware RNG refactored —
sync.Poolof goroutine-local PCG sources for the unseeded path (no global mutex under concurrency); seeded path uses a single mutex-serialised*rand.Randfor reproducibility - Config validation deduplicated —
config/delegates all shared rules topulse.ValidateConfig; target-specific rules remain inconfig.validateConfig - CLI requires explicit
<config.yaml>argument — undocumentedhttpbin.orgfallback removed
Tests & benchmarks
- 11 new benchmarks across
scheduler,engine, andmetricspackages mockservertest coverage including context-cancellation behaviour inslowmode
Toolchain
- Go 1.26.4 (fixes [GO-2026-5039](https://pkg.go.dev/vuln/GO-2026-5039) and [GO-2026-5037](https://pkg.go.dev/vuln/GO-2026-5037))
Full changelog: [v0.3.7...v0.4.0](https://github.com/algoryn-io/algoryn-fabric-ecosystem/compare/v0.3.7...v0.4.0)
v0.3.7 — Fabric protobuf integration
What's Changed
Full Changelog: v0.3.6...v0.3.7
v0.3.6 — chaos level 4: circuit breaker simulation
What's new
Chaos engineering — Circuit Breaker
WithCircuitBreaker(threshold, window, timeout)— simulates cascading
failures by opening a circuit when error rate exceeds threshold- Three states: CLOSED → OPEN → HALF-OPEN → CLOSED
ErrCircuitOpen— sentinel error when circuit rejects requests- Time-window based error rate evaluation with automatic reset
What this tests
Use WithCircuitBreaker to verify that your application's own circuit
breaker activates and protects downstream dependencies under load.
pulse.RunT(t, pulse.Test{
Scenario: pulse.Apply(
myScenario,
pulse.WithCircuitBreaker(0.3, 10*time.Second, 5*time.Second),
pulse.WithErrorRate(0.8),
),
Config: pulse.Config{
Thresholds: pulse.Thresholds{
ErrorRate: 0.4,
MaxP99Latency: 2*time.Second,
},
},
})
// PASS → tu app manejó los fallos en cadena correctamente
// FAIL → tu CB no está protegiendo las dependenciasv0.3.5 — chaos level 3: retry and bulkhead
What's new
Chaos engineering — Level 3
WithRetry(n, backoff)— retry failed scenarios up to n timesWithBulkhead(maxConcurrent)— limit concurrent executions per scenarioErrBulkheadFull— sentinel error when bulkhead capacity is exceeded
Example
pulse.RunT(t, pulse.Test{
Scenario: pulse.Apply(
myScenario,
pulse.WithBulkhead(10), // max 10 concurrent
pulse.WithRetry(3, 50*time.Millisecond), // retry up to 3 times
),
})v0.3.4 — chaos level 2: jitter, timeout, status code injection
What's new
Chaos engineering — Level 2
WithJitter(min, max, rate)— inject random latency between two boundsWithTimeout(d)— enforce a maximum duration per scenario executionWithStatusCode(code, rate)— force a specific HTTP status code
Example
pulse.RunT(t, pulse.Test{
Scenario: pulse.Apply(
myScenario,
pulse.WithJitter(10*time.Millisecond, 100*time.Millisecond, 0.2),
pulse.WithTimeout(200*time.Millisecond),
pulse.WithStatusCode(503, 0.05),
),
})v0.3.3 — middleware pipeline & chaos engineering primitives
What's new
Middleware pipeline
pulse.Middleware— composable scenario wrapper typepulse.Chain(...Middleware)— compose multiple middlewarespulse.Apply(scenario, ...Middleware)— ergonomic helper
Chaos engineering primitives
pulse.WithLatency(d, rate)— inject artificial latency into a percentage of requestspulse.WithErrorRate(rate)— inject faults into a percentage of requestspulse.ErrInjected— sentinel error for injected faults
Example
pulse.RunT(t, pulse.Test{
Scenario: pulse.Apply(
myScenario,
pulse.WithLatency(50*time.Millisecond, 0.1), // 10% con 50ms extra
pulse.WithErrorRate(0.05), // 5% fallan
),
})v0.3.2 — native go test integration
What's new
go test integration
pulse.RunT(t, test)— run load tests insidego testpulse.SkipIfShort(t)— skip load tests when-shortflag is set- Metrics reported via
t.Log, visible withgo test -v - Threshold failures call
t.Fatalautomatically
Example
func TestAPIUnderLoad(t *testing.T) {
pulse.SkipIfShort(t)
pulse.RunT(t, pulse.Test{
Config: pulse.Config{
Phases: []pulse.Phase{
{Type: pulse.PhaseTypeRamp, Duration: 30*time.Second, From: 10, To: 100},
},
Thresholds: pulse.Thresholds{
ErrorRate: 0.01,
MaxP99Latency: 200 * time.Millisecond,
},
},
Scenario: myScenario,
})
}