SebastianGruza commented on PR #2994: URL: https://github.com/apache/hugegraph/pull/2994#issuecomment-5562353646
**Measurement of head `ac641c6` against `origin/master` `36811483`, 2026-09-06** — follow-up to my 2026-09-03 comment, this time on #2994 alone: the head contains current master, so `pr2994` = master + this PR and nothing else (#3184 was added only in the variant used for the hstore axis). Same method: the same REST/Gremlin queries against hstore (PD + 3 store nodes) and against rocksdb built from the same tree, compared as sets of element ids. Raw reports, plans and reproduction commands: [hugegraph-validation, tag `pr-2994-2026-09-06`](https://github.com/SebastianGruza/hugegraph-validation/blob/pr-2994-2026-09-06/reports/pr-2994/README.md). Independent work, not affiliated with the project. **1. Backend axis (hstore vs rocksdb, same server code).** 174 suite cases plus 155 new shapes taken from this review (barrier-like steps, mixed-key `or()`, child traversals in both directions, point lookups, SEARCH, element-changing steps, `~page`). On `pr2994` + #3184: **170 OK, 4 BOTH-ERR** (REST string range predicates, same as master), no MISMATCH; the new shapes **92/92 OK**, `explain()` plans 20/20 identical. The only difference between the variant with and without #3184 is one #3090 shape. **2. Version axis (rocksdb master vs rocksdb pr2994).** Suite: 168 OK and exactly two cases where master throws and the PR answers (the same `without()` cases as on 2026-09-03). In the new shapes the PR changes the result of 18 queries, and every one of them is the same defect on master: `score` is range-indexed on `person` only; 60 of the 300 `robot` vertices have `score >= 40`. | Query | master | pr2994 | |---|---|---| | `g.V().has('score',gte(40)).hasLabel(neq('person'))` | 60 | 60 | | same with `limit(100000)`, `skip(0)`, `range(0,100000)`, `aggregate('x')`, `coin(1.0)` or `barrier()` between the property and the label | **0** | 60 | | `g.V().has('score',gte(40)).not(hasLabel('person'))`, also with `limit()` | **0** | 60 | | `g.V().has('score',gte(40)).where(__.not(__.hasLabel('person')))`, also with `limit()` | **0** | 60 | | `g.V('a').union(__.V().has('score',gte(40))).hasLabel(neq('person'))`, `flatMap`, `repeat().times(1)` | **0** | 60 | | `g.V().has('score',gte(40)).or(hasLabel(neq('person')), has('cnt',5))` | **180** | 240 | Master pushes `score` into the `person` index and filters the label locally, so the robots are lost silently, no error; without the barrier-like step the same master returns 60. The PR returns the complete set in every shape, identically on hstore. Two more cases where master throws (`has('cnt',5).limit().hasLabel(neq(..))`, `outE('deal').hasLabel(neq('flow'))`) become correct empty sets. `~page` with a downstream negative label: master rejects it (`Invalid paging traversal`, 12 cases), the PR pages it and the union of all pages equals the unpaged set for page sizes 7, 50 and 500 on both backends. **3. Plan and cost axis (the two findings of @bitflicker64 from 2026-09-06).** On top of the suite data I loaded 1 000 000 `big` vertices without any property index on rocksdb, which is what a real schema looks like, and recorded times plus `explain()`. Point lookups (finding 1): at `ac641c6` the plan is identical to master, `[HugeGraphStep(vertex,[big00000010]), RangeGlobalStep(0,10), HasStep([~label.neq(mark)])]`, 3 to 10 ms for `g.V('x').limit(10).hasLabel(neq(..))`, `g.V().hasId('x').limit(10)...` and the two-id variant. From my side this one is closed. Gate breadth (finding 2), on 1 M vertices: | Query | master | pr2994 | |---|---|---| | `g.V().has('age',gte(60)).limit(100000).hasLabel(neq('person'))` | 8 ms, incomplete result (as in section 2) | `Too many records(must <= 800000) for the query: Query * from VERTEX` | | `g.V().has('age',gte(60)).out().hasLabel(neq('person'))` | 17 ms, correct | `Too many records(...)` | | `g.V().has('age',gte(60)).out().where(__.not(__.hasLabel('person')))` | 18 ms, correct | `Too many records(...)` | | `g.V().has('fname',Text.contains('gold')).limit(100000).hasLabel(neq('person'))` | 7 ms | `Too many records(...)`, plan `HasStep(lambda)` | | `g.V().has('age',gte(60)).hasLabel(neq('person'))`, no barrier | `Too many records(...)` | `Too many records(...)` | Row 1 is the deliberate trade-off the PR description records (a complete answer instead of a silently incomplete 8 ms one) and looks right to me. Rows 2 and 3 are a regression: the negative label applies to the `out()` vertices, never to the `g.V()` candidates, master answers correctly in 17 ms, and the PR fails above `Query.DEFAULT_CAPACITY`. On the small suite graph, where only `person` and `robot` exist and both carry an `age` index, the same shapes keep the pushdown, which is why the PR's own tests do not see it: the gate depends on whether every label in the schema has compatible index coverage. **In short:** label semantics are correct and identical on both backends, the PR fixes a real class of silently incomplete answers, point lookups are preserved. The one thing I would close before merging is bounding the gate to steps that still operate on the source step's own elements, exactly in the spirit of finding 2. Happy to re-run the measurement on the next head, same script, same cluster. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected] --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
