You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Determine how many concurrent users/uploads TracePcap can support, identify the bottlenecks, and document the levers for scaling (software + hardware) and the optimizations found. This is a research/findings write-up to inform capacity planning and future work; it is not a bug.
TL;DR
Concurrent users (analysts browsing results) is not the constraint — read endpoints are sub-second and trivially handle 10+ users even on modest hardware. The real constraint is concurrent analyses (ingestion throughput).
The analysis pipeline is CPU-bound and dominated by Suricata (~94% of per-file time). Throughput is capped by the in-process async pool (max-pool-size, default 10).
Load generated with k6 (containerised, grafana/k6) driving the real user journey: POST /files (multipart upload) → poll GET /files/{id} until completed → fetch /analysis/{id}/summary, /analysis/{id}/protocols, /conversations/{id}.
Each upload made unique (append a trailing tag) to avoid the duplicate-hash 409.
Test file: sample-files/geotag_demo.pcap (538 KB, 1,890 packets), full pipeline (nDPI + Suricata + extraction).
Caveat: run on a shared/contended dev box (backend got ~6 cores), not representative prod hardware, and a tiny pcap. Numbers below are indicative of shape/relative cost, not absolute capacity.
Findings
1. Concurrency sweep (per-analysis latency vs. concurrent uploads)
Concurrent uploads
Completed within 4 min
Analysis time (avg → p95)
HTTP errors
5
100%
66s → 76s
0%
10
100%
1m55s → 2m27s
0%
15
100%
2m43s → 3m41s
0%
20
42%*
3m20s → 4m+
0%
30
22%*
3m43s → 4m+
0%
* not failures — these exceeded the test's 4-minute poll cap; verified zero FAILED files. Backend CPU pegged ~6 cores throughout; memory (2–4 GB) was never the limiter. Failure mode is graceful degradation via queueing, not errors — until the queue overflows (see #451).
Breaking point on this hardware: ~20 simultaneous analyses before latency exceeds a 4-min budget. This scales with cores; it is not a fixed ceiling.
2. The dominant bottleneck: Suricata (controlled isolation test)
Pipeline config
Time per file
Full (nDPI + Suricata + extraction)
~53s
Suricata OFF
~3s
nDPI + Suricata OFF
~3s
Suricata OFF + extraction OFF
~2s
Suricata alone is ~94% of per-file analysis time, because it reloads the full Emerging Threats ruleset on every suricata -r invocation — a fixed cost independent of pcap size. Disabling it is an ~18× throughput gain.
Async pool sizing — max-pool-size=10 caps concurrent analyses regardless of available cores (backend/.../config/AsyncConfig.java). On a many-core host this leaves CPU idle.
Single-node monolith — one backend container owns the executor; no horizontal scale-out of ingestion.
Per-file fixed overhead generally — small files pay disproportionate process-startup cost (tshark/Suricata spawns); the 538 KB test is startup-dominated.
How to scale
Software (cheapest, biggest wins)
Don't run Suricata per file on the bulk/ingest path. Make full IDS an on-demand action for files/windows under investigation. (~18×.)
If kept on volume paths, run Suricata in persistent/socket mode or batch multiple pcaps per invocation to amortise the ruleset load.
Right-size async.max-pool-size to physical cores (it's already @Value-backed) and document tuning.
Externalise the work queue (DB work table or broker + N stateless workers) to scale ingestion horizontally beyond one node.
Fix the duplicate-hash check (read-then-insert isn't atomic; needs a DB unique constraint) to avoid redundant work under concurrent uploads.
Hardware
Ingestion is CPU-bound → more cores ≈ proportionally more analysis throughput (with the pool sized to match).
Storage: put Postgres + MinIO hot set on SSD/NVMe; spinning disks hurt at scale. For large corpora, prefer windowed ingest + eviction over storing everything in MinIO.
GPU is irrelevant to the analysis hot path (only the optional LLM-insights feature uses it).
Objective
Determine how many concurrent users/uploads TracePcap can support, identify the bottlenecks, and document the levers for scaling (software + hardware) and the optimizations found. This is a research/findings write-up to inform capacity planning and future work; it is not a bug.
TL;DR
max-pool-size, default 10).Methodology
grafana/k6) driving the real user journey:POST /files(multipart upload) → pollGET /files/{id}untilcompleted→ fetch/analysis/{id}/summary,/analysis/{id}/protocols,/conversations/{id}.per-vu-iterations, increasing VUs, sampling backend container CPU/mem per level.sample-files/geotag_demo.pcap(538 KB, 1,890 packets), full pipeline (nDPI + Suricata + extraction).Findings
1. Concurrency sweep (per-analysis latency vs. concurrent uploads)
* not failures — these exceeded the test's 4-minute poll cap; verified zero
FAILEDfiles. Backend CPU pegged ~6 cores throughout; memory (2–4 GB) was never the limiter. Failure mode is graceful degradation via queueing, not errors — until the queue overflows (see #451).Breaking point on this hardware: ~20 simultaneous analyses before latency exceeds a 4-min budget. This scales with cores; it is not a fixed ceiling.
2. The dominant bottleneck: Suricata (controlled isolation test)
Suricata alone is ~94% of per-file analysis time, because it reloads the full Emerging Threats ruleset on every
suricata -rinvocation — a fixed cost independent of pcap size. Disabling it is an ~18× throughput gain.3. Per-stage breakdown (from
AnalysisService.analyzeFiletiming logs, 538 KB file)Bottlenecks (ranked)
max-pool-size=10caps concurrent analyses regardless of available cores (backend/.../config/AsyncConfig.java). On a many-core host this leaves CPU idle.How to scale
Software (cheapest, biggest wins)
async.max-pool-sizeto physical cores (it's already@Value-backed) and document tuning.Hardware
Optimizations identified (summary)
Open questions / follow-up benchmarks
Related
🤖 Filed with Claude Code