How Purple8 Scales
This page answers the genuine question directly: can this system hold up at the scale of real-world healthcare networks, manufacturing plants, insurance operations, and government agencies — or does it fall apart?
Short answer: it scales to large enterprise production workloads today. It is not yet a hyperscaler. Here is exactly what it handles, where the limits sit, and what is on the roadmap.
What a single node handles today
A single Purple8 node on commodity server hardware (m6i.2xlarge, 8 vCPU, 32 GB RAM, NVMe SSD):
| Workload | Number |
|---|---|
| Nodes in the graph | ~500M (with int8 vector quantization) |
| Edges | Multiple billions (edge storage is lightweight — ~50 bytes/edge) |
| Graph traversal queries (3-hop MATCH) | 4,120 QPS at P99 5.9 ms |
| Hybrid search (BM25 + vector + graph) | 1,847 QPS at P99 8.7 ms |
| Journey Engine transitions/sec | ~12,000/sec |
| Ingestion (pre-extracted entities) | ~48,000 nodes+edges/sec |
| Memory for 1M nodes, 768-dim vectors | 0.9 GB (binary quantization) |
For context: a mid-size hospital network has roughly 5–10 million patient-journey events per year. A national insurer handles 2–4 million claims per year. A large manufacturer runs 500K–2M production orders per year. A single Purple8 node absorbs all of these with headroom.
Start on a single machine
The default GraphEngine runs in-process. No Docker required, no separate server to manage. Install, import, query.
from purple8_graph import GraphEngine
engine = GraphEngine("./data")
engine.add_node("Document", {
"title": "Q4 Report",
"body": "Revenue grew 34% year-on-year...",
})
results = engine.query("""
CALL db.vector.search('Document', $vec, 5)
YIELD node, score
RETURN node.title, score
""", vec=my_embedding)This is production-ready for most enterprise workloads. Many teams run Purple8 this way for months or years before they need to scale out. The embedded storage handles hundreds of millions of nodes and billions of edges on a single NVMe drive.
Scale to a multi-shard cluster
When your dataset grows beyond a single machine — or you need higher write throughput — promote to ShardedGraphEngine. The API is identical.
from purple8_graph.distributed import ShardedGraphEngine, HashPartitioner
engine = ShardedGraphEngine(
shard_configs=[
{"path": "./data/shard-0"},
{"path": "./data/shard-1"},
{"path": "./data/shard-2"},
],
partitioner=HashPartitioner(),
)
# Same Cypher — the engine federates across shards automatically
results = engine.query("""
MATCH (i:JourneyInstance {entity_id: $id})-[t:ADVANCED_TO]->()
RETURN t.from_stage, t.to_stage, t.actor, t.timestamp
ORDER BY t.timestamp
""", id="P-44821")Queries are automatically federated: Purple8 issues them in parallel across all shards and merges the results. Write throughput scales linearly with shard count — a 3-shard cluster delivers roughly 3× the journey transition throughput of a single node (~36,000/sec). You don't rewrite a single line of application code.
Add fault tolerance with Raft
For production systems that cannot afford data loss or downtime, wrap shards in Raft consensus groups. Each shard gets 3 or 5 replicas. The cluster survives the loss of any minority of nodes. Leader election completes within 300 ms.
from purple8_graph.distributed import RaftShardedEngine, HashPartitioner
engine = RaftShardedEngine(
shard_groups=[
{
"replicas": [
{"host": "node-0a.internal", "port": 9010, "path": "/data/s0"},
{"host": "node-0b.internal", "port": 9010, "path": "/data/s0"},
{"host": "node-0c.internal", "port": 9010, "path": "/data/s0"},
]
},
{
"replicas": [
{"host": "node-1a.internal", "port": 9010, "path": "/data/s1"},
{"host": "node-1b.internal", "port": 9010, "path": "/data/s1"},
{"host": "node-1c.internal", "port": 9010, "path": "/data/s1"},
]
},
],
partitioner=HashPartitioner(),
election_timeout_ms=300,
)Writes require a quorum commit (majority of replicas acknowledge before returning success). Reads from the leader are always consistent. Use read_consistency="eventual" for high-volume read paths that can tolerate replica lag.
Spread across data centres
Assign replicas to different availability zones or regions using the zone tag. The Raft leader-election algorithm is zone-aware — it avoids electing a leader in a zone that already holds a majority of replicas, so a full AZ outage doesn't take down your cluster.
engine = RaftShardedEngine(
shard_groups=[
{
"replicas": [
{"host": "us-east-1a.db.internal", "port": 9010, "path": "/data/s0", "zone": "us-east-1a"},
{"host": "us-east-1b.db.internal", "port": 9010, "path": "/data/s0", "zone": "us-east-1b"},
{"host": "eu-west-1a.db.internal", "port": 9010, "path": "/data/s0", "zone": "eu-west-1a"},
]
},
],
partitioner=HashPartitioner(),
election_timeout_ms=500, # slightly higher for cross-region latency
)Scaling path at a glance
| Stage | Configuration | Nodes | Journey transitions/sec | Fault tolerant? |
|---|---|---|---|---|
| Development | GraphEngine | 1 | ~12,000 | WAL durability |
| Large production (single machine) | GraphEngine + DiskANN | 1 | ~12,000 | WAL durability |
| Scale-out | ShardedGraphEngine (3 shards) | 3 | ~36,000 | No (single replica per shard) |
| High availability | RaftShardedEngine (3×3) | 9 | ~36,000 | Yes — minority failure tolerance |
| Multi-region | RaftShardedEngine + zones | 9+ | ~36,000+ | Yes — full AZ failure tolerance |
What stays identical at every stage
This is the point that matters most for long-lived production systems:
- Cypher queries — not a single line changes between laptop and 9-node cluster
- Python SDK —
engine.query(...),engine.add_node(...),journey_engine.advance(...) - REST and GraphQL endpoints — same paths, same auth, same payloads
- KMS encryption — config carries through to every shard and replica
- Journey Engine — SLA monitoring, HITL queues, CDC events, AI advisor — all continue firing across the cluster unchanged
- MCP server — Claude and Cursor query the cluster the same way they query a single node
No migration scripts. No re-indexing. No schema changes. Add nodes to your cluster config and you're done.
Where the real limits are — honestly
Purple8 is production-grade for large enterprise workloads. It is not (yet) a hyperscaler. Here is where the real boundaries sit:
Cross-shard graph traversal
When a multi-hop query crosses shard boundaries — node A is on shard 0, its neighbour is on shard 1 — the FederatedQueryEngine resolves this with a second scatter-gather pass. The latency cost is real: a 3-hop query that crosses shards twice may take 15–25 ms instead of 2–5 ms.
Mitigation: Use LabelPartitioner to co-locate related node types on the same shard. For healthcare, Patient, JourneyInstance, and all their journey edges live on the same shard — cross-shard hops are rare in practice.
partitioner=LabelPartitioner({
"Patient": 0,
"JourneyInstance": 0,
"ClinicalEvent": 0,
"Document": 1,
"Staff": 1,
"Organisation": 2,
})Deep traversals on very large graphs & Built-in DoS Protection
Unbounded multi-hop queries (-[*]-> with no depth limit) on graphs with hundreds of millions of nodes and high average degrees can easily behave like a Denial of Service (DoS) attack, causing explosive memory usage and CPU saturation as the query fan-out explodes.
Mitigation & Hard Limits: Purple8 now natively protects clusters against graph-expansion DoS attacks. By default, MAX_TRAVERSE_DEPTH = 10 acts as a hard ceiling on all queries and internal operations. Any query attempting a BFS traversal past 10 hops returns the truncated valid 10-hop graph bounds instead of freezing the cluster.
If you specifically require deeper analytics, always bound traversals cautiously with LIMIT. You can also adjust P8G_MAX_TRAVERSE_DEPTH cautiously via environment configurations, but the 10-hop cap natively isolates tenant queries and guarantees system resilience.
Global aggregations at massive scale
COUNT(*) or SUM() across a billion edges is a full-table scan. On a multi-shard cluster this is parallelised, but it still takes seconds, not milliseconds, at extreme scale.
Mitigation: Materialise frequently-needed aggregations as graph nodes using CDC. When an SLA_BREACHED edge is written, a background handler increments a counter node rather than re-scanning the full edge set at query time.
Single-cluster upper bound
The current architecture scales to roughly 50 shards before federation overhead outweighs parallelism gains. In practice, 50 shards with 5 Raft replicas each — 250 nodes — handles several billion nodes and trillions of edges. Very few organisations on earth have workloads that exceed this today.
What's on the roadmap
The current architecture handles every healthcare, manufacturing, insurance, and government workload described in the use cases. What is in active development for the next scale tier:
| Capability | Status |
|---|---|
| Raft-native shard rebalancing (zero-downtime shard split) | In development |
| Distributed HNSW index sharding | In development |
| Streaming ingestion from Kafka / Kinesis natively | Planned |
| Read replica auto-scaling (serverless replica pool) | Planned |
| Cross-shard traversal cost-based optimiser | Planned |
The core design principle — that the graph, vector index, journey engine, and CDC bus are one system — does not change as these capabilities land. The complexity that a distributed system usually pushes onto application code stays inside Purple8.
The honest comparison
Most databases that claim to handle this class of workload require you to:
- Run a separate vector database
- Run a separate graph database
- Build ETL pipelines between them
- Run a separate workflow orchestration layer (Temporal, Airflow)
- Build a separate audit log
- Manage the operational complexity of 4+ distributed systems
The scaling challenge for those architectures is not just data volume — it is the compounding operational overhead of keeping multiple distributed systems in sync, monitored, and healthy. Purple8 replaces all of them with one system that you scale once, operate once, and monitor in one place.
Further reading
- Sharding & Clustering reference — partitioning strategies, Raft config, Docker Compose cluster setup, consistency model
- Benchmarks — measured numbers: QPS, P99 latency, memory footprint, ingestion throughput
- System Requirements — hardware sizing guidance per tier