# ordinis-bench

JMH microbenchmarks для hot paths Ordinis. Не deployable artifact —
только для ad-hoc и CI manual runs.

## Цель

Catch регрессии алгоритмической сложности на критичных горячих путях:

- **Schema traversal** в `LineageIndexService.findSchemaDependents` —
  iterate properties, find x-references, parse каждый.
- **`ReferenceValidator.parseRef`** — schema reference parsing (вызывается
  на bundle import + per-record CRUD validation).
- **`OnCloseAction.fromString`** — case-insensitive enum lookup внутри parseRef.

## Когда обновлять baseline

Базовые цифры (M3 MacBook Pro, JDK 25, single thread, 1 fork × 5 measurements):

| Benchmark | ns/op | Notes |
|---|---|---|
| `onCloseAction_null` | < 1 | trivial null branch |
| `onCloseAction_block` | ~5 | first enum match |
| `onCloseAction_cascade` | ~12 | third enum match (linear values() scan) |
| `parseRef_simple` | ~20 | без property schema |
| `parseRef_withSchemaButNoOnClose` | ~25 | + JsonNode lookup, no on-close |
| `parseRef_withOnClose` | ~93 | + on-close parsing |
| `schemaTraversal_findRefs` (40 props) | ~417 | ~83 ns per ref |

Если PR делает любой из этих в 2x медленнее — investigate.
Cumulative budget for `findSchemaDependents` на 40 dicts × 40 props × 5 refs:
40 * 417 ns ≈ 17 µs. p99 endpoint SLO = 200 ms — запас 4 порядка.

## Run locally

Build uber-jar (~12 sec, includes shade):

```bash
mvn -P bench -pl ordinis-bench -am package -DskipTests
```

Run all benchmarks (~5-7 min total):

```bash
java -jar ordinis-bench/target/benchmarks.jar
```

Filter by name:

```bash
java -jar ordinis-bench/target/benchmarks.jar Lineage
java -jar ordinis-bench/target/benchmarks.jar parseRef
```

Quick smoke (1 fork, 1 warmup, 2 measurements × 1 sec — finishes in ~10 sec):

```bash
java -jar ordinis-bench/target/benchmarks.jar -f 1 -wi 1 -i 2 -w 1 -r 1 Lineage
```

Profile с GC:

```bash
java -jar ordinis-bench/target/benchmarks.jar -prof gc Lineage
```

JSON output для дальнейшего парсинга:

```bash
java -jar ordinis-bench/target/benchmarks.jar -rf json -rff bench-results.json
```

## Run в CI

Bench job — **manual trigger only** через `web` source в GitLab.
По default не fire'ит на push (slow + noisy в CI history).

Trigger:
1. https://git.nstart.cloud/2-6/2-6-4/terravault/ordinis/-/pipelines/new
2. Variable: `RUN_BENCH=true`
3. Run pipeline

Output: artifact `bench-results.json` + `bench-results.txt` (5 day expire).

## Adding new benchmark

1. Создать класс в `cloud.nstart.terravault.ordinis.bench` package.
2. Annotate с `@BenchmarkMode(Mode.AverageTime)` + `@OutputTimeUnit(TimeUnit.NANOSECONDS)`.
3. Добавить `@Warmup`, `@Measurement`, `@Fork`, `@State(Scope.Benchmark)`.
4. JMH annotation processor автоматически генерирует runner classes на compile.
5. Update baseline numbers в этом README после первого fork × 5 run'а.

## Why JMH and not k6 / wrk

- JMH = JVM-level microbenchmarks. Catches regressions в алгоритмах
  (parseRef становится O(n²), regex compilation per call, allocation hot spot).
- k6 / wrk = HTTP load tests. Тестируют end-to-end SLO — это другой layer
  (см. Phase 4 Prometheus alerts: `OrdinisDependentsLatencyHigh`,
  `OrdinisCascadeCloseTimeouts`).
- Оба нужны: JMH для hot paths сразу при PR review, k6 для periodic SLO
  validation против staging/prod.
