Summary
A single-selection db.load(class, id) against a Concourse server >= 1.0.0 routes through BatchReader / CommandGroup machinery whenever selections.length == 1 and the class's navigate-path set is non-null — even when the navigate-path set is null/empty, i.e. when there is no batching benefit. In that narrow case the load pays the prepare() / submit() / drain() ceremony to ship a single operation that IncrementalReader could ship with less machinery.
Scope and impact
This is contained perf hygiene with narrow applicability, not a fix for any specific latency regression. Worth understanding when this short-circuit actually fires before assuming it helps:
- For routes that go through the framework's prefetch middleware (e.g.,
MetaRouter in cinchapi-platform-core), the prefetch enjoins the record selection with the audience-User selection into one BatchReader submit. By the time the call reaches Runway.selectOne, selections.length >= 2, so the short-circuit doesn't apply. The handler's subsequent audience.load(clazz, id) is then a reservation cache hit and doesn't reach this code path at all.
- It does apply to: direct
db.load(class, id) calls in application code (e.g., from @Computed methods or other model code), routes that bypass the prefetch middleware, and any future code path that calls selectOne with a single selection against a class that has no Record-typed fields.
For the typical authenticated /collection/<id> route shape, this short-circuit is unlikely to be the lever that moves a measured regression.
Evidence
The length-1 branch in selectOne already exists, but inside it the choice between BatchReader and IncrementalReader is gated only on supportsBulkCommands, with no consideration of whether there is anything to batch:
Runway.java:1051-1064
if(selections.length == 1) {
DatabaseSelection<?> selection = selections[0];
if(selection.state == Selection.State.RESOLVED) {
selection.setState(Selection.State.FINISHED);
}
else {
try (Reader reader = supportsBulkCommands
? new BatchReader(connections)
: new IncrementalReader(connections)) {
$selectWithPossibleSources(reader, selection, null);
reader.drain();
}
reserve(selection);
}
BatchReader.prepare() / submit(): BatchReader.java:186, :395
When BatchReader is the right choice
selections.length > 1 — genuine batching benefit.
selections.length == 1 && navigatePaths != null — navigate is folded into the same CommandGroup as the select; one wire RTT instead of two.
When it is pure overhead
selections.length == 1 && (navigatePaths == null || navigatePaths.isEmpty()) — nothing to fold; the IncrementalReader path does the same wire work with less machinery.
Proposed fix
Inside the existing selections.length == 1 branch, additionally choose IncrementalReader when the class for that selection has no navigate paths:
boolean hasNavigate = getNavigatePathsForClassIfSupported(selection.clazz) != null;
try (Reader reader = (supportsBulkCommands && hasNavigate)
? new BatchReader(connections)
: new IncrementalReader(connections)) {
...
}
(With the class-hierarchy variant for the needsSectionLookup path.)
Verification
- Add a counter on
BatchReader.submit() / IncrementalReader.read() in a sandbox build.
- Load one record of a class with no
Record-typed fields. Pre-fix: BatchReader.submit count = 1. Post-fix: IncrementalReader.read count = 1.
- Benchmark a single-record load of a leaf-ish model. Expect a measurable drop in wall time per call.
Out of scope
- The unconditional cleanup BFS following raw
Link values in non-DeferredReference fields (Runway.java:1773, :2056, :2595) — separate concern, larger impact.
Priority
Low. Real inefficiency where it applies, but the cases where it applies are narrow and don't include the typical authed /collection/<id> route shape served by the framework. File and fix when convenient; not a blocker for any known latency regression.
Summary
A single-selection
db.load(class, id)against a Concourse server >= 1.0.0 routes throughBatchReader/CommandGroupmachinery wheneverselections.length == 1and the class's navigate-path set is non-null — even when the navigate-path set is null/empty, i.e. when there is no batching benefit. In that narrow case the load pays theprepare()/submit()/drain()ceremony to ship a single operation thatIncrementalReadercould ship with less machinery.Scope and impact
This is contained perf hygiene with narrow applicability, not a fix for any specific latency regression. Worth understanding when this short-circuit actually fires before assuming it helps:
MetaRouterin cinchapi-platform-core), the prefetch enjoins the record selection with the audience-User selection into one BatchReader submit. By the time the call reachesRunway.selectOne,selections.length >= 2, so the short-circuit doesn't apply. The handler's subsequentaudience.load(clazz, id)is then a reservation cache hit and doesn't reach this code path at all.db.load(class, id)calls in application code (e.g., from@Computedmethods or other model code), routes that bypass the prefetch middleware, and any future code path that callsselectOnewith a single selection against a class that has no Record-typed fields.For the typical authenticated
/collection/<id>route shape, this short-circuit is unlikely to be the lever that moves a measured regression.Evidence
The length-1 branch in
selectOnealready exists, but inside it the choice betweenBatchReaderandIncrementalReaderis gated only onsupportsBulkCommands, with no consideration of whether there is anything to batch:Runway.java:1051-1064BatchReader.prepare()/submit():BatchReader.java:186, :395When
BatchReaderis the right choiceselections.length > 1— genuine batching benefit.selections.length == 1 && navigatePaths != null— navigate is folded into the sameCommandGroupas the select; one wire RTT instead of two.When it is pure overhead
selections.length == 1 && (navigatePaths == null || navigatePaths.isEmpty())— nothing to fold; theIncrementalReaderpath does the same wire work with less machinery.Proposed fix
Inside the existing
selections.length == 1branch, additionally chooseIncrementalReaderwhen the class for that selection has no navigate paths:(With the class-hierarchy variant for the
needsSectionLookuppath.)Verification
BatchReader.submit()/IncrementalReader.read()in a sandbox build.Record-typed fields. Pre-fix:BatchReader.submitcount = 1. Post-fix:IncrementalReader.readcount = 1.Out of scope
Linkvalues in non-DeferredReferencefields (Runway.java:1773, :2056, :2595) — separate concern, larger impact.Priority
Low. Real inefficiency where it applies, but the cases where it applies are narrow and don't include the typical authed
/collection/<id>route shape served by the framework. File and fix when convenient; not a blocker for any known latency regression.