{"id":47669941,"url":"https://github.com/algoryn-io/pulse","last_synced_at":"2026-04-02T12:30:30.484Z","repository":{"id":342730207,"uuid":"1174922505","full_name":"algoryn-io/pulse","owner":"algoryn-io","description":"Programmable load testing engine in Go with arrival-rate scheduling, thresholds, and structured JSON output.","archived":false,"fork":false,"pushed_at":"2026-03-25T22:47:02.000Z","size":356,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-03-25T22:49:20.685Z","etag":null,"topics":["cli","golang","http","load-testing","observability","performance-testing"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/algoryn-io.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-07T01:55:04.000Z","updated_at":"2026-03-25T22:47:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/algoryn-io/pulse","commit_stats":null,"previous_names":["jmgo38/pulse"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/algoryn-io/pulse","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/algoryn-io%2Fpulse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/algoryn-io%2Fpulse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/algoryn-io%2Fpulse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/algoryn-io%2Fpulse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/algoryn-io","download_url":"https://codeload.github.com/algoryn-io/pulse/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/algoryn-io%2Fpulse/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31306050,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T09:48:21.550Z","status":"ssl_error","status_checked_at":"2026-04-02T09:48:19.196Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","golang","http","load-testing","observability","performance-testing"],"created_at":"2026-04-02T12:30:29.798Z","updated_at":"2026-04-02T12:30:30.464Z","avatar_url":"https://github.com/algoryn-io.png","language":"Go","readme":"# Pulse\n\n**Pulse** is a programmable reliability and load testing engine written in Go.\n\n[![CI](https://github.com/algoryn-io/pulse/actions/workflows/ci.yml/badge.svg)](https://github.com/algoryn-io/pulse/actions)\n[![Go Version](https://img.shields.io/github/go-mod/go-version/algoryn-io/pulse)](https://go.dev/doc/install)\n[![Latest Release](https://img.shields.io/github/v/release/algoryn-io/pulse)](https://github.com/algoryn-io/pulse/releases)\n[![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)\n[![Go Reference](https://pkg.go.dev/badge/algoryn.io/pulse.svg)](https://pkg.go.dev/algoryn.io/pulse)\n\nLightweight, deterministic, and designed for real-world automation.\n\nIt generates controlled HTTP load against a target, collects latency and error metrics, and evaluates configurable pass/fail thresholds. Tests are driven by a YAML config file and executed through the `pulse` CLI.\n\n## Quick Start\n\nFrom the repository root, with a **Go** toolchain matching [`go.mod`](go.mod):\n\n**1. Start the mock HTTP server** (listens on `:8080` by default):\n\n```sh\ngo run ./cmd/mockserver -mode healthy\n```\n\n**2. In another terminal, run a load test** against the examples (they target `http://localhost:8080`):\n\n```sh\ngo run ./cmd/pulse run examples/baseline.yaml\n```\n\n**3. Print results as JSON** on stdout:\n\n```sh\ngo run ./cmd/pulse run examples/baseline.yaml --json\n```\n\nAfter installing the binaries:\n\n```sh\ngo install ./cmd/pulse\ngo install ./cmd/mockserver\n```\n\nYou can run `pulse` and `mockserver` from your `PATH` instead of using `go run`.\n\n**Use as a library** in your Go project:\n```sh\ngo get algoryn.io/pulse@latest\n```\n\n**Expected results** (with the mock server in the suggested mode from [Examples](#examples)):\n\n- [`baseline.yaml`](examples/baseline.yaml) → **PASS**\n- [`mixed-errors.yaml`](examples/mixed-errors.yaml) → **FAIL** (thresholds)\n- [`timeout.yaml`](examples/timeout.yaml) → **FAIL** (thresholds)\n\n---\n\n## Features\n\n- **Arrival-rate scheduling** — request-driven load (requests/sec), with constant, ramp, step, and spike phases (not user/VU-based)\n- **Bounded concurrency** — configurable goroutine limit prevents runaway resource usage\n- **Metrics aggregation** — total, failed, RPS, latency (min, mean, p50, p95, p99, max), status code distribution, normalized error categories\n- **Thresholds** — `error_rate`, `mean_latency`, `p95_latency`, `p99_latency` with PASS / FAIL in the text report\n- **HTTP transport** — GET, POST, PUT, DELETE, PATCH; optional `headers`, `body`, and `timeout` in YAML\n- **CLI** — `pulse run \u003cconfig.yaml\u003e` with human-readable text and JSON output modes\n- **Result hook** — optional `OnResult` callback in `Config` for post-run integrations (CI systems, observability pipelines)\n- **Middleware pipeline** — composable `Middleware` type with `Chain` and `Apply` helpers\n- **Chaos engineering** — `WithLatency`, `WithErrorRate`, `WithJitter`, `WithTimeout`, `WithStatusCode`, `WithRetry`, `WithBulkhead`, `WithCircuitBreaker`\n- **go test integration** — `RunT` and `SkipIfShort` for load testing inside `go test`\n---\n\n## Mock Server\n\nPulse includes a **built-in mock HTTP server** for local testing and demos (`cmd/mockserver`). It avoids external dependencies while you try the example YAML files.\n\n**Run** (default address `:8080`):\n\n```sh\ngo run ./cmd/mockserver -mode healthy\n```\n\nOptional: `-addr :9090` to listen on another port (then set `target.url` in your YAML accordingly).\n\n| Mode | Behavior |\n|------|----------|\n| `healthy` | Always responds **200 OK** quickly with a short body. |\n| `mixed-errors` | Alternates **200** and **500** on successive requests (deterministic). |\n| `slow` | Sleeps **120ms** before each **200** — useful with `examples/timeout.yaml` (short client timeout). |\n\n```sh\ngo run ./cmd/mockserver -mode mixed-errors\ngo run ./cmd/mockserver -mode slow\n```\n\n---\n\n## Usage\n\n### 1. Write a config file\n\n```yaml\nphases:\n  - type: constant\n    duration: 30s\n    arrivalRate: 50\n\n  - type: ramp\n    duration: 30s\n    from: 10\n    to: 100\n\n  - type: step\n    duration: 60s\n    from: 10\n    to: 100\n    steps: 5\n\n  - type: spike\n    duration: 60s\n    from: 20\n    to: 300\n    spikeAt: 20s\n    spikeDuration: 10s\n\ntarget:\n  method: GET\n  url: https://api.example.com/health\n\nmaxConcurrency: 100\n\nthresholds:\n  errorRate: 0.01       # fail if error rate exceeds 1%\n  maxMeanLatency: 200ms # fail if mean latency exceeds 200ms\n```\n\n### 2. Run the test\n\n```sh\npulse run config.yaml\n```\n\nOptional flags:\n\n| Flag | Description |\n|---|---|\n| `--json` | Print results as JSON to stdout |\n| `--out \u003cfile\u003e` | Write results as JSON to a file (can combine with `--json` to mirror the same JSON to stdout) |\n\n---\n\n## Examples\n\nReady-to-run scenarios live under [`examples/`](examples/). By default they use **`http://localhost:8080`** — pair them with **`go run ./cmd/mockserver`** in the matching mode (see above). Expected outcomes depend on server behavior.\n\n| File | Intent | Suggested mock mode | Example command |\n|------|--------|---------------------|-----------------|\n| [`baseline.yaml`](examples/baseline.yaml) | Latency SLOs; all thresholds should **PASS** on a fast service | `healthy` | `go run ./cmd/pulse run examples/baseline.yaml` |\n| [`mixed-errors.yaml`](examples/mixed-errors.yaml) | Strict `errorRate`; should **FAIL** when failures exceed the limit | `mixed-errors` | `go run ./cmd/pulse run examples/mixed-errors.yaml` |\n| [`timeout.yaml`](examples/timeout.yaml) | Short client timeout vs slow responses; error rate should **FAIL** | `slow` | `go run ./cmd/pulse run examples/timeout.yaml` |\n| [`post-json.yaml`](examples/post-json.yaml) | POST with JSON body and headers | `healthy` (POST body accepted) | `go run ./cmd/pulse run examples/post-json.yaml` |\n| [`put-json.yaml`](examples/put-json.yaml) | PUT with JSON body | `healthy` | `go run ./cmd/pulse run examples/put-json.yaml` |\n| [`step.yaml`](examples/step.yaml) | Step phase: discrete rate levels from 10 to 100 RPS in 5 steps | `healthy` | `go run ./cmd/pulse run examples/step.yaml` |\n| [`spike.yaml`](examples/spike.yaml) | Spike phase: base 20 RPS, burst to 300 RPS for 10s | `healthy` | `go run ./cmd/pulse run examples/spike.yaml` |\n\n---\n\n## Exit Codes\n\nThe `pulse` CLI uses exit codes for automation (e.g. CI):\n\n| Code | Meaning |\n|------|--------|\n| **0** | Run finished and **all configured thresholds passed** (`pulse.Run` returned no error). |\n| **2** | Run finished but **at least one threshold failed** — the error chain contains only `*pulse.ThresholdViolationError` values. |\n| **1** | Anything else: invalid usage, config/load failure, I/O error, scheduler/engine failure, or a **mix** of threshold and non-threshold errors. |\n\n---\n\n## JSON Output\n\nWith **`--json`**, the CLI prints one indented JSON object to stdout. With **`--out \u003cpath\u003e`**, it writes the **same** object to a file. Without **`--json`**, stdout still shows the **text** report when a result is available; with **`--json`**, stdout is JSON only, and you can still add **`--out`** to persist a copy.\n\n**Structure:**\n\n```json\n{\n  \"summary\": {\n    \"total\": 0,\n    \"failed\": 0,\n    \"rps\": 0,\n    \"duration_ms\": 0\n  },\n  \"latency\": {\n    \"min_ms\": 0,\n    \"p50_ms\": 0,\n    \"mean_ms\": 0,\n    \"p95_ms\": 0,\n    \"p99_ms\": 0,\n    \"max_ms\": 0\n  },\n  \"status_codes\": { \"200\": 0 },\n  \"errors\": { \"http_status_error\": 0 },\n  \"thresholds\": [\n    { \"description\": \"string\", \"pass\": true }\n  ],\n  \"passed\": true\n}\n```\n\n- **Durations** — `summary.duration_ms` is the run length in **milliseconds** (integer). **`latency.*_ms`** values are also in **milliseconds** (floating-point).\n- **`passed`** — `true` when **every** configured threshold evaluation succeeded; `false` if any failed. Aligns with [exit code](#exit-codes) **0** vs **2** for threshold-only failures.\n- **`thresholds`** — ordered list of individual checks; each entry has a human-readable **`description`** and **`pass`**.\n\n`{}` and `[]` are valid when that part of the result is empty—for instance, no recorded status codes, no classified errors, or no threshold outcomes to list.\n\n---\n\n## Example output\n\n**Text (default):**\n\n```\nTotal requests: 2250\nFailed requests: 12\nDuration: 1m0.41s\nRPS: 37.25\n\nMin latency: 18ms\nP50 latency: 45ms\nMean latency: 52ms\nP95 latency: 134ms\nP99 latency: 198ms\nMax latency: 312ms\n\nStatus codes:\n  200: 2238\n  503: 12\n\nErrors:\n  http_status_error: 12\n\nThresholds:\n  PASS error_rate \u003c 0.01\n  PASS mean_latency \u003c 200ms\n```\n\n**JSON (`--json`):**\n\n```json\n{\n  \"summary\": {\n    \"total\": 2250,\n    \"failed\": 12,\n    \"rps\": 37.25,\n    \"duration_ms\": 60410\n  },\n  \"latency\": {\n    \"min_ms\": 18,\n    \"p50_ms\": 45,\n    \"mean_ms\": 52,\n    \"p95_ms\": 134,\n    \"p99_ms\": 198,\n    \"max_ms\": 312\n  },\n  \"status_codes\": { \"200\": 2238, \"503\": 12 },\n  \"errors\": { \"http_status_error\": 12 },\n  \"thresholds\": [\n    { \"description\": \"error_rate \u003c 0.01\", \"pass\": true },\n    { \"description\": \"mean_latency \u003c 200ms\", \"pass\": true }\n  ],\n  \"passed\": true\n}\n```\n\n---\n\n## Architecture\n\n```\npulse run config.yaml\n        │\n        ▼\n   config.Load()          Parses YAML → pulse.Test\n        │\n        ▼\n    pulse.Run()           Validates inputs, evaluates thresholds\n        │\n        ▼\n    engine.Run()          Orchestrates phases and concurrency\n        │\n        ▼\n  scheduler.Run()         Token-bucket pacing; constant, ramp, step, and spike phases\n        │\n        ▼\n   Scenario func          Executes the HTTP request via transport.HTTPClient\n        │\n        ▼\n  metrics.Aggregator      Records latency, status code, and error per call\n        │\n        ▼\n    pulse.Result          Returned to the CLI for text or JSON rendering\n```\n\n### Components\n\n| Package | Responsibility |\n|---|---|\n| `pulse` (root) | Public API — `Test`, `Config`, `Phase`, `Run`, `Result`, `ResultHook` |\n| `engine` | Runs phases in sequence; manages goroutine lifecycle and concurrency limiter |\n| `scheduler` | Fires scenario calls at the configured arrival rate (token bucket) |\n| `metrics` | Thread-safe aggregation of latency, status codes, and normalized error categories |\n| `transport` | Minimal HTTP client (GET, POST, PUT, DELETE, PATCH) built on `net/http` |\n| `config` | YAML loader — maps file config to `pulse.Test` |\n| `internal` | Concurrency limiter (semaphore); token bucket helper |\n\n---\n\n## Roadmap\n\n### v0.2.0 ✓\n- **Step and spike phases** — discrete and burst arrival-rate scheduling\n- **Full HTTP method support** — PUT, DELETE, PATCH\n- **Result hook** — `OnResult` callback for post-run integrations\n\n### v0.3.x ✓\n- **Algoryn ecosystem** — module path migrated to `algoryn.io/pulse`\n- **Fabric integration** — `ToRunEvent` connects Pulse to Algoryn ecosystem\n- **go test integration** — `RunT` and `SkipIfShort`\n- **Middleware pipeline** — `Chain`, `Apply`, `WithLatency`, `WithErrorRate`\n- **Chaos toolkit** — `WithJitter`, `WithTimeout`, `WithStatusCode`, `WithRetry`, `WithBulkhead`, `WithCircuitBreaker`\n\n### Upcoming\n- **Export formats** — CSV, OpenTelemetry\n- **gRPC transport**\n- **docs/architecture.md** — technical design decisions\n---\n\n## Part of Algoryn Fabric\n\nPulse is part of the [Algoryn Fabric](https://github.com/algoryn-io/fabric) ecosystem —\nan open source infrastructure toolkit for Go teams building reliable products.\n\n| Tool | What it does | Status |\n|------|-------------|--------|\n| **Pulse** | Load testing \u0026 chaos engineering | `v0.3.6` |\n| **Relay** | API Gateway \u0026 observability | `coming soon` |\n| **Beacon** | Alerting \u0026 on-call | `planned` |\n\n---\n\n## License\n\nMIT\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falgoryn-io%2Fpulse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falgoryn-io%2Fpulse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falgoryn-io%2Fpulse/lists"}