# Scaling

Horizontal and vertical scaling strategies for FluxiQ PIX to meet TPS targets.

## Prerequisites

- Understanding of current load patterns
- Access to Kubernetes cluster or infrastructure management
- Monitoring configured to measure baseline performance

## TPS Targets

| Target | Value | Pods | Pool per Repo |
|--------|-------|------|--------------|
| Development | ~100 TPS | 1 | 10 |
| Staging | ~500 TPS | 2 | 50 |
| Production | 5,000 TPS | 4-8 | 200 |
| Burst | 7,500 TPS | 8-12 | 250 |

## Horizontal Scaling

### Backend Pods

```bash
# Manual scaling
kubectl scale deployment pix-backend -n pix --replicas=4

# Horizontal Pod Autoscaler
kubectl autoscale deployment pix-backend -n pix \
  --min=2 --max=8 --cpu-percent=70
```

Each pod runs all 4 Elixir applications (shared, dict, spi, settlement). NATS workers distribute load automatically via consumer groups.

### Connection Pool Impact

When scaling pods, ensure the database can handle the total connections:

```
Total connections = POOL_SIZE x 4 repos x num_pods
```

| Pods | POOL_SIZE | Total Connections | Required max_connections |
|------|-----------|-------------------|------------------------|
| 2 | 200 | 1,600 | 1,800+ |
| 4 | 200 | 3,200 | 3,500+ |
| 8 | 200 | 6,400 | 7,000+ |

## Vertical Scaling

### Kubernetes Resources

```yaml
resources:
  requests:
    cpu: "2"
    memory: 2Gi
  limits:
    cpu: "4"
    memory: 4Gi
```

For higher TPS, increase limits:

```yaml
resources:
  requests:
    cpu: "4"
    memory: 4Gi
  limits:
    cpu: "8"
    memory: 8Gi
```

### BEAM VM Tuning

The Erlang VM benefits from scheduler configuration:

```bash
# Set in release environment
export ERL_FLAGS="+S 8:8 +sbwt very_long +swt very_low"
```

## NATS Worker Scaling

Workers scale with pods automatically. Tuning per worker:

| Parameter | Low Load | High Load |
|-----------|----------|-----------|
| `batch_size` | 50 | 100 |
| `poll_interval` | 500ms | 200ms |
| `max_concurrency` | 5 | 10 |

Adaptive backpressure adjusts `poll_interval` automatically:
- Full batch (100 messages): min interval (50ms)
- Empty batch (0 messages): max interval (2000ms)
- Partial batch: configured interval

## Database Scaling

### Read Replicas

For read-heavy workloads, add PostgreSQL read replicas:

```elixir
# config/runtime.exs
config :shared, Shared.Repo.Replica,
  hostname: System.get_env("DB_REPLICA_HOST"),
  pool_size: 100
```

### Connection Pooling (PgBouncer)

For very high connection counts, add PgBouncer:

```ini
[databases]
monetarie = host=10.140.241.2 port=5432 dbname=monetarie

[pgbouncer]
pool_mode = transaction
max_client_conn = 10000
default_pool_size = 200
```

### Table Partitioning

For tables exceeding 100M rows, consider partitioning `monetarie_spi.messages` by date:

```sql
-- Future migration
ALTER TABLE monetarie_spi.messages
  PARTITION BY RANGE (inserted_at);
```

## Redis Scaling

Redis handles rate limiting, blacklist, and idempotency. For high TPS:

| Configuration | Value |
|---------------|-------|
| `maxmemory` | 4-8 GB |
| `maxmemory-policy` | `allkeys-lru` |
| Cluster mode | Enable for > 10K TPS |

## Load Testing

```bash
# K6 load test — 5K TPS sustained
k6 run --vus 500 --duration 5m k6/load-5k-tps.js

# K6 phase 12 validation
k6 run k6/phase12-validation.js
```

## Expected Outcome

After implementing scaling:

- Backend pods auto-scale between 2 and 8 based on CPU utilization
- Database connection pool sized for the number of pods
- NATS workers distributing load across pods
- Adaptive backpressure maintaining throughput under load
- 5,000 TPS sustained with 4+ pods
