---
title: Capacity planning
---

This guide helps you size Deephaven deployments for optimal performance based on workload characteristics, data volumes, and user requirements. Proper capacity planning ensures you provision adequate resources without over-provisioning, balancing performance and cost.

> [!NOTE]
> This guide focuses on **performance-oriented capacity planning**. For high availability, disaster recovery, and resilience planning, see [Resilience planning](../architecture/resilience-planning/resilience-planning-overview.md).

## Capacity planning methodology

Effective capacity planning follows a structured approach:

1. **[Define workload characteristics](#define-workload-characteristics)**: Data volumes, query patterns, user concurrency
2. **[Estimate resource requirements](#estimate-resource-requirements)**: CPU, memory, storage, network
3. **[Validate with testing](#validation-and-testing)**: Load test with representative workloads
4. **[Plan for growth](#growth-planning)**: Monitor trends and set scaling triggers

### Define workload characteristics

Characterize your workload across these dimensions:

**Data characteristics (aggregate across all tables):**

- **Ingest rate**: Total rows per second across all tables
- **Row size**: Average bytes per row (varies by table)
- **Table count**: Number of actively ingested tables
- **Data types**: Proportion of numeric vs. string vs. complex types
- **Retention**: Days of intraday data before merging to historical

**Query characteristics:**

- **Concurrent users**: Users running queries _and_ users viewing/subscribing to query results (each consumer adds load)
- **Query complexity**: Simple aggregations vs. complex joins vs. custom operations
- **Active workers**: Concurrent PQs and Code Studios per user
- **Tracked rows**: Rows tracked per active query
- **Interactive vs. batch**: Real-time dashboards vs. scheduled reports

**Access patterns:**

- **Historical data access**: Percentage of queries accessing historical vs. intraday data
- **Data scanning**: Typical date ranges queried (1 day, 1 week, 1 year)
- **Update frequency**: How often tables update (tick-by-tick, 1-second snapshots, etc.)

### Estimate resource requirements

Use the following formulas as starting points, then validate with testing. The sections below cover [CPU](#cpu-capacity-planning), [memory](#memory-capacity-planning), [storage](#storage-capacity-planning), and [network](#network-capacity-planning) sizing.

## CPU capacity planning

### Query host CPU sizing

Workers use all available CPUs on the host. When sizing a query host, estimate total cores needed:

**Formula:**

```
Host cores = (Update parallelism × Concurrent workers) + Background threads + Headroom
```

**Components:**

- **Update parallelism**: Varies by table complexity and update frequency
- **Concurrent workers**: Peak number of active workers on the host
- **Background threads**: Additional cores for updates, GC, and maintenance per worker
- **Headroom**: Reserve capacity for burst loads

> [!NOTE]
> CPU requirements vary significantly based on query complexity, data types, and update frequency. There are no universal estimates — always validate with load testing using your specific workload.

See [CPU optimization](./cpu-optimization.md) for tuning strategies.

### DIS CPU sizing

**Data Import Server (DIS):**

DIS CPU requirements depend heavily on row complexity, column count, data transformations, and disk throughput.

**To estimate your requirements:**

1. Deploy a test DIS with representative data schemas and ingestion rates.
2. Measure CPU utilization under expected load.
3. Scale based on observed utilization, leaving headroom for burst capacity.

> [!NOTE]
> CPU requirements vary significantly by workload. There are no universal estimates — always validate with load testing using your specific data.

## Memory capacity planning

### Heap sizing

Heap sizing depends on query complexity, table sizes, and concurrent operations. Key factors include:

- **Query working set**: Tables actively being processed
- **Table caches**: Cached metadata and frequently accessed data
- **Overhead**: JVM internals, garbage collection headroom

> [!NOTE]
> Memory usage varies significantly by data type (primitives: 8 bytes, strings: variable, objects: variable) and Deephaven's internal structures.

**Starting point:**

The default heap for new Code Studios and Persistent Queries is controlled by `RemoteProcessingRequest.defaultQueryHeapMB`. Check your environment's configured default and adjust based on workload.

**How to adjust:**

1. **Start a PQ or Code Studio** with the default heap size.
2. **Run your typical workload** and monitor heap usage using:
   - The **heap indicator** in the Code Studio toolbar (shows current usage, free, and max).
   - Or query `ProcessMetricsLogCoreV2` for heap metrics:
     ```python
     heap_metrics = (
         db.live_table("DbInternal", "ProcessMetricsLogCoreV2")
         .where(["Date = today()", "Name.startsWith(`Memory-Heap`)"])
         .view(["Timestamp", "ProcessInfoId", "Name", "Last", "Max"])
     )
     ```
     ```groovy
     heapMetrics = db.liveTable("DbInternal", "ProcessMetricsLogCoreV2")
         .where("Date = today()", "Name.startsWith(`Memory-Heap`)")
         .view("Timestamp", "ProcessInfoId", "Name", "Last", "Max")
     ```
3. **Increase heap** if you see OOM errors or heap usage consistently near max after GC.
4. **Decrease heap** if usage after GC is consistently below 50% (frees resources for other workers).

See [Controlling query worker heap size](../pq-controller/worker-heap-size.md) for configuration details.

**Direct memory sizing:** When using direct memory for data buffers, the data buffer pool can be configured up to 2× heap size. Size `-XX:MaxDirectMemorySize` to accommodate the data buffer pool plus overhead for network buffers.

### System memory sizing

**Formula:**

```
System RAM = Heap + Direct memory + OS overhead + Additional headroom

OS overhead ≈ 4-8 GB
```

Any RAM not used by processes becomes available to the OS page cache, improving I/O performance. Leave headroom beyond process requirements to benefit from caching.

**Example:**

- Heap: 32 GB
- Direct memory: 16 GB
- OS overhead: 8 GB
- **Minimum system RAM: 56 GB → provision 64 GB or more**

The extra ~8 GB becomes available for OS page cache.

See [Memory management](./memory-management.md) for tuning strategies.

## Storage capacity planning

### Storage capacity sizing

**Intraday storage:**

```
Intraday capacity = Daily data volume × Retention days × Growth factor
```

> [!NOTE]
> The growth factor, compression ratios, and overhead multipliers in these examples are illustrative only. Actual values vary significantly based on your data characteristics, storage format, and access patterns. Always measure with representative data to determine accurate sizing for your environment.

Example (illustrative): 100 GB/day × 3 days × 1.5 (growth factor) = 450 GB per namespace

**Historical storage:**

```
Historical capacity = (Daily volume × Days retained × Compression ratio) + Overhead
```

Compression ratios vary widely based on data characteristics (cardinality, repetition, data types). As a rough guide:

- **Deephaven native**: ~1.0× (uncompressed)
- **Parquet SNAPPY**: 0.3× to 0.5× typical, but can range from 0.1× (highly compressible) to 0.8× (low cardinality strings, random data)

Example (illustrative): 100 GB/day × 365 days × 0.4 (compression) × 1.2 (overhead) = 17.5 TB

### Storage performance sizing

IOPS and throughput requirements vary significantly based on:

- **Data ingestion rate**: Higher row rates require more write IOPS/throughput.
- **Query patterns**: Concurrent queries increase read demands.
- **Column count and types**: Wide tables with strings require more I/O than narrow numeric tables.
- **Merge workloads**: Merge operations require sustained read+write throughput.

**To estimate your requirements:**

1. Measure baseline I/O with representative test data using tools like `iostat` or `iotop`.
2. Scale estimates based on expected production data volumes.
3. Add headroom (20-30%) for burst capacity.

See [Storage tuning](./storage-tuning.md) for optimization strategies.

## Network capacity planning

### Network traffic sources

Network bandwidth requirements depend on the sum of all traffic sources:

- **Data ingestion**: Inbound data from external sources (Kafka, binary logs, etc.)
- **TDCP traffic**: Data served to workers via TDCP from DIS
- **NFS/S3 storage**: Historical data access over network-attached storage
- **etcd**: Cluster coordination traffic (frequent small packets)
- **Inter-process communication**: Controller, dispatchers, workers, web services

**To estimate requirements:**

1. Measure baseline traffic with tools like `iftop`, `nethogs`, or switch port statistics.
2. Identify peak periods (market open, batch jobs, etc.).
3. Add headroom (2-3×) for burst capacity.

> [!NOTE]
> Network requirements vary significantly by deployment. Measure actual traffic patterns rather than relying on estimates.

## Validation and testing

### Capacity testing procedure

1. **Load test with synthetic data:**
   - Generate representative data volumes and schemas.
   - Simulate peak concurrent user load.
   - Measure resource utilization (CPU, memory, I/O, network).

2. **Identify bottlenecks:**
   - Which resource reaches capacity first?
   - What is headroom at peak load?
   - Are there queuing delays or timeouts?

3. **Measure key metrics:**
   - Query response time (p50, p95, p99) — see [`QueryPerformanceLogCoreV2`](../internal-tables/query-performance-log.md)
   - Data ingestion lag
   - GC pause times and frequency
   - Storage I/O wait
   - Network utilization

4. **Adjust and retest:**
   - Increase constrained resources.
   - Validate improvement.
   - Test edge cases (burst load, data spikes).

### Capacity headroom guidelines

Maintain headroom for growth and burst capacity.

**How to establish headroom:**

1. **Measure baseline utilization** during normal and peak operations:
   - CPU: `htop` or `top`
   - Memory: `free -h` and heap metrics from `ProcessMetricsLogCoreV2`
   - Storage: `iostat -xz 5`
   - Network: `iftop` or switch port statistics
2. **Note your typical peak** — this becomes your baseline.
3. **Plan to scale** when sustained utilization consistently exceeds your baseline with limited headroom for bursts.

## Growth planning

### Scaling triggers

Plan to scale when you observe:

- **CPU:** Sustained high utilization during peak hours relative to your baseline
- **Memory:** Frequent full GC cycles, growing GC pause times, or OOM errors
- **Storage:** Sustained high I/O utilization or nearing capacity
- **Network:** Sustained high utilization or increasing latency

### Scaling strategies

| Strategy                   | Pros                                    | Cons                                  |
| -------------------------- | --------------------------------------- | ------------------------------------- |
| **Vertical (scale up)**    | Simpler, better for single-worker loads | Hardware limits, requires downtime    |
| **Horizontal (scale out)** | Near-unlimited scaling, incremental     | More complex, requires load balancing |

## Quick reference checklist

1. ✅ Define workload: Data ingestion rate, concurrent users, query patterns
2. ✅ Estimate CPU: Host cores based on query parallelism and concurrency
3. ✅ Estimate memory: Heap and direct memory based on working set size
4. ✅ Estimate storage: Capacity (intraday + historical) and performance
5. ✅ Estimate network: Bandwidth for ingestion, queries, and storage access
6. ✅ Validate with testing: Load test with representative workload
7. ✅ Measure headroom: Ensure 20-40% capacity above peak load
8. ✅ Plan for growth: Monitor trends and set scaling triggers

## Related documentation

- [Performance tuning overview](./overview.md)
- [CPU optimization](./cpu-optimization.md)
- [Memory management](./memory-management.md)
- [Storage tuning](./storage-tuning.md)
- [Scaling to multiple servers](../architecture/scaling.md)
- [Resilience planning](../architecture/resilience-planning/resilience-planning-overview.md)
