Skip to content

Repository files navigation

Hammer

CI License: MIT .NET

A lightweight, arrival-rate HTTP load generator for .NET. Uses an open-loop producer-consumer model built around a bounded Channel<T> — no external dependencies beyond the .NET runtime.

Model

The tool uses a three-component design:

  • Producer — schedules request tickets at the target arrival rate, independent of how long requests take.
  • Bounded channel — absorbs short bursts without allowing unbounded queue growth; excess tickets are dropped and counted.
  • Consumer pool — a fixed number of workers that greedily pull tickets and fire HTTP requests in parallel.

Because the producer is decoupled from response latency, the configured request rate is open-loop: if the service slows down, the producer still attempts to schedule work at the target rate. When consumers can't keep up, the backlog grows and the tool starts dropping scheduled requests once the queue fills. The report surfaces this through dropped, queue-delay, and status-code distributions so you can see exactly where the system saturates.

Requirements

Build

dotnet build Hammer.slnx

Run

dotnet run --project src/Hammer.Console -- \
  --url "https://example.com/api/endpoint" \
  --method GET \
  --target-rps 150 \
  --duration 00:02:00 \
  --consumers 64 \
  --max-backlog 500 \
  --timeout 00:00:15

POST with a bearer token and request body:

dotnet run --project src/Hammer.Console -- \
  --url "https://example.com/api/endpoint" \
  --method POST \
  --body "{}" \
  --content-type "application/json" \
  --bearer-token "<jwt>" \
  --target-rps 75 \
  --duration 300 \
  --consumers 48 \
  --max-backlog 300

Options

Option Default Description
--url (required) Target URL
--method GET HTTP method
--target-rps (required) Target requests per second
--duration 00:01:00 Test duration (hh:mm:ss or seconds)
--consumers 32 Number of concurrent worker tasks
--max-backlog 256 Bounded channel capacity; excess tickets are dropped
--timeout 00:00:30 Per-request timeout (hh:mm:ss or seconds)
--body Request body
--content-type application/json Content-Type header (used with --body)
--bearer-token Authorization: Bearer token
--header Extra headers in Name: Value format; repeatable

Output

Scheduled:   9000   Enqueued:  8874   Dropped:    126
  Started:   8874  Completed:  8874
  Successes: 8701  Failures:    173   Success rate: 98.1%

Target RPS:    150.0   Start RPS:   147.9   Completion RPS: 139.4

Latency (ms)
  avg   p50   p95   p99   min   max
  84.2  71.0  210.5 388.1  12.3  921.4

Queue delay (ms)
  avg   p50   p95   p99   min   max
  2.1   0.8   9.4   28.0   0.0   143.2

Status codes
  200: 8701   timeout: 112   503: 61
  • Dropped — tickets discarded because the channel was full. Rising drops mean consumers can't keep up.
  • Queue delay — how long a ticket waited before a consumer picked it up. Growing queue delay is an early signal of overload.
  • Latency — wall-clock time from when a consumer started a request to when it completed.

Notes

  • Keep --max-backlog bounded. An unbounded queue hides the overload signal.
  • Watch dropped and queue-delay p95/p99 as saturation indicators — they react before success rate degrades.
  • Tune --consumers to match the concurrency your service can handle; too few workers starve the target, too many can skew latency by exhausting server thread pools.

License

MIT

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages