Skip to content

Commit d6c0bcc

Browse files
committed
Introduce managed query execution on Driver level
This is a new basic high-level API for executing idempotent queries.
1 parent 94f9c20 commit d6c0bcc

25 files changed

+1406
-144
lines changed

driver/clirr-ignored-differences.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,4 +409,16 @@
409409
<method>org.neo4j.driver.BaseSession session(java.lang.Class, org.neo4j.driver.SessionConfig)</method>
410410
</difference>
411411

412+
<difference>
413+
<className>org/neo4j/driver/Driver</className>
414+
<differenceType>7012</differenceType>
415+
<method>org.neo4j.driver.querytask.QueryTask queryTask(java.lang.String)</method>
416+
</difference>
417+
418+
<difference>
419+
<className>org/neo4j/driver/Driver</className>
420+
<differenceType>7012</differenceType>
421+
<method>org.neo4j.driver.BookmarkManager queryBookmarkManager()</method>
422+
</difference>
423+
412424
</differences>

driver/src/main/java/module-info.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
exports org.neo4j.driver.net;
2828
exports org.neo4j.driver.util;
2929
exports org.neo4j.driver.exceptions;
30+
exports org.neo4j.driver.querytask;
3031

3132
requires reactor.core;
3233
requires io.netty.common;

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

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ public final class Config implements Serializable {
7575

7676
private static final Config EMPTY = builder().build();
7777

78+
private final BookmarkManager queryBookmarkManager;
79+
7880
/**
7981
* User defined logging
8082
*/
@@ -102,6 +104,7 @@ public final class Config implements Serializable {
102104
private final MetricsAdapter metricsAdapter;
103105

104106
private Config(ConfigBuilder builder) {
107+
this.queryBookmarkManager = builder.queryBookmarkManager;
105108
this.logging = builder.logging;
106109
this.logLeakedSessions = builder.logLeakedSessions;
107110

@@ -123,6 +126,19 @@ private Config(ConfigBuilder builder) {
123126
this.metricsAdapter = builder.metricsAdapter;
124127
}
125128

129+
/**
130+
* A {@link BookmarkManager} implementation for the driver to use on
131+
* {@link Driver#queryTask(String)} method and its variants by default.
132+
* <p>
133+
* Please note that sessions will not use this automatically, but it is possible to enable it explicitly
134+
* using {@link SessionConfig.Builder#withBookmarkManager(BookmarkManager)}.
135+
*
136+
* @return bookmark manager, must not be {@code null}
137+
*/
138+
public BookmarkManager queryBookmarkManager() {
139+
return queryBookmarkManager;
140+
}
141+
126142
/**
127143
* Logging provider
128144
*
@@ -262,6 +278,8 @@ public String userAgent() {
262278
* Used to build new config instances
263279
*/
264280
public static final class ConfigBuilder {
281+
private BookmarkManager queryBookmarkManager =
282+
BookmarkManagers.defaultManager(BookmarkManagerConfig.builder().build());
265283
private Logging logging = DEV_NULL_LOGGING;
266284
private boolean logLeakedSessions;
267285
private int maxConnectionPoolSize = PoolSettings.DEFAULT_MAX_CONNECTION_POOL_SIZE;
@@ -281,6 +299,22 @@ public static final class ConfigBuilder {
281299

282300
private ConfigBuilder() {}
283301

302+
/**
303+
* Sets a {@link BookmarkManager} implementation for the driver to use on
304+
* {@link Driver#queryTask(String)} method and its variants by default.
305+
* <p>
306+
* Please note that sessions will not use this automatically, but it is possible to enable it explicitly
307+
* using {@link SessionConfig.Builder#withBookmarkManager(BookmarkManager)}.
308+
*
309+
* @param queryBookmarkManager bookmark manager, must not be {@code null}
310+
* @return this builder
311+
*/
312+
public ConfigBuilder withQueryBookmarkManager(BookmarkManager queryBookmarkManager) {
313+
Objects.requireNonNull(queryBookmarkManager, "queryBookmarkManager must not be null");
314+
this.queryBookmarkManager = queryBookmarkManager;
315+
return this;
316+
}
317+
284318
/**
285319
* Provide a logging implementation for the driver to use. Java logging framework {@link java.util.logging} with {@link Level#INFO} is used by default.
286320
* Callers are expected to either implement {@link Logging} interface or provide one of the existing implementations available from static factory

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

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.concurrent.CompletionStage;
2222
import org.neo4j.driver.async.AsyncSession;
2323
import org.neo4j.driver.exceptions.ClientException;
24+
import org.neo4j.driver.querytask.QueryTask;
2425
import org.neo4j.driver.reactive.ReactiveSession;
2526
import org.neo4j.driver.reactive.RxSession;
2627
import org.neo4j.driver.types.TypeSystem;
@@ -63,6 +64,24 @@
6364
* @since 1.0 (Modified and Added {@link AsyncSession} and {@link RxSession} since 2.0)
6465
*/
6566
public interface Driver extends AutoCloseable {
67+
/**
68+
* Creates a new {@link QueryTask} instance that executes an idempotent query in a managed transaction with
69+
* automatic retries on retryable errors.
70+
*
71+
* @param query query string
72+
* @return new query task instance
73+
* @since 5.5
74+
*/
75+
@Experimental
76+
QueryTask queryTask(String query);
77+
78+
/**
79+
* Returns an instance of {@link BookmarkManager} used by {@link QueryTask} instances by default.
80+
*
81+
* @return bookmark manager, must not be {@code null}
82+
*/
83+
BookmarkManager queryBookmarkManager();
84+
6685
/**
6786
* Return a flag to indicate whether or not encryption is used for this driver.
6887
*
@@ -84,6 +103,7 @@ default Session session() {
84103
/**
85104
* Instantiate a new {@link Session} with a specified {@link SessionConfig session configuration}.
86105
* Use {@link SessionConfig#forDatabase(String)} to obtain a general purpose session configuration for the specified database.
106+
*
87107
* @param sessionConfig specifies session configurations for this session.
88108
* @return a new {@link Session} object.
89109
* @see SessionConfig
@@ -257,6 +277,7 @@ default AsyncSession asyncSession(SessionConfig sessionConfig) {
257277
/**
258278
* Returns the driver metrics if metrics reporting is enabled via {@link Config.ConfigBuilder#withDriverMetrics()}.
259279
* Otherwise, a {@link ClientException} will be thrown.
280+
*
260281
* @return the driver metrics if enabled.
261282
* @throws ClientException if the driver metrics reporting is not enabled.
262283
*/
@@ -281,7 +302,7 @@ default AsyncSession asyncSession(SessionConfig sessionConfig) {
281302
/**
282303
* This verifies if the driver can connect to a remote server or a cluster
283304
* by establishing a network connection with the remote and possibly exchanging a few data before closing the connection.
284-
*
305+
* <p>
285306
* It throws exception if fails to connect. Use the exception to further understand the cause of the connectivity problem.
286307
* Note: Even if this method throws an exception, the driver still need to be closed via {@link #close()} to free up all resources.
287308
*/
@@ -290,7 +311,7 @@ default AsyncSession asyncSession(SessionConfig sessionConfig) {
290311
/**
291312
* This verifies if the driver can connect to a remote server or cluster
292313
* by establishing a network connection with the remote and possibly exchanging a few data before closing the connection.
293-
*
314+
* <p>
294315
* This operation is asynchronous and returns a {@link CompletionStage}. This stage is completed with
295316
* {@code null} when the driver connects to the remote server or cluster successfully.
296317
* It is completed exceptionally if the driver failed to connect the remote server or cluster.
@@ -303,12 +324,14 @@ default AsyncSession asyncSession(SessionConfig sessionConfig) {
303324

304325
/**
305326
* Returns true if the server or cluster the driver connects to supports multi-databases, otherwise false.
327+
*
306328
* @return true if the server or cluster the driver connects to supports multi-databases, otherwise false.
307329
*/
308330
boolean supportsMultiDb();
309331

310332
/**
311333
* Asynchronous check if the server or cluster the driver connects to supports multi-databases.
334+
*
312335
* @return a {@link CompletionStage completion stage} that returns true if the server or cluster
313336
* the driver connects to supports multi-databases, otherwise false.
314337
*/

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,8 @@ protected InternalDriver createRoutingDriver(
269269
*/
270270
protected InternalDriver createDriver(
271271
SecurityPlan securityPlan, SessionFactory sessionFactory, MetricsProvider metricsProvider, Config config) {
272-
return new InternalDriver(securityPlan, sessionFactory, metricsProvider, config.logging());
272+
return new InternalDriver(
273+
config.queryBookmarkManager(), securityPlan, sessionFactory, metricsProvider, config.logging());
273274
}
274275

275276
/**

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,25 +24,31 @@
2424
import java.util.concurrent.CompletionStage;
2525
import java.util.concurrent.atomic.AtomicBoolean;
2626
import org.neo4j.driver.BaseSession;
27+
import org.neo4j.driver.BookmarkManager;
2728
import org.neo4j.driver.Driver;
2829
import org.neo4j.driver.Logger;
2930
import org.neo4j.driver.Logging;
3031
import org.neo4j.driver.Metrics;
32+
import org.neo4j.driver.Query;
3133
import org.neo4j.driver.Session;
3234
import org.neo4j.driver.SessionConfig;
3335
import org.neo4j.driver.async.AsyncSession;
3436
import org.neo4j.driver.internal.async.InternalAsyncSession;
3537
import org.neo4j.driver.internal.async.NetworkSession;
3638
import org.neo4j.driver.internal.metrics.DevNullMetricsProvider;
3739
import org.neo4j.driver.internal.metrics.MetricsProvider;
40+
import org.neo4j.driver.internal.querytask.InternalQueryTask;
3841
import org.neo4j.driver.internal.reactive.InternalRxSession;
3942
import org.neo4j.driver.internal.security.SecurityPlan;
4043
import org.neo4j.driver.internal.types.InternalTypeSystem;
4144
import org.neo4j.driver.internal.util.Futures;
45+
import org.neo4j.driver.querytask.QueryConfig;
46+
import org.neo4j.driver.querytask.QueryTask;
4247
import org.neo4j.driver.reactive.RxSession;
4348
import org.neo4j.driver.types.TypeSystem;
4449

4550
public class InternalDriver implements Driver {
51+
private final BookmarkManager queryBookmarkManager;
4652
private final SecurityPlan securityPlan;
4753
private final SessionFactory sessionFactory;
4854
private final Logger log;
@@ -51,16 +57,28 @@ public class InternalDriver implements Driver {
5157
private final MetricsProvider metricsProvider;
5258

5359
InternalDriver(
60+
BookmarkManager queryBookmarkManager,
5461
SecurityPlan securityPlan,
5562
SessionFactory sessionFactory,
5663
MetricsProvider metricsProvider,
5764
Logging logging) {
65+
this.queryBookmarkManager = queryBookmarkManager;
5866
this.securityPlan = securityPlan;
5967
this.sessionFactory = sessionFactory;
6068
this.metricsProvider = metricsProvider;
6169
this.log = logging.getLog(getClass());
6270
}
6371

72+
@Override
73+
public QueryTask queryTask(String query) {
74+
return new InternalQueryTask(this, new Query(query), QueryConfig.defaultConfig());
75+
}
76+
77+
@Override
78+
public BookmarkManager queryBookmarkManager() {
79+
return queryBookmarkManager;
80+
}
81+
6482
@SuppressWarnings({"unchecked", "deprecation"})
6583
@Override
6684
public <T extends BaseSession> T session(Class<T> sessionClass, SessionConfig sessionConfig) {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ public class NoOpBookmarkManager implements BookmarkManager {
3131
@Serial
3232
private static final long serialVersionUID = 7175136719562680362L;
3333

34+
public static final NoOpBookmarkManager INSTANCE = new NoOpBookmarkManager();
35+
3436
private static final Set<Bookmark> EMPTY = Collections.emptySet();
3537

38+
private NoOpBookmarkManager() {}
39+
3640
@Override
3741
public void updateBookmarks(Set<Bookmark> previousBookmarks, Set<Bookmark> newBookmarks) {
3842
// ignored

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public NetworkSession newInstance(SessionConfig sessionConfig) {
6161
parseFetchSize(sessionConfig),
6262
sessionConfig.impersonatedUser().orElse(null),
6363
logging,
64-
sessionConfig.bookmarkManager().orElse(new NoOpBookmarkManager()));
64+
sessionConfig.bookmarkManager().orElse(NoOpBookmarkManager.INSTANCE));
6565
}
6666

6767
private Set<Bookmark> toDistinctSet(Iterable<Bookmark> bookmarks) {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright (c) "Neo4j"
3+
* Neo4j Sweden AB [http://neo4j.com]
4+
*
5+
* This file is part of Neo4j.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.neo4j.driver.internal.querytask;
20+
21+
import java.util.List;
22+
import org.neo4j.driver.Record;
23+
import org.neo4j.driver.querytask.EagerResult;
24+
import org.neo4j.driver.summary.ResultSummary;
25+
26+
public record EagerResultValue(List<String> keys, List<Record> records, ResultSummary summary) implements EagerResult {}

0 commit comments

Comments
 (0)