# 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.