Skip to content

Releases: algoryn-io/pulse

Pulse v0.7.0 — WebSocket & multipart, YAML checks, TTFB/throughput, stress mode, and data-driven feeders

Choose a tag to compare

@jmgo38 jmgo38 released this 26 Jun 23:35
82fe32c

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

  • WebSockettransport.NewWebSocketClient (Go API) and a ws:///wss:// target.url in YAML (message, expectReply, subprotocol); message bytes feed throughput metrics and context deadlines bound each exchange.
  • Multipart uploadstransport.BuildMultipart + HTTPClient.DoMultipart (Go API) and a target.multipart block in YAML (text fields + files) for file-upload load tests.

Metrics & observability

  • Time-to-first-byte (TTFB) — measured per request via httptrace in its own HDR histogram, independent of total latency.
  • Throughput / byte countersbytes_in/bytes_out and 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; --quiet prints 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 checkstarget.checks (status / header / body-contains / JSON field), counted under a new check_failed category and forwarded to distributed workers.
  • Query parameters and a per-request timeout (applied as a context deadline).

Reliability & taxonomy

  • user_error category + 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

Choose a tag to compare

@jmgo38 jmgo38 released this 21 Jun 02:34
a8ddb9f

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

Choose a tag to compare

@jmgo38 jmgo38 released this 21 Jun 01:34
c11f315

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

Choose a tag to compare

@jmgo38 jmgo38 released this 19 Jun 19:33
6558642

v0.4.0 — Reliability, Security & DX improvements

What's changed

Reliability

  • Scheduler timer leak fixedtime.After in the poll loop replaced with time.NewTimer + Reset, eliminating one channel allocation per tick at high RPS
  • WithRetry respects 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 --out writes — 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/GCP 169.254.169.254, Azure 168.63.129.16) by default when enabled

New features

  • --dry-run CLI flag — validates the config file and prints a phase summary without sending any traffic
  • --seed <n> CLI flag + Config.Seed + seed YAML 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 patterns
  • pulse.ValidateConfig exported — shared validation logic is now callable programmatically, not just from the CLI
  • Connection pool config in HTTPClientConfig — new MaxIdleConns, MaxIdleConnsPerHost, and DisableKeepAlives fields with YAML support (maxIdleConns, maxIdleConnsPerHost, disableKeepAlives)
  • mockserver modesslow (--slow-delay <duration>), flaky (--flaky-rate <0-1>), and down modes available for local test targets

Internals & DX

  • Middleware RNG refactoredsync.Pool of goroutine-local PCG sources for the unseeded path (no global mutex under concurrency); seeded path uses a single mutex-serialised *rand.Rand for reproducibility
  • Config validation deduplicatedconfig/ delegates all shared rules to pulse.ValidateConfig; target-specific rules remain in config.validateConfig
  • CLI requires explicit <config.yaml> argument — undocumented httpbin.org fallback removed

Tests & benchmarks

  • 11 new benchmarks across scheduler, engine, and metrics packages
  • mockserver test coverage including context-cancellation behaviour in slow mode

Toolchain


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

Choose a tag to compare

@jmgo38 jmgo38 released this 01 May 02:57
5363782

What's Changed

  • feat: emit Fabric protobuf RunEvent and RunCompleted from Pulse by @jmgo38 in #59

Full Changelog: v0.3.6...v0.3.7

v0.3.6 — chaos level 4: circuit breaker simulation

Choose a tag to compare

@jmgo38 jmgo38 released this 26 Mar 23:49
ce0b9c3

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 dependencias

v0.3.5 — chaos level 3: retry and bulkhead

Choose a tag to compare

@jmgo38 jmgo38 released this 26 Mar 22:33
289c981

What's new

Chaos engineering — Level 3

  • WithRetry(n, backoff) — retry failed scenarios up to n times
  • WithBulkhead(maxConcurrent) — limit concurrent executions per scenario
  • ErrBulkheadFull — 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

Choose a tag to compare

@jmgo38 jmgo38 released this 26 Mar 22:19
4931d3b

What's new

Chaos engineering — Level 2

  • WithJitter(min, max, rate) — inject random latency between two bounds
  • WithTimeout(d) — enforce a maximum duration per scenario execution
  • WithStatusCode(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

Choose a tag to compare

@jmgo38 jmgo38 released this 26 Mar 21:22
4b132fd

What's new

Middleware pipeline

  • pulse.Middleware — composable scenario wrapper type
  • pulse.Chain(...Middleware) — compose multiple middlewares
  • pulse.Apply(scenario, ...Middleware) — ergonomic helper

Chaos engineering primitives

  • pulse.WithLatency(d, rate) — inject artificial latency into a percentage of requests
  • pulse.WithErrorRate(rate) — inject faults into a percentage of requests
  • pulse.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

Choose a tag to compare

@jmgo38 jmgo38 released this 26 Mar 19:47
18bb44d

What's new

go test integration

  • pulse.RunT(t, test) — run load tests inside go test
  • pulse.SkipIfShort(t) — skip load tests when -short flag is set
  • Metrics reported via t.Log, visible with go test -v
  • Threshold failures call t.Fatal automatically

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,
    })
}