Table of Contents

Capacity and sizing

Short answer: there is no published "N calls" number for CalloraVoipSdk, and you should distrust anyone who gives you one. Concurrent-call capacity is a property of your host, runtime, GC mode, power profile and PBX — not of the SDK. What we publish instead is a reproducible measurement method plus one fully-documented reference run, so you can measure your own envelope with the same gate and compare like with like.

The quality gate

Capacity is only meaningful together with the quality bar it was measured at. A call counts as passing only when it stayed connected for the whole measurement window and both directions satisfy every one of:

Gate Threshold
Application and RTP delivery ≥ 99 % of the time-derived expectation
p99 frame interval ≤ 40 ms
Longest silence (including window edges) < 250 ms
Packet loss < 1 %
RTP/RTCP jitter ≤ 30 ms

Evidence is recorded per call and per direction: application frames (IMediaSender.SendAsync / IMediaReceiver.FrameReceived), RTP counters from ICall.RtpStatistics, p50/p95/p99 frame intervals, interarrival jitter, longest silence, gap-class counts, and RFC 3550 extended-sequence gaps. A call with incomplete RTP/RTCP evidence does not pass, even if audio kept flowing.

Three classifications — read the label

  • Strict capacity — the largest stage where every call passed every gate. One failure of any kind rejects the stage.
  • Functional full-duplex evidence — every call stayed connected, exchanged media, kept complete RTP/RTCP evidence and met the delivery/loss/silence gates, but at least one strict timing gate failed. Useful diagnostic; not a capacity claim.
  • Generator-limited evidence — the load generator, not the SDK, was the bottleneck. Recognisable by an outbound failure cluster whose call indices share a remainder modulo the worker count. Must never be presented as an SDK boundary.

Near the scheduler boundary, strict outcomes vary between repetitions. That is why results are always reported as "largest repeatedly validated stage" plus "any higher single-window observation" — never as one universal maximum.

Reference run (2026-07-28)

One concrete host: 8-core / 16-thread Intel i7-11800H, Nobara Linux 44, .NET 10, ~31 GiB RAM, Server GC, a restricted "Office" power profile (capped ~65 %, no turbo), with a co-located Asterisk Echo() container sharing the same machine. 20-ms PCMU frames, 10 s settling, 30 s exact measurement window.

Calls Result
1,408 Strict — repeatedly validated. Every call passed every gate, in independent runs
1,792 Strict in a single window; a repetition put 128 calls at a 41–43 ms inbound p99
1,920 Functional full-duplex. All calls connected with complete RTP/RTCP evidence and ≥ 99 % delivery; 27 calls recorded a 41 ms inbound p99 against the 40 ms limit

Resource picture at 1,920 calls: SDK testhost ≈ 0.85 GiB working set (1.10 GiB peak), Asterisk ≈ 0.41 GiB, managed allocations ≈ 3.35 GiB over 30 s (~120 MB/s), combined SDK + PBX CPU below 45 % of the machine. RAM and CPU were not the binding constraint — inbound playout scheduling was.

What this means for your sizing

  • These numbers describe that host and that profile. Treat 1,408 as evidence that the SDK is not the bottleneck at four-figure call counts on a laptop-class CPU, not as your number.
  • GC mode matters a lot. Workstation GC at 1,024 calls produced 158/158/0 (Gen0/1/2) collections; Server GC produced 30/0/0. Use Server GC for concurrency.
  • Power/frequency policy matters. The reference run was deliberately measured on a throttled profile, so it is conservative rather than best-case.
  • Co-locating the PBX costs you headroom. Asterisk shared the same 16 logical CPUs.
  • The first thing to fail is timing, not memory. Size for scheduler headroom, and watch p99 frame interval rather than RAM.

Measure your own envelope

The benchmark ships with the repository. It is marked SoakLong + Capacity and is deliberately excluded from PR and release CI — it takes a machine to itself:

dotnet test tests/CalloraVoipSdk.InteropTests -c Release -f net10.0 \
  --filter "Category=Capacity"

Defaults ramp from 64 to 4,096 calls. For a focused run around a boundary you already suspect:

CALLORA_CAPACITY_LEVELS=512,1024,1280 \
CALLORA_CAPACITY_REPORT=/tmp/callora-capacity.json \
dotnet test tests/CalloraVoipSdk.InteropTests -c Release -f net10.0 \
  --filter "Category=Capacity"

Every run serializes its full profile — host, runtime, thresholds, worker count, per-stage results and teardown state — into the JSON report. Any capacity claim that does not come with that report is not a validated claim.

The complete profile, environment variables, report fields and interpretation limits are documented for maintainers in docs/maintainers/capacity-quality-benchmark.md, and the full reference-run write-up — including the pre-fix results and what changed — in docs/audit/2026-07-28-capacity-quality-evidence.md.

Known observability limit

Inbound RTP exposes exact local sequence-range evidence. For outbound RTP the public surface exposes sent-packet counters plus the peer's latest RTCP receiver-report loss and jitter, but not the peer's extended-highest-sequence counter — so an exact outbound sequence-gap count cannot be derived, and the benchmark reports it as null rather than inventing one. Adding raw remote receiver-report counters to the public quality snapshot would close that gap.