Skip to content

Commit 1b81124

Browse files
authored
perf(value): optimise value handling for Bolt Connection (#1658)
The objective of this update is to reduce the amount of memory allocations needed when Bolt Connection values are used. Driver internal values now implement Bolt Connection values directly and no longer need a wrapper adapter. In addition, lists are used instead of arrays to prevent array copying.
1 parent 881e901 commit 1b81124

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+372
-510
lines changed

driver/src/main/java/org/neo4j/driver/Values.java

Lines changed: 30 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import java.util.List;
3737
import java.util.Map;
3838
import java.util.function.Function;
39+
import java.util.stream.Collectors;
3940
import java.util.stream.IntStream;
4041
import java.util.stream.Stream;
4142
import org.neo4j.driver.exceptions.ClientException;
@@ -44,7 +45,6 @@
4445
import org.neo4j.driver.internal.InternalIsoDuration;
4546
import org.neo4j.driver.internal.InternalPoint2D;
4647
import org.neo4j.driver.internal.InternalPoint3D;
47-
import org.neo4j.driver.internal.value.BoltValue;
4848
import org.neo4j.driver.internal.value.BooleanValue;
4949
import org.neo4j.driver.internal.value.BytesValue;
5050
import org.neo4j.driver.internal.value.DateTimeValue;
@@ -110,9 +110,6 @@ public static Value value(Object value) {
110110
return NullValue.NULL;
111111
}
112112

113-
if (value instanceof BoltValue boltValue) {
114-
return boltValue.asDriverValue();
115-
}
116113
if (value instanceof AsValue) {
117114
return ((AsValue) value).asValue();
118115
}
@@ -252,10 +249,7 @@ public static Value[] values(final Object... input) {
252249
* @return the value
253250
*/
254251
public static Value value(Value... input) {
255-
var size = input.length;
256-
var values = new Value[size];
257-
System.arraycopy(input, 0, values, 0, size);
258-
return new ListValue(values);
252+
return new ListValue(List.of(input));
259253
}
260254

261255
/**
@@ -273,7 +267,9 @@ public static Value value(byte... input) {
273267
* @return the value
274268
*/
275269
public static Value value(String... input) {
276-
var values = Arrays.stream(input).map(StringValue::new).toArray(StringValue[]::new);
270+
var values = Arrays.stream(input)
271+
.map(StringValue::new)
272+
.collect(Collectors.toCollection(() -> new ArrayList<>(input.length)));
277273
return new ListValue(values);
278274
}
279275

@@ -283,8 +279,9 @@ public static Value value(String... input) {
283279
* @return the value
284280
*/
285281
public static Value value(boolean... input) {
286-
var values =
287-
IntStream.range(0, input.length).mapToObj(i -> value(input[i])).toArray(Value[]::new);
282+
var values = IntStream.range(0, input.length)
283+
.mapToObj(i -> value(input[i]))
284+
.collect(Collectors.toCollection(() -> new ArrayList<>(input.length)));
288285
return new ListValue(values);
289286
}
290287

@@ -294,8 +291,9 @@ public static Value value(boolean... input) {
294291
* @return the value
295292
*/
296293
public static Value value(char... input) {
297-
var values =
298-
IntStream.range(0, input.length).mapToObj(i -> value(input[i])).toArray(Value[]::new);
294+
var values = IntStream.range(0, input.length)
295+
.mapToObj(i -> value(input[i]))
296+
.collect(Collectors.toCollection(() -> new ArrayList<>(input.length)));
299297
return new ListValue(values);
300298
}
301299

@@ -305,7 +303,9 @@ public static Value value(char... input) {
305303
* @return the value
306304
*/
307305
public static Value value(long... input) {
308-
var values = Arrays.stream(input).mapToObj(Values::value).toArray(Value[]::new);
306+
var values = Arrays.stream(input)
307+
.mapToObj(Values::value)
308+
.collect(Collectors.toCollection(() -> new ArrayList<>(input.length)));
309309
return new ListValue(values);
310310
}
311311

@@ -315,8 +315,9 @@ public static Value value(long... input) {
315315
* @return the value
316316
*/
317317
public static Value value(short... input) {
318-
var values =
319-
IntStream.range(0, input.length).mapToObj(i -> value(input[i])).toArray(Value[]::new);
318+
var values = IntStream.range(0, input.length)
319+
.mapToObj(i -> value(input[i]))
320+
.collect(Collectors.toCollection(() -> new ArrayList<>(input.length)));
320321
return new ListValue(values);
321322
}
322323
/**
@@ -325,7 +326,9 @@ public static Value value(short... input) {
325326
* @return the value
326327
*/
327328
public static Value value(int... input) {
328-
var values = Arrays.stream(input).mapToObj(Values::value).toArray(Value[]::new);
329+
var values = Arrays.stream(input)
330+
.mapToObj(Values::value)
331+
.collect(Collectors.toCollection(() -> new ArrayList<>(input.length)));
329332
return new ListValue(values);
330333
}
331334
/**
@@ -334,7 +337,9 @@ public static Value value(int... input) {
334337
* @return the value
335338
*/
336339
public static Value value(double... input) {
337-
var values = Arrays.stream(input).mapToObj(Values::value).toArray(Value[]::new);
340+
var values = Arrays.stream(input)
341+
.mapToObj(Values::value)
342+
.collect(Collectors.toCollection(() -> new ArrayList<>(input.length)));
338343
return new ListValue(values);
339344
}
340345

@@ -344,8 +349,9 @@ public static Value value(double... input) {
344349
* @return the value
345350
*/
346351
public static Value value(float... input) {
347-
var values =
348-
IntStream.range(0, input.length).mapToObj(i -> value(input[i])).toArray(Value[]::new);
352+
var values = IntStream.range(0, input.length)
353+
.mapToObj(i -> value(input[i]))
354+
.collect(Collectors.toCollection(() -> new ArrayList<>(input.length)));
349355
return new ListValue(values);
350356
}
351357

@@ -355,11 +361,8 @@ public static Value value(float... input) {
355361
* @return the value
356362
*/
357363
public static Value value(List<Object> vals) {
358-
var values = new Value[vals.size()];
359-
var i = 0;
360-
for (var val : vals) {
361-
values[i++] = value(val);
362-
}
364+
var values =
365+
vals.stream().map(Values::value).collect(Collectors.toCollection(() -> new ArrayList<>(vals.size())));
363366
return new ListValue(values);
364367
}
365368

@@ -382,7 +385,7 @@ public static Value value(Iterator<Object> val) {
382385
while (val.hasNext()) {
383386
values.add(value(val.next()));
384387
}
385-
return new ListValue(values.toArray(new Value[0]));
388+
return new ListValue(values);
386389
}
387390

388391
/**
@@ -391,7 +394,7 @@ public static Value value(Iterator<Object> val) {
391394
* @return the value
392395
*/
393396
public static Value value(Stream<Object> stream) {
394-
var values = stream.map(Values::value).toArray(Value[]::new);
397+
var values = stream.map(Values::value).collect(Collectors.toList());
395398
return new ListValue(values);
396399
}
397400

driver/src/main/java/org/neo4j/driver/internal/DriverFactory.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,16 @@ private InternalDriver createDriver(
176176
homeDatabaseCache);
177177
var driver = createDriver(securityPlanManager, sessionFactory, metricsProvider, config);
178178
var log = config.logging().getLog(getClass());
179-
log.info("Driver instance %s created for server uri '%s'", driver.hashCode(), uri);
179+
var host = uri.getHost();
180+
var port = uri.getPort();
181+
if (port == -1) {
182+
port = 7687;
183+
}
184+
if (uri.getScheme().startsWith("bolt")) {
185+
log.info("Direct driver instance %s created for server address %s:%d", driver.hashCode(), host, port);
186+
} else {
187+
log.info("Routing driver instance %s created for server address %s:%d", driver.hashCode(), host, port);
188+
}
180189
return driver;
181190
} catch (Throwable driverError) {
182191
if (boltConnectionProvider != null) {

driver/src/main/java/org/neo4j/driver/internal/InternalRecord.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import static org.neo4j.driver.Values.ofValue;
2222
import static org.neo4j.driver.internal.util.Format.formatPairs;
2323

24-
import java.util.Arrays;
2524
import java.util.List;
2625
import java.util.Map;
2726
import java.util.NoSuchElementException;
@@ -39,15 +38,15 @@
3938

4039
public class InternalRecord extends InternalMapAccessorWithDefaultValue implements Record {
4140
private final QueryKeys queryKeys;
42-
private final Value[] values;
41+
private final List<Value> values;
4342
private int hashCode = 0;
4443

45-
public InternalRecord(List<String> keys, Value[] values) {
44+
public InternalRecord(List<String> keys, List<Value> values) {
4645
this.queryKeys = new QueryKeys(keys);
4746
this.values = values;
4847
}
4948

50-
public InternalRecord(QueryKeys queryKeys, Value[] values) {
49+
public InternalRecord(QueryKeys queryKeys, List<Value> values) {
5150
this.queryKeys = queryKeys;
5251
this.values = values;
5352
}
@@ -59,7 +58,7 @@ public List<String> keys() {
5958

6059
@Override
6160
public List<Value> values() {
62-
return Arrays.asList(values);
61+
return values;
6362
}
6463

6564
@Override
@@ -94,18 +93,18 @@ public Value get(String key) {
9493
if (fieldIndex == -1) {
9594
return Values.NULL;
9695
} else {
97-
return values[fieldIndex];
96+
return values.get(fieldIndex);
9897
}
9998
}
10099

101100
@Override
102101
public Value get(int index) {
103-
return index >= 0 && index < values.length ? values[index] : Values.NULL;
102+
return index >= 0 && index < values.size() ? values.get(index) : Values.NULL;
104103
}
105104

106105
@Override
107106
public int size() {
108-
return values.length;
107+
return values.size();
109108
}
110109

111110
@Override
@@ -161,7 +160,7 @@ public boolean equals(Object other) {
161160
@Override
162161
public int hashCode() {
163162
if (hashCode == 0) {
164-
hashCode = 31 * queryKeys.hashCode() + Arrays.hashCode(values);
163+
hashCode = 31 * queryKeys.hashCode() + values.hashCode();
165164
}
166165
return hashCode;
167166
}

driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/AdaptingDriverResponseHandler.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717
package org.neo4j.driver.internal.adaptedbolt;
1818

19-
import java.util.Arrays;
19+
import java.util.List;
2020
import java.util.Map;
2121
import java.util.Objects;
2222
import org.neo4j.bolt.connection.ResponseHandler;
@@ -32,7 +32,6 @@
3232
import org.neo4j.bolt.connection.summary.RunSummary;
3333
import org.neo4j.bolt.connection.summary.TelemetrySummary;
3434
import org.neo4j.driver.Value;
35-
import org.neo4j.driver.internal.value.BoltValue;
3635
import org.neo4j.driver.internal.value.BoltValueFactory;
3736

3837
final class AdaptingDriverResponseHandler implements ResponseHandler {
@@ -63,11 +62,8 @@ public void onRunSummary(RunSummary summary) {
6362
}
6463

6564
@Override
66-
public void onRecord(org.neo4j.bolt.connection.values.Value[] fields) {
67-
var mappedFields = Arrays.stream(fields)
68-
.map(field -> ((BoltValue) field).asDriverValue())
69-
.toArray(Value[]::new);
70-
delegate.onRecord(mappedFields);
65+
public void onRecord(List<org.neo4j.bolt.connection.values.Value> fields) {
66+
delegate.onRecord(boltValueFactory.toDriverList(fields));
7167
}
7268

7369
@Override

driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/BasicResponseHandler.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737

3838
public class BasicResponseHandler implements DriverResponseHandler {
3939
private final CompletableFuture<Summaries> summariesFuture = new CompletableFuture<>();
40-
private final List<Value[]> valuesList = new ArrayList<>();
40+
private final List<List<Value>> valuesList = new ArrayList<>();
4141

4242
private BeginSummary beginSummary;
4343
private RunSummary runSummary;
@@ -83,7 +83,7 @@ public void onRunSummary(RunSummary summary) {
8383
}
8484

8585
@Override
86-
public void onRecord(Value[] fields) {
86+
public void onRecord(List<Value> fields) {
8787
valuesList.add(fields);
8888
}
8989

@@ -162,7 +162,7 @@ public void onComplete() {
162162
public record Summaries(
163163
BeginSummary beginSummary,
164164
RunSummary runSummary,
165-
List<Value[]> valuesList,
165+
List<List<Value>> valuesList,
166166
PullSummary pullSummary,
167167
DiscardSummary discardSummary,
168168
CommitSummary commitSummary,

driver/src/main/java/org/neo4j/driver/internal/adaptedbolt/DriverResponseHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package org.neo4j.driver.internal.adaptedbolt;
1818

19+
import java.util.List;
1920
import org.neo4j.bolt.connection.summary.BeginSummary;
2021
import org.neo4j.bolt.connection.summary.CommitSummary;
2122
import org.neo4j.bolt.connection.summary.LogoffSummary;
@@ -41,7 +42,7 @@ default void onRunSummary(RunSummary summary) {
4142
// ignored
4243
}
4344

44-
default void onRecord(Value[] fields) {
45+
default void onRecord(List<Value> fields) {
4546
// ignored
4647
}
4748

driver/src/main/java/org/neo4j/driver/internal/async/DelegatingResponseHandler.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package org.neo4j.driver.internal.async;
1818

19+
import java.util.List;
1920
import java.util.Objects;
2021
import org.neo4j.bolt.connection.summary.BeginSummary;
2122
import org.neo4j.bolt.connection.summary.CommitSummary;
@@ -54,7 +55,7 @@ public void onRunSummary(RunSummary summary) {
5455
}
5556

5657
@Override
57-
public void onRecord(Value[] fields) {
58+
public void onRecord(List<Value> fields) {
5859
delegate.onRecord(fields);
5960
}
6061

driver/src/main/java/org/neo4j/driver/internal/cursor/ResultCursorImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -591,7 +591,7 @@ public void onRunSummary(RunSummary summary) {
591591
}
592592

593593
@Override
594-
public void onRecord(Value[] fields) {
594+
public void onRecord(List<Value> fields) {
595595
var record = new InternalRecord(runSummary.keys(), fields);
596596
CompletableFuture<Record> peekFuture;
597597
CompletableFuture<Record> recordFuture = null;

driver/src/main/java/org/neo4j/driver/internal/cursor/RxResultCursorImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ public synchronized void onIgnored() {
328328
}
329329

330330
@Override
331-
public void onRecord(Value[] fields) {
331+
public void onRecord(List<Value> fields) {
332332
log.trace("[%d] onRecord", hashCode());
333333
synchronized (this) {
334334
updateRecordState(RecordState.HAD_RECORD);

driver/src/main/java/org/neo4j/driver/internal/util/Extract.java

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import static org.neo4j.driver.internal.util.Iterables.newHashMapWithSize;
2727

2828
import java.util.ArrayList;
29-
import java.util.Arrays;
3029
import java.util.List;
3130
import java.util.Map;
3231
import java.util.function.Function;
@@ -52,25 +51,17 @@ private Extract() {
5251
throw new UnsupportedOperationException();
5352
}
5453

55-
public static List<Value> list(Value[] values) {
56-
return switch (values.length) {
57-
case 0 -> emptyList();
58-
case 1 -> singletonList(values[0]);
59-
default -> List.of(values);
60-
};
61-
}
62-
63-
public static <T> List<T> list(Value[] data, Function<Value, T> mapFunction) {
64-
var size = data.length;
54+
public static <T> List<T> list(List<? extends Value> data, Function<Value, T> mapFunction) {
55+
var size = data.size();
6556
switch (size) {
6657
case 0 -> {
6758
return emptyList();
6859
}
6960
case 1 -> {
70-
return singletonList(mapFunction.apply(data[0]));
61+
return singletonList(mapFunction.apply(data.get(0)));
7162
}
7263
default -> {
73-
return Arrays.stream(data).map(mapFunction).toList();
64+
return data.stream().map(mapFunction).toList();
7465
}
7566
}
7667
}

0 commit comments

Comments
 (0)