|
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / Classify release event (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
* fix(scope-resolution): link Record graph nodes Register Record definitions and caller anchors so Java and C# record targets and initializer sources resolve to canonical nodes. Keep generated LadybugDB relation pairs and the production benchmark baseline synchronized. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(bench): harden schema-pair production gate Derive the production count from executable DDL, fail closed when its budget is missing, and independently pin Record-to-Property coverage. Keep benchmark evidence machine-scoped and correct stale schema counts. --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Gergő Magyar <gergomagyar@icloud.com> |
||
|---|---|---|
| .. | ||
| baselines.json | ||
| measure.mjs | ||
| README.md | ||
Schema pair-set bench (#2793)
What a bigger CodeRelation FROM/TO pair set costs at query time, measured
against a real @ladybugdb/core database.
# from gitnexus/
node --import tsx bench/schema-pairs/measure.mjs # print one JSON line per size + a summary
node --import tsx bench/schema-pairs/measure.mjs --check # gate vs baselines.json
Why it exists
src/core/lbug/schema.ts generates its relation pairs from two cross products,
and declines to add a third one on the strength of a number — roughly 1.04×
near production's pair count, 1.6× at 786, 2.1× at 1024. That measurement used
to live in a scratch directory, so nobody proposing a third rule could re-run
it. This harness is that measurement, committed — and it reproduces those
figures.
Run it before widening a rule, and quote the new ratio in the review.
Observed on the reference box, four runs (ratios vs the 332-pair list):
| pairs | untyped | typed (floor) |
|---|---|---|
| 332 | 1.00× | 1.00× |
| 450 | 0.93–1.05× | 0.98–1.17× |
| 641 | 1.22–1.43× | 1.11–1.23× |
| 786 | 1.52–1.75× | 1.19–1.31× |
| 1024 | 2.03–2.34× | 1.31–1.57× |
Production's former 450-pair surface came out faster than 332 on three of the four runs, so at this size the pair count is inside run-to-run noise. Everything past ~640 is not.
#2801 remeasured the new 461-pair production surface on Windows six times:
| run | untyped ratio | typed ratio | interpretation |
|---|---|---|---|
| 1 | 1.101× | 1.157× | noise-dominated (typed > untyped) |
| 2 | 1.324× | 1.122× | below the operational budget |
| 3 | 2.705× | 1.089× | exceeds the operational budget |
| 4 | 1.417× | 1.065× | below the operational budget |
| 5 | 1.196× | 1.050× | below the operational budget |
| 6 | 1.585× | 2.577× | noise-dominated (typed > untyped) |
The three comparable Windows runs below the 1.5× operational ceiling span
1.20–1.42× untyped / 1.05–1.12× typed. Run 3 is published rather than
silently discarded: no pre-registered rule excludes it, and --check would
correctly reject it. These Windows measurements are not combined with the
historical reference-box rows to infer cross-size ordering.
Quote the range, not a single run — one run is not evidence here.
What it measures
For each pair-set size it builds a fresh database with all 32 node tables, a
CodeRelation table declaring exactly that many FROM/TO pairs, and identical
data, then times two query shapes over 40 anchors × 15 reps (median):
untyped_ms_<size>—MATCH (a {id: $id})-[r:CodeRelation]->(b). Neither endpoint is labelled, so LadybugDB must treat every declared pair as a candidate. This is the shapeimpact,contextanddetect_changesissue when they walk out from one node id, and the only one whose plan depends on how many pairs the table declares.typed_ms_<size>—MATCH (a:Function {…})-[r]->(b:Function), the lower bound. Both endpoints labelled prunes the plan to a single pair, so this was expected to be flat in the pair count. It is not — up to 1.17× at 450 and 1.57× at 1024 — so a declared-but-unused pair costs something even when the planner never considers it.typed_ratio_*is therefore the floor, not a noise control; the real cost of widening sits between it andratio_*. A run wheretyped_ratiomoves more thanratiois noise-dominated and should be rerun.ratio_<size>—untyped_ms_<size> / untyped_ms_332. The production-size ratio is the figureschema.tsquotes.
Sizes
The pair set is a prefix of a fixed 32×32 (NODE_TABLES²) enumeration, so each
size is a strict superset of the smaller ones. The four pairs the synthetic data
uses are pinned to the front, so the same rows are reachable by the same query
at every size — the only variable is how many unused pairs are declared. The
harness fails if the row counts ever differ across sizes.
| size | what it is |
|---|---|
| 332 | the pre-#2792 hand-written list — the reference for every ratio |
| 450 | production before Record became linkable (#2801) |
| 461 | production today (two cross products + 69 hand-declared pairs) |
| 641 | the third cross product schema.ts defers (DEFINITION_ANCHOR_LABELS × {CodeElement, Section, Typedef, Union, Namespace, Impl, TypeAlias, Static, Template}), which would leave ~29 hand-declared lines |
| 786 | the size an earlier revision of that comment attributed to the third rule — it is 641; kept as a measured waypoint |
| 1024 | the full cross product, the ceiling |
Correctness gate
Before timing anything, the harness round-trips the real SCHEMA_QUERIES
through a real database and asserts that CALL SHOW_CONNECTION('CodeRelation')
reports exactly the pairs parseRelationSchemaPairs finds in RELATION_SCHEMA.
No production-size magic number is baked in: the measured production size and
budget key are derived from that parsed DDL count. A missing ratio_<size>_budget
entry makes --check fail closed. The invariant is that the DDL LadybugDB
accepted carries the pair set our own parser believes it declares. The
absolute count is reported as declared_pairs. A pair declared twice would not
reach this check at all — LadybugDB rejects the CREATE REL TABLE outright,
which is why a duplicate kills every analyze rather than one repository's.
What it does NOT measure
- Ingest /
COPYcost. Pair-set size also multiplies the number of per-pair CSVs the emitter routes to (src/core/lbug/rel-pair-routing.ts); that cost is covered bybench/emit-persistence. - At-scale absolute numbers. Row counts here are small and deliberately constant. The ratios are the signal; the milliseconds are box-specific.
Regenerating the baseline
baselines.json holds one production-size budget — the ceiling on what
production's own pair count may cost relative to the 332-pair hand-list it
replaced. Re-run without --check several times and copy the top of the
observed production-size ratio range plus headroom — the spread between runs on
this box is wider than the effect being measured near production, so a single
run cannot set it. Publish the raw ratios and apply only the pre-registered
typed_ratio > ratio noise rule; do not silently discard another run to make a
budget pass.