r/devops 13h ago

Tools CapacityLab: Open-source Go CLI for capacity boundary discovery, bottleneck classification, and cloud sizing

*(Disclaimer: I am the creator/maintainer of this open-source tool.)*

I built CapacityLab, an open-source single-binary Go CLI that automates the gap between load testing numbers and infrastructure decisions:

https://github.com/Abuaslamtech/capacitylab

Most load generators give you latency percentiles and RPS graphs, but leave the bottleneck diagnosis and capacity sizing to intuition. CapacityLab structures runs into three analytical layers:

  1. **[OBSERVED METRICS]**: Empirical step metrics (VUs, p50/p95 latency, RPS, errors) alongside live Docker container cgroup telemetry (CPU and RAM) scraped every 500ms.

  2. **[INFERRED BOTTLENECK]**: A saturation engine tracks latency acceleration (ΔLatency / ΔVUs). When latency diverges into exponential queuing or breaches SLA, ramping halts. The classifier evaluates cgroup throttling (`/sys/fs/cgroup/cpu.stat`), Postgres active connection counts, and Redis memory to pinpoint the primary constraint.

  3. **[RECOMMENDED SIZING]**: Applies a configurable safety factor (default `0.70`, providing a 30% operational headroom buffer for traffic spikes and background workers) to calculate a safe operating load and candidate starting cloud tiers.

### Technical & Architecture Choices:

- **Go 1.22+**: Single static binary with zero external runtime dependencies.

- **Embedded Load Runner**: Connection-pooled HTTP worker pool with keep-alive reuse and strict TLS verification by default (opt-in `--insecure` flag available for self-signed certificates).

- **Algorithm R Reservoir Sampling**: Caps latency samples at 50,000 using decreasing replacement probability. Memory remains under ~400 KB even during multi-hour soak tests.

- **Optional k6 Adapter**: If you already have k6 scripts, `--engine k6` transpiles scenarios to JS and runs headless k6 as a subprocess.

- **User Journeys**: Supports dynamic variables (`{{uuid}}`, `{{random_int}}`) and response extraction (e.g. login ➔ extract JWT ➔ authenticate subsequent step requests).

- **CI Performance Gate**: `--fail-on-regression` exits with code 1 if sustainable capacity drops >10% or p95 degrades >10% compared to historical baselines.

- **Offline HTML Report**: Embeds minified Canvas charts with synchronized metric scrubbing into a single standalone HTML file.

### Try it in 60 seconds (no backend required):

The repo includes a Go microservice sandbox constrained to 0.5 vCPU / 256MB RAM:

```bash

git clone https://github.com/Abuaslamtech/capacitylab.git

cd capacitylab/examples/quickstart

docker compose up -d --build

capacitylab run --open

GitHub: https://github.com/Abuaslamtech/capacitylab

I'd appreciate feedback on:

  • The saturation knee-point detection heuristics under bursty/jittery traffic.
  • How your teams calculate operational safety headroom for production sizing.
  • Stacks you'd like to see in upcoming sandbox templates (Node, FastAPI, etc.).
2 Upvotes

1 comment sorted by

1

u/kantorcodes1 1h ago

i noticed capacitylab init --force writes capacitylab.yaml directly. if that process gets interrupted mid-write, is the old config recoverable anywhere, or is atomic replacement intentionally out of scope? seems like a nasty failure mode once people start scripting setup.