SebastianGruza commented on issue #3090:
URL: https://github.com/apache/hugegraph/issues/3090#issuecomment-5721344424

   Back to item 1 (the property codec) with a full trace after #3184 landed on 
master (`1a15e762`). Two corrections to my 2026-09-16 comments first, then 
measurements and a proposal.
   
   ### Corrections
   
   - I wrote that the paged full scan (`has('~page', '')`) reaches 
`HstoreTable.queryAll()` as a `ConditionQuery`. Wrong: 
`HugeGraphStep.addHasContainer()` (line 184) consumes `~page` via `setPage()`, 
the has-container list stays empty and `makeQuery()` builds a plain `Query`, so 
`queryAll()` pushes `null`. Measured: `GET 
/graph/{edges,vertices}?page=&limit=5` → 200, no decoder errors.
   - I wrote that whether `queryAll()` and the shard overload fail "is data 
dependent". Also wrong; both are safe for reasons visible in the code: the 
shard overload `queryByRange(Session, Shard, ConditionQuery)` calls 
`session.scan(table, codeFrom, codeTo, scanType, query)`, and that overload in 
`HstoreSessionsImpl` (lines 757-767 and 770-780) discards the condition bytes 
and sends `new byte[0]`. Measured: 12-shard scan through 
`/traversers/edges/scan` → 106 edges, 0 errors, although every one of them has 
a DOUBLE as its first property.
   
   ### The codec: every type fails, and schema does not change that
   
   Cross-module test: a value written with the server 
`BytesBuffer.writeProperty(PropertyKey, value)` (hugegraph-core), read back 
with the struct `BytesBuffer.readProperty(PropertyKey)` (hugegraph-struct), 
which is exactly what `FilterIterator.hasNext()` does on the store for every 
pushed-down query. Master `1a15e762`, JDK 21:
   
   | type, value | server bytes | struct `readProperty` |
   |---|---|---|
   | DOUBLE 1.5 | `3ff8000000000000` | `Can't construct Cardinality from code 
0` |
   | DOUBLE 63.5 | `404fc00000000000` | `Unsupported data type UNKNOWN` |
   | LONG 100 | `64` | `Can't construct DataType from code 36` |
   | TEXT "ETC" | `03455443` | `Can't construct Cardinality from code 0` |
   | INT 7 · BOOLEAN true · FLOAT 0.5 · DATE 0 | `07` · `01` · `3f000000` · 
`00` | `Can't construct Cardinality from code 0` |
   
   No type gets through; which exception fires depends only on the first byte 
of the value. Relevant for the fix design: the struct 
`readProperty(PropertyKey)` (`BytesBuffer.java:587-596`) *always* reads the 
header byte and only then sets `cardinality`/`dataType` on the key it was 
given, so the schema-aware path (`BusinessHandlerImpl` around line 700, 
`getGraphSupplier(graph)`) uses the schema for the key's identity only, not for 
decoding. Wiring a `HugeGraphSupplier` into `FilterIterator` fixes nothing on 
its own. Test source and output: 
https://github.com/SebastianGruza/hugegraph-validation/tree/master/results/issue-3090/codec.
   
   The other side of the channel works: the server `ConditionQuery.bytes()` 
round-trips through the struct `ConditionQuery.fromBytes()` with 
`resultType=EDGE` and the conditions intact (`[LABEL == 1, 1 > 1.0]`), so if 
the bytes reached the store, parsing would start and fail on the first row with 
properties.
   
   ### Why master does not fail: the server no longer sends any condition that 
forces a parse
   
   I traced every place where `HstoreTable` serializes a `ConditionQuery` and 
measured each on the lab (master `1a15e762`, 1 PD + 3 stores, edges with a 
DOUBLE as the first property; 0 decoder errors in the server and store logs 
after the whole series):
   
   | path | what reaches the store | why | measured |
   |---|---|---|---|
   | `queryByPrefix` / `queryByRange(IdRangeQuery)` by sort key, with an extra 
non-sort-key property (`has('asset','ETC').has('epoch', gt(50)).has('amount', 
gt(1.0))`) | nothing | `GraphTransaction.optimizeQuery()` (lines 1600-1614) 
does `query.copy()` + `resetUserpropConditions()` once the sort keys match; 
`HstoreStore.query()` (line 400) copies the already optimized query as 
`originQuery`, so `prepareConditionQuery()` sees sysprops only and returns 
`null` after #3184; the rest is filtered by the server | 7 REST adjacency 
filters, including equality and range on `amount`: 200, correct sets |
   | non-sort-key property without an index (`g.E().has('amount', gt(1.0))`, 
`g.V().hasLabel('person').has('name','zzz')`) | nothing | rejected in 
`GraphIndexTransaction`: `NoIndexException` | 7 Gremlin queries: 500 
NoIndexException, store untouched |
   | `queryAll()`, both exits | nothing | only a plain `Query` gets there: 
`~page` is consumed in `HugeGraphStep`, label queries go to the label index or 
`NoIndexException` (`supportsQueryByLabel()==false`) | paged edge and vertex 
scans: 200 |
   | `queryByRange(Session, Shard, ConditionQuery)` (`/traversers/*/scan`) | 
empty array | `HstoreSessionsImpl.scan(table, codeFrom, codeTo, …)` drops 
`query` | 12 shards, 106 edges, 0 errors |
   | sysprop-only pushdown from `queryByRange` | nothing since `1a15e762` | 
#3184 | 13/13 sort-key shapes, id sets equal to rocksdb (in the PR) |
   
   Conclusion: after #3184 there is no OLTP query that delivers 
`ConditionQuery` bytes to `FilterIterator`. The whole store-side "operator 
sinking" (`FilterIterator`, sort/aggregation through `SortShuffleSerializer` 
and `AggregativeQueryObserver`, `indexIntersection`) is dead code for 
server-written data, because each of those paths starts by parsing the row with 
the same `readProperty`. #3184 did not fix item 1; it cut the last path by 
which the server reached the broken parser. That changes the problem statement: 
not "some queries fail" but "the feature is disabled by an incompatible codec, 
and two guards keep it away from traffic".
   
   ### Proposed direction (simpler than the versioned blob from 2026-09-02)
   
   On 2026-09-02 I proposed a versioned pushdown blob carrying schema hints. 
After the trace that is unnecessary: the store has the schema. The struct 
`SchemaDriver` reads 
`HUGEGRAPH/{cluster}/GRAPHSPACE/{gs}/GRAPH/{graph}/SCHEMA/PROPERTY_KEY/…` from 
PD, the same path the server `SchemaMetaManager` writes, and 
`BusinessHandlerImpl.getGraphSupplier(graph)` already builds a `SchemaGraph` 
from it. So the codec can simply be unified on the server's schema-driven 
format:
   
   1. struct `BytesBuffer.readProperty/writeProperty(PropertyKey)`: decode by 
the key's `dataType`/`cardinality`, no header byte, identical to hugegraph-core;
   2. `BinaryElementSerializer.parseProperty()` requires a `HugeGraphSupplier`; 
`FilterIterator.of()` and `HgStoreWrapperEx` pass `getGraphSupplier(graph)` 
instead of `null` (edge label from the schema as well, instead of `UNDEF`);
   3. the store's internal writers 
(`SortShuffleSerializer.writeVertex/writeEdge`, `AggregativeQueryObserver`) 
move to the same codec. No data migration: the store has no persisted rows in 
its own format, spill files and aggregation responses are transient;
   4. align the `prepareConditionQuery()` / `prepareConditionQueryList()` 
guards (item 2 of my 2026-09-16 list);
   5. a cross-module test in CI: server writes, struct reads, all types and 
cardinalities, plus a `Comparable` test for `Condition.RelationType.compare` on 
the struct side;
   6. only afterwards, separately: decide whether the server should push 
user-property conditions again (today it pushes none), i.e. whether sinking 
returns to traffic.
   
   Rolling upgrades are not a concern, because no bytes reach the store today: 
an old server with a new store and a new server with an old store behave 
exactly as now. Consequence for item 6: without it, 1-5 is a latent fix visible 
only in tests, and I treat it as such.
   
   @imbajin @bitflicker64: is this the direction you want? If so, I'll take 1-5 
as one PR across struct + store + hstore, with the test from item 5 as the 
first commit (red on master). If you would rather keep sinking disabled and 
remove the dead code, I can prepare that too, but I'd prefer not to guess.
   


-- 
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]

Reply via email to