Skip to main content
Version: 5.x

Performance and quality

Design choices that matter for throughput​

  • One serialization per broadcast. A broadcast is serialized once and the same bytes are written to every recipient.
  • Concurrent fanout. Local recipients are written in parallel under one shared deadline, so a slow socket is aborted without holding up the rest.
  • Bounded work per connection. A fixed number of concurrent requests and a bounded queue keep one busy client from exhausting the server, while heartbeats are answered immediately.
  • Serialized writes. Each socket has one writer at a time; responses and broadcasts never interleave frames.
  • Redis delivery slots. Up to 16 Redis deliveries run concurrently per instance.

Benchmarks​

Microbenchmarks from a local Release run of 5.0 on .NET 10.0.12, AMD Ryzen 7 3700X, Windows 11 (median of five samples, 364-byte payload). The transport is an in-memory sink that performs no network I/O, so these numbers show library overhead, not end-to-end latency.

ScenarioRecipientsTime per operationAllocated per operation
Serialize per recipient1000.266 ms39.2 KB
Serialize once1000.004 ms1.2 KB
In-memory fanout1000.021 ms3.5 KB
Serialize per recipient1 0002.901 ms385 KB
Serialize once1 0000.009 ms1.2 KB
In-memory fanout1 0000.143 ms11.8 KB
Serialize per recipient10 0006.040 ms3.84 MB
Serialize once10 0000.021 ms1.2 KB
In-memory fanout10 0002.202 ms102 KB
Parse 1 000 requests–4.438 ms984 KB
  • Serialize per recipient and serialize once compare naive per-client serialization with DarkWS's shared payload.
  • In-memory fanout is a real IBroadcaster.PublishAsync through the in-memory backplane to every recipient: about 0.14 µs and 12 bytes per recipient at 1 000 recipients.
  • Parse 1 000 requests parses request envelopes, about 4 µs per request.

Run them yourself on an idle machine:

dotnet run --project benchmarks/DarkWS.Benchmarks -c Release

Results depend on hardware and runtime; compare runs on the same machine. They do not model TLS, slow clients, or handler work.

Tests​

Every change runs the full suite on .NET 8, 9, and 10:

SuiteTests
Server (DarkWS)193
.NET client56
Testing package29
Redis backplane (real Redis in Docker)27
Browser client (Vitest)161

Coverage gates fail the build below 90% lines or 80% branches for any package. Current coverage:

PackageLinesBranches
DarkWS97.6%94.3%
DarkWS.Redis93.2%91.7%
DarkWS.Client96.3%92.3%
DarkWS.Client.DependencyInjection100%100%
DarkWS.Testing100%100%
darkws99.0%97.1%

Besides unit tests, the gate:

  • runs the browser client, compiled from source, on Node.js against a real DarkWS server;
  • tests the Redis backplane against StackExchange.Redis 2.13.17 and 3.2.1;
  • packs every NuGet package, checks its XML documentation and symbols, installs it into a clean consumer project, and runs it on all target frameworks;
  • packs the npm package from a clean copy to verify its build;
  • runs a Redis load regression that checks pending work and retained memory under a stuck recipient.

API stability​

Public APIs are tracked with PublicApiAnalyzers: any change to the public surface must be declared and reviewed, or the build fails. Breaking changes ship only in major versions, with migration notes in the changelog and in Upgrading.