# RunsReplicationService Error Fingerprinting Benchmark

This benchmark measures the performance impact of error fingerprinting in the RunsReplicationService.

## Overview

The benchmark:
1. Creates a realistic dataset of TaskRuns (7% with errors by default)
2. Runs the producer in a **separate process** to simulate real-world load
3. Measures replication throughput and Event Loop Utilization (ELU)
4. Compares performance with fingerprinting **enabled** vs **disabled**

## Architecture

```
┌─────────────────┐         ┌──────────────────────┐
│  Producer       │         │  Benchmark Test      │
│  (Child Process)│─────────│  (Main Process)      │
│                 │  IPC    │                      │
│  - Inserts      │         │  - RunsReplication   │
│    TaskRuns     │         │    Service           │
│    to Postgres  │         │  - ELU Monitor       │
│                 │         │  - Metrics           │
└─────────────────┘         └──────────────────────┘
         │                           │
         │                           │
         ▼                           ▼
    ┌──────────┐              ┌──────────────┐
    │ Postgres │              │  ClickHouse  │
    └──────────┘              └──────────────┘
```

## Files

- `runsReplicationBenchmark.test.ts` - Main benchmark test
- `runsReplicationBenchmark.producer.ts` - Producer script (runs in child process)
- `runsReplicationBenchmark.README.md` - This file

## Configuration

The benchmark can be configured via environment variables or by editing `BENCHMARK_CONFIG` in the test file:

```typescript
const BENCHMARK_CONFIG = {
  // Number of runs to create
  NUM_RUNS: parseInt(process.env.BENCHMARK_NUM_RUNS || "5000", 10),

  // Error rate (0.07 = 7%)
  ERROR_RATE: 0.07,

  // Producer batch size
  PRODUCER_BATCH_SIZE: 100,

  // Replication service settings
  FLUSH_BATCH_SIZE: 50,
  FLUSH_INTERVAL_MS: 100,
  MAX_FLUSH_CONCURRENCY: 4,

  // Timeout
  REPLICATION_TIMEOUT_MS: 120_000, // 2 minutes
};
```

## Running the Benchmark

### Quick Test (Small Dataset)

```bash
cd apps/webapp
BENCHMARK_NUM_RUNS=1000 pnpm run test ./test/runsReplicationBenchmark.test.ts --run
```

### Realistic Benchmark (Larger Dataset)

```bash
cd apps/webapp
BENCHMARK_NUM_RUNS=10000 pnpm run test ./test/runsReplicationBenchmark.test.ts --run
```

### High Volume Benchmark

```bash
cd apps/webapp
BENCHMARK_NUM_RUNS=50000 pnpm run test ./test/runsReplicationBenchmark.test.ts --run
```

**Note:** The benchmark is gated by the `BENCHMARKS_ENABLED` environment variable
(via `containerTest.skipIf`), so you don't need to edit the test file. Set
`BENCHMARKS_ENABLED=1` (and optionally `BENCHMARK_NUM_RUNS`) then run:

```bash
cd apps/webapp
BENCHMARKS_ENABLED=1 pnpm run test ./test/runsReplicationBenchmark.test.ts --run
```

## What Gets Measured

### 1. Producer Metrics
- Total runs created
- Runs with errors (should be ~7%)
- Duration
- Throughput (runs/sec)

### 2. Replication Metrics
- Total runs replicated to ClickHouse
- Replication duration
- Replication throughput (runs/sec)

### 3. Event Loop Utilization (ELU)
- Mean utilization (%)
- P50 (median) utilization (%)
- P95 utilization (%)
- P99 utilization (%)
- All samples for detailed analysis

### 4. OpenTelemetry Metrics
- Batches flushed
- Task runs inserted
- Payloads inserted
- Events processed

## Output

The benchmark produces detailed output including:

```
================================================================================
BENCHMARK: baseline-no-fingerprinting
Error Fingerprinting: DISABLED
Runs: 5000, Error Rate: 7.0%
================================================================================

[Producer] Starting - will create 5000 runs (7.0% with errors)
[Producer] Progress: 1000/5000 runs (2500 runs/sec)
...
[Producer] Completed:
  - Total runs: 5000
  - With errors: 352 (7.0%)
  - Duration: 2145ms
  - Throughput: 2331 runs/sec

[Benchmark] Waiting for replication to complete...

================================================================================
RESULTS: baseline-no-fingerprinting
================================================================================

Producer:
  Created: 5000 runs
  With errors: 352 (7.0%)
  Duration: 2145ms
  Throughput: 2331 runs/sec

Replication:
  Replicated: 5000 runs
  Duration: 3456ms
  Throughput: 1447 runs/sec

Event Loop Utilization:
  Mean: 23.45%
  P50: 22.10%
  P95: 34.20%
  P99: 41.30%
  Samples: 346

Metrics:
  Batches flushed: 102
  Task runs inserted: 5000
  Payloads inserted: 5000
  Events processed: 5000
================================================================================

[... Similar output for "with-fingerprinting" benchmark ...]

================================================================================
COMPARISON
Baseline: baseline-no-fingerprinting (fingerprinting OFF)
Comparison: with-fingerprinting (fingerprinting ON)
================================================================================

Replication Duration:
  3456ms → 3512ms (+1.62%)

Throughput:
  1447 → 1424 runs/sec (-1.59%)

Event Loop Utilization (Mean):
  23.45% → 24.12% (+2.86%)

Event Loop Utilization (P99):
  41.30% → 43.20% (+4.60%)

================================================================================

BENCHMARK COMPLETE
Fingerprinting impact on replication duration: +1.62%
Fingerprinting impact on throughput: -1.59%
Fingerprinting impact on ELU (mean): +2.86%
Fingerprinting impact on ELU (P99): +4.60%
```

## Interpreting Results

### What to Look For

1. **Replication Duration Delta** - How much longer replication takes with fingerprinting
2. **Throughput Delta** - Change in runs processed per second
3. **ELU Delta** - Change in event loop utilization (higher = more CPU bound)

### Expected Results

With a 7% error rate and SHA-256 hashing:
- **Small impact** (<5% overhead): Fingerprinting is well optimized
- **Moderate impact** (5-15% overhead): May want to consider optimizations
- **Large impact** (>15% overhead): Fingerprinting needs optimization

### Performance Optimization Ideas

If the benchmark shows significant overhead, consider:

1. **Faster hashing algorithm** - Replace SHA-256 with xxHash or MurmurHash3
2. **Worker threads** - Move fingerprinting to worker threads
3. **Caching** - Cache fingerprints for identical errors
4. **Lazy computation** - Only compute fingerprints when needed
5. **Batch processing** - Group similar errors before hashing

## Dataset Characteristics

The producer generates realistic error variety:

- TypeError (undefined property access)
- Error (API fetch failures)
- ValidationError (input validation)
- TimeoutError (operation timeouts)
- DatabaseError (connection failures)
- ReferenceError (undefined variables)

Each error template includes:
- Realistic stack traces
- Variable IDs and timestamps
- Line/column numbers
- File paths

This ensures the fingerprinting algorithm is tested with realistic data.

## Troubleshooting

### Benchmark Times Out

Increase the timeout:
```typescript
REPLICATION_TIMEOUT_MS: 300_000, // 5 minutes
```

### Producer Fails

Check Postgres connection and ensure:
- Docker services are running (`pnpm run docker`)
- Database is accessible
- Sufficient disk space

### Different Results Each Run

This is normal! Factors affecting variance:
- System load
- Docker container overhead
- Database I/O
- Network latency (even localhost)

Run multiple times and look at trends.

## Future Enhancements

Potential improvements to the benchmark:

1. **Multiple error rates** - Test 0%, 5%, 10%, 25%, 50% error rates
2. **Different hash algorithms** - Compare SHA-256 vs xxHash vs MurmurHash3
3. **Worker thread comparison** - Test main thread vs worker threads
4. **Concurrent producers** - Multiple producer processes
5. **Memory profiling** - Track memory usage over time
6. **Flame graphs** - Generate CPU flame graphs for analysis
7. **Historical tracking** - Store results over time to track regressions
