Plan receiver capacity
Size a receiver and prove it can sustain the feed before production activation.
Before you start
- A target latency budget
- Representative packet captures or live staging traffic
Establish the input budget
The measured feed is 54.3 Mbps, 5,585 packets per second, and 1,216 bytes per packet on average. It totals about 17.6 TB per 30-day month. The wire maximum is 1,228 bytes per UDP datagram. Plan from packets per second for CPU and queue capacity, from bytes per second for network and memory bandwidth, and from monthly bytes only when retaining traffic.
The measured payload arithmetic is:
text
5,585 packets/s × 1,216 bytes × 8 = 54,332,480 bits/s
5,585 packets/s × 86,400 s = 482,544,000 packets/day
54.3 Mb/s ÷ 8 = 6.7875 MB/s
6.7875 MB/s × 2,592,000 s = about 17.6 TB per 30 daysEthernet, IP, UDP, framing, and inter-packet gaps add link overhead beyond UDP payload. A 1 Gbps interface leaves ample margin. Do not provision a 100 Mbps interface for a 54.3 Mbps mean when bursts, monitoring, RPC, storage replication, and encapsulation share the path.
Apply independent headroom factors
Use four times the mean packet rate for short-window receive sizing until measurements support a different factor. That target is 22,340 packets per second and about 217 Mbps of UDP payload if mean packet size remains 1,216 bytes. Use two times the observed sustained busy-window rate for worker capacity. Burst capacity and sustained capacity are different requirements.
Compute queue slots from duration:
text
slots = ceil(5,585 × 4 × 0.250 seconds) = 5,585
round to implementation capacity = 8,192 packet slots
raw slot bytes = 8,192 × 1,228 = 10,059,776 bytesThen add packet metadata, allocator or slab bookkeeping, channel storage, FEC maps, slot ordering state, duplicate keys, decoded entries, and downstream messages. Measure resident memory at forced maximum queue occupancy. Avoid estimating a Rust object from field sizes alone because allocator alignment and container capacity matter.
Benchmark stages separately
Measure receive, header parse, leader lookup, signature and Merkle verification, duplicate check, FEC insertion, Reed-Solomon recovery, deshred, entry decode, transaction extraction, and downstream publication. Report items per core-second and p50, p95, and p99 execution time.
Use captured shreds with realistic duplicates, reordering, missing data, and coding ratios. A benchmark containing only complete, ordered data shreds skips the expensive error and recovery paths. Include empty tick entries and versioned transactions.
Estimate core demand from measured service time:
text
cores = arrival_rate × mean_cpu_seconds_per_item ÷ target_utilization
example = 5,585 × 0.000050 ÷ 0.60 = 0.47 cores for a 50 microsecond stageThis example is a formula, not a claim that verification takes 50 microseconds on your CPU. Use no more than about 60 to 70 percent sustained utilization on latency-sensitive workers. Tail latency grows sharply near saturation, and FEC recovery creates uneven work.
The following small calculator keeps units explicit:
rust
fn cores(rate_per_second: f64, micros_per_item: f64, target: f64) -> f64 {
rate_per_second * micros_per_item / 1_000_000.0 / target
}
fn queue_slots(rate: f64, burst: f64, stall_ms: f64) -> usize {
(rate * burst * stall_ms / 1000.0).ceil() as usize
}
fn main() {
let _required_cores = cores(5585.0, 50.0, 0.60);
let _required_slots = queue_slots(5585.0, 4.0, 250.0);
}Bound protocol state
Key FEC state by (slot, fec_set_index). Bound active slots, active sets, shards per set, total stored packet bytes, and age. The typical current target is 32 data and 32 coding shreds per FEC block, but read actual counts from coding headers for recovery. A final set can be shorter, and protocol releases can change construction.
If 128 active FEC sets each retain 64 maximum datagrams, raw packet storage is about 10 MiB. Multiple copies across queues and maps multiply that number. Store one owned packet and move ownership between stages where practical.
Bound duplicate retention by time or recent slots. A key can be slot, index, and shred type; conflict detection also needs a payload fingerprint or canonical bytes. Clear state after the slot is no longer useful and record evictions.
Plan storage only when required
Do not write every raw datagram to the primary receiver disk by default. At 17.6 TB per month, retention becomes a storage and write-amplification project. A rotating diagnostic capture of minutes or a sampled failure capture is cheaper and safer. If full retention is required, provision sustained sequential write, metadata, compression CPU, replication, index overhead, and recovery time.
Prove headroom
Replay one hour of busy traffic at one, two, and four times original timing. Inject packet loss and reorder windows so recovery runs. Pass only if the receiver keeps kernel and application drops at zero at the promised target, queue age returns to baseline, memory stays bounded, and no worker exceeds the sustained utilization budget.
Repeat the benchmark on the actual instance type. Cloud CPU generation, steal time, NIC virtualization, NUMA placement, kernel, compiler, and Agave dependency revision can materially change results. Capacity is a tested property of the deployed system, not a property of the source code alone.
Record the benchmark inputs and build identity with every result. A throughput number without the capture digest, replay timing, loss pattern, CPU allocation, and binary revision cannot support the next capacity decision.
Parameters
When it goes wrong
benchmark passes ordered data but fails live traffic
Cause. The test omitted duplicates, reordering, signature work, or Reed-Solomon recovery.
Fix. Replay representative captured traffic and inject recoverable and unrecoverable loss.
CPU remains below 50 percent but queue age grows
Cause. One partition or single-threaded stage is saturated while aggregate CPU hides it.
Fix. Measure per-thread utilization, queue age, and key distribution, then repartition the constrained stage.
disk fills near day 2
Cause. Raw retention was sized from an incorrect decimal, duration, or compression assumption.
Fix. Use the measured 17.6 TB per 30 days before replication and filesystem overhead.
Questions
- Why plan for four times the average packet rate?
- The one-second average hides leader emission patterns, network queue compression, and scheduler pauses. Four times is a conservative starting point for short windows, not a measured maximum. Record 1, 10, and 100 millisecond rates in production and replace the factor with observed percentiles.
- How much disk does full raw retention need?
- The measured feed is about 17.6 TB per 30-day month before filesystem metadata, indexes, replicas, or backups. Compression depends on the data and costs CPU. Most low-latency receivers should keep bounded diagnostic captures unless complete raw retention is a stated product requirement.
- Can one CPU core receive the feed?
- One dedicated core can often drain 5,585 UDP packets per second, but receipt is only the first stage. Signature verification, FEC recovery, entry decoding, and downstream work need measured capacity. Benchmark each stage on the deployment host and retain utilization headroom for bursty recovery work.