Repository navigation
Description
SEVERITY: MEDIUM
Summary
The two-argument toGrpcValue(Object, ProjectionConfig) threads the request's ProjectionConfig into the arm that
encodes a document's own properties (toGrpcValue(doc.get(k), pc)), but the Map, Collection,
array and Result arms all recurse through the single-argument overload, which passes no config. In
that overload pc is null, so inProjection is false and every document reached through a container
takes the "not a projection row" path and is sent as a bare GrpcLink, whatever the caller asked
for.
Two consequences. First, a collection-valued projection of records loses the records: HTTP answers
the neighbour documents, gRPC answers a list of RID strings. Second, the request's soft_limit_bytes
setting is a budget held on the ProjectionConfig, so a document reached through a list
or a map never calls pc.wouldExceed(...) and never accumulates into pc.used: the limit does not
bound the values that are most likely to blow past it, and truncated is not raised for them.
This is the same table as the previous finding, one argument rather than one arm: the sweep that
threaded pc through converted the document arm and not the four container arms.
Where (HEAD 4e6cd88)
grpcw/src/main/java/com/arcadedb/server/grpc/ArcadeDbGrpcService.java:5687-5691theMaparm,
recursing withtoGrpcValue(e.getValue())grpcw/src/main/java/com/arcadedb/server/grpc/ArcadeDbGrpcService.java:5695-5700theCollection
arm, recursing withtoGrpcValue(e)grpcw/src/main/java/com/arcadedb/server/grpc/ArcadeDbGrpcService.java:5702-5710the array armgrpcw/src/main/java/com/arcadedb/server/grpc/ArcadeDbGrpcService.java:5717-5726theResultarm- compare
grpcw/src/main/java/com/arcadedb/server/grpc/ArcadeDbGrpcService.java:5522and:5575,
the document arms, which do passpc grpcw/src/main/java/com/arcadedb/server/grpc/ArcadeDbGrpcService.java:5504where a nullpc
becomesinProjection == false, and:5508-5513where that answers a bare linkgrpcw/src/main/java/com/arcadedb/server/grpc/ArcadeDbGrpcService.java:7151-7159
ProjectionConfig.wouldExceed, the budget the container arms never consult
Repro
Same harness as above. both('E') returns a List (so it survives the previous finding's
catch-all) and is the clean demonstration of this one. Real recorded output:
REPRO query2: SELECT both('FE') AS neighbours FROM FV WHERE name = 'a'
REPRO2 HTTP: java.util.ArrayList = [{@rid=#31:0, @type=FV, name=b, @cat=v}]
REPRO2 GRPC: java.util.ArrayList = [#31:0]
And from the wider sweep, bothE(), whose elements are loaded edges:
PROBEQ bothE() || SELECT bothE() AS v FROM TV29 WHERE name='a'
PROBER HTTP :: {v=[{@cat=e, @in=#31:0, @out=#28:0, @rid=#34:0, @type=TE29},]}
PROBER GRPC :: {v=[#34:0{java.lang.String},]}
The soft-limit half I did NOT execute: it is read from the code (the container arms never reach pc.wouldExceed, and pc is null for them in any case). Say so plainly rather than claiming it.
Refutation attempted
- Is "links inside a collection stay links" deliberate? Nothing says so. The arms carry no comment,
while the arms that DO threadpcare the ones in the same method. The HTTP serializer, which the
rest of this encoder is repeatedly justified against, returns the records. - Could it be the client's decoder flattening the link? The link is what arrived: the server built a
GrpcLinkbecauseinProjectionwas false. Settinginclude_projections=truewith either
encoding does not change it, because the setting never reaches the element. - Does it match HTTP for a STORED list of links? Possibly, and that may be where the shape came
from. It does not match for a list of LOADED records, which is what the graph functions return,
and that is the case measured above.
Suggested fix
Pass pc through the four container arms, as the document arms already do. The guarantee to ask
for: ProjectionConfig should not be an argument a recursion can drop silently. Either make the
single-argument overload private and unreachable from inside the encoder (so every recursion must
name a config, even if it is null), or carry the config on the call instead of the parameter.
A per-request budget that any container hop can shed is not a budget. Whichever is chosen, the
soft-limit behaviour wants a test that nests a document one level inside a list and asserts the
budget sees it.
Scope / confidence
The RID-instead-of-record half is executed and recorded. The soft-limit half is read only. Medium
to high confidence on the first, high on the code reading for the second, untested.
Dedup
Checked the dedup file for projection, soft limit, PROJECTION_AS, link, RID over gRPC. #9804 (the
Java remote driver deleting @rid/@type from rows carrying a @props hint) is the nearest neighbour
and is a different mechanism on the HTTP driver. Nothing covers this.
Activity
github-actions commentedon Nov 1, 2026
Merged into #11067, which now tracks this together with the other related issues in grpc. Closing here as a duplicate - reopen this issue if the grouping is wrong.
feat(#11068, #11067, #11061, #11059, #11054): idle databases close an…
lvca commentedon Nov 2, 2026
Fixed in #11118 (see #11067), available from 26.11.1.
Records inside lists, maps and arrays (for example
SELECT both('E')) are now encoded with the request's projection settings, andsoft_limit_bytesbounds the whole response.❤️ Thanks for helping make ArcadeDB better! ArcadeDB is free and open source, sustained by its community. If it's useful to you or your company, please consider becoming a sponsor to keep development going.
feat(#11068, #11067, #11061, #11059, #11054): idle databases close an…