Skip to content

Commit 96c9f27

Browse files
committed
Initial BookmarkManager implementation
1 parent 7a0515f commit 96c9f27

File tree

53 files changed

+878
-455
lines changed

Some content is hidden

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

53 files changed

+878
-455
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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;
20+
21+
import java.io.Serializable;
22+
import java.util.Set;
23+
24+
/**
25+
* Keeps track of database bookmarks and is used by the driver to ensure causal consistency between sessions and query executions.
26+
* <p>
27+
* Please note that implementations of this interface MUST NOT block for extended periods of time.
28+
*
29+
* @see org.neo4j.driver.Config.ConfigBuilder#withBookmarkManager(BookmarkManager)
30+
*/
31+
public interface BookmarkManager extends Serializable {
32+
/**
33+
* Fallback database name used by the driver when session has no database name configured and database discovery is unavailable.
34+
*/
35+
String FALLBACK_DATABASE_NAME = "";
36+
37+
/**
38+
* Updates database bookmarks by deleting the given previous bookmarks and adding the new bookmarks.
39+
*
40+
* @param database the database name
41+
* @param previousBookmarks the previous bookmarks
42+
* @param newBookmarks the new bookmarks
43+
*/
44+
void updateBookmarks(String database, Set<Bookmark> previousBookmarks, Set<Bookmark> newBookmarks);
45+
46+
/**
47+
* Gets an immutable set of bookmarks for a given database.
48+
*
49+
* @param database the database name
50+
* @return the set of bookmarks or an empty set if the database name is unknown to the bookmark manager
51+
*/
52+
Set<Bookmark> getBookmarks(String database);
53+
54+
/**
55+
* Gets an immutable set of bookmarks for all databases.
56+
* <p>
57+
* The additional set of database names provided to this method allows bookmark manager implementations to supply
58+
* bookmarks for databases that are not directly managed by them. For instance, a bookmark manager implementation
59+
* may use an external supplier of bookmarks for databases.
60+
*
61+
* @param additionalDatabases the set of additional database names
62+
* @return the set of bookmarks
63+
*/
64+
Set<Bookmark> getAllBookmarks(Set<String> additionalDatabases);
65+
66+
/**
67+
* Deletes bookmarks for the given databases.
68+
*
69+
* @param databases the set of database names
70+
*/
71+
void forget(Set<String> databases);
72+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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;
20+
21+
import java.util.Collections;
22+
import java.util.Map;
23+
import java.util.Set;
24+
import java.util.function.BiConsumer;
25+
import java.util.function.Function;
26+
27+
public final class BookmarkManagerConfig {
28+
private final Map<String, Set<Bookmark>> initialBookmarks;
29+
private final BiConsumer<String, Set<Bookmark>> updateListener;
30+
private final Function<String, Set<Bookmark>> extraBookmarksProvider;
31+
32+
private BookmarkManagerConfig(BookmarkManagerConfigBuilder builder) {
33+
this.initialBookmarks = builder.initialBookmarks;
34+
this.updateListener = builder.updateListener;
35+
this.extraBookmarksProvider = builder.extraBookmarksProvider;
36+
}
37+
38+
public Map<String, Set<Bookmark>> initialBookmarks() {
39+
return initialBookmarks;
40+
}
41+
42+
public BiConsumer<String, Set<Bookmark>> updateListener() {
43+
return updateListener;
44+
}
45+
46+
public Function<String, Set<Bookmark>> extraBookmarksProvider() {
47+
return extraBookmarksProvider;
48+
}
49+
50+
public static BookmarkManagerConfigBuilder builder() {
51+
return new BookmarkManagerConfigBuilder();
52+
}
53+
54+
public static class BookmarkManagerConfigBuilder {
55+
private Map<String, Set<Bookmark>> initialBookmarks = Collections.emptyMap();
56+
private BiConsumer<String, Set<Bookmark>> updateListener;
57+
private Function<String, Set<Bookmark>> extraBookmarksProvider;
58+
59+
private BookmarkManagerConfigBuilder() {}
60+
61+
public BookmarkManagerConfigBuilder withInitialBookmarks(Map<String, Set<Bookmark>> initialBookmarks) {
62+
this.initialBookmarks = initialBookmarks;
63+
return this;
64+
}
65+
66+
public BookmarkManagerConfigBuilder withUpdateListener(BiConsumer<String, Set<Bookmark>> updateListener) {
67+
this.updateListener = updateListener;
68+
return this;
69+
}
70+
71+
public BookmarkManagerConfigBuilder withExtraBookmarksProvider(
72+
Function<String, Set<Bookmark>> extraBookmarksProvider) {
73+
this.extraBookmarksProvider = extraBookmarksProvider;
74+
return this;
75+
}
76+
77+
public BookmarkManagerConfig build() {
78+
return new BookmarkManagerConfig(this);
79+
}
80+
}
81+
}
Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,14 @@
1616
* See the License for the specific language governing permissions and
1717
* limitations under the License.
1818
*/
19-
package org.neo4j.driver.internal;
19+
package org.neo4j.driver;
2020

21-
import java.util.Set;
22-
import org.neo4j.driver.Bookmark;
21+
import org.neo4j.driver.internal.Neo4jBookmarkManager;
2322

24-
/**
25-
* @since 2.0
26-
*/
27-
public class ReadOnlyBookmarksHolder implements BookmarksHolder {
28-
private final Set<Bookmark> bookmarks;
29-
30-
public ReadOnlyBookmarksHolder(Set<Bookmark> bookmarks) {
31-
this.bookmarks = bookmarks;
32-
}
33-
34-
@Override
35-
public Set<Bookmark> getBookmarks() {
36-
return bookmarks;
37-
}
23+
public interface BookmarkManagers {
3824

39-
@Override
40-
public void setBookmark(Bookmark bookmark) {
41-
// NO_OP
25+
static BookmarkManager bookmarkManager(BookmarkManagerConfig config) {
26+
return new Neo4jBookmarkManager(
27+
config.initialBookmarks(), config.updateListener(), config.extraBookmarksProvider());
4228
}
4329
}

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ public class Config implements Serializable {
9898
private final int eventLoopThreads;
9999
private final String userAgent;
100100
private final MetricsAdapter metricsAdapter;
101+
private final BookmarkManager bookmarkManager;
101102

102103
private Config(ConfigBuilder builder) {
103104
this.logging = builder.logging;
@@ -119,6 +120,8 @@ private Config(ConfigBuilder builder) {
119120

120121
this.eventLoopThreads = builder.eventLoopThreads;
121122
this.metricsAdapter = builder.metricsAdapter;
123+
124+
this.bookmarkManager = builder.bookmarkManager;
122125
}
123126

124127
/**
@@ -252,6 +255,15 @@ public String userAgent() {
252255
return userAgent;
253256
}
254257

258+
/**
259+
* A {@link BookmarkManager} implementation for the driver to use.
260+
*
261+
* @return bookmark implementation or {@code null}.
262+
*/
263+
public BookmarkManager bookmarkManager() {
264+
return bookmarkManager;
265+
}
266+
255267
/**
256268
* Used to build new config instances
257269
*/
@@ -272,6 +284,7 @@ public static class ConfigBuilder {
272284
private MetricsAdapter metricsAdapter = MetricsAdapter.DEV_NULL;
273285
private long fetchSize = FetchSizeUtil.DEFAULT_FETCH_SIZE;
274286
private int eventLoopThreads = 0;
287+
private BookmarkManager bookmarkManager;
275288

276289
private ConfigBuilder() {}
277290

@@ -645,6 +658,19 @@ public ConfigBuilder withUserAgent(String userAgent) {
645658
return this;
646659
}
647660

661+
/**
662+
* Sets a {@link BookmarkManager} implementation for the driver to use.
663+
* <p>
664+
* By default, bookmark manager is effectively disabled.
665+
*
666+
* @param bookmarkManager bookmark manager implementation. Providing {@code null} effectively disables bookmark manager.
667+
* @return this builder.
668+
*/
669+
public ConfigBuilder withBookmarkManager(BookmarkManager bookmarkManager) {
670+
this.bookmarkManager = bookmarkManager;
671+
return this;
672+
}
673+
648674
/**
649675
* Extracts the driver version from the driver jar MANIFEST.MF file.
650676
*/

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,15 @@ public class SessionConfig implements Serializable {
4545
private final String database;
4646
private final Long fetchSize;
4747
private final String impersonatedUser;
48+
private final boolean bypassBookmarkManager;
4849

4950
private SessionConfig(Builder builder) {
5051
this.bookmarks = builder.bookmarks;
5152
this.defaultAccessMode = builder.defaultAccessMode;
5253
this.database = builder.database;
5354
this.fetchSize = builder.fetchSize;
5455
this.impersonatedUser = builder.impersonatedUser;
56+
this.bypassBookmarkManager = builder.bypassBookmarkManager;
5557
}
5658

5759
/**
@@ -130,6 +132,15 @@ public Optional<String> impersonatedUser() {
130132
return Optional.ofNullable(impersonatedUser);
131133
}
132134

135+
/**
136+
* Determines if {@link BookmarkManager} configured at driver level should be bypassed.
137+
*
138+
* @return {@code true} if bookmark manager should be bypassed and not otherwise.
139+
*/
140+
public boolean bypassBookmarkManager() {
141+
return bypassBookmarkManager;
142+
}
143+
133144
@Override
134145
public boolean equals(Object o) {
135146
if (this == o) {
@@ -143,12 +154,13 @@ public boolean equals(Object o) {
143154
&& defaultAccessMode == that.defaultAccessMode
144155
&& Objects.equals(database, that.database)
145156
&& Objects.equals(fetchSize, that.fetchSize)
146-
&& Objects.equals(impersonatedUser, that.impersonatedUser);
157+
&& Objects.equals(impersonatedUser, that.impersonatedUser)
158+
&& Objects.equals(bypassBookmarkManager, that.bypassBookmarkManager);
147159
}
148160

149161
@Override
150162
public int hashCode() {
151-
return Objects.hash(bookmarks, defaultAccessMode, database, impersonatedUser);
163+
return Objects.hash(bookmarks, defaultAccessMode, database, impersonatedUser, bypassBookmarkManager);
152164
}
153165

154166
@Override
@@ -167,6 +179,7 @@ public static class Builder {
167179
private AccessMode defaultAccessMode = AccessMode.WRITE;
168180
private String database = null;
169181
private String impersonatedUser = null;
182+
private boolean bypassBookmarkManager = false;
170183

171184
private Builder() {}
172185

@@ -292,6 +305,17 @@ public Builder withImpersonatedUser(String impersonatedUser) {
292305
return this;
293306
}
294307

308+
/**
309+
* Bypass {@link BookmarkManager} configured at driver level using {@link org.neo4j.driver.Config.ConfigBuilder#withBookmarkManager(BookmarkManager)}.
310+
*
311+
* @param bypass bypass if {@code true}, use otherwise.
312+
* @return this builder.
313+
*/
314+
public Builder withBypassedBookmarkManager(boolean bypass) {
315+
this.bypassBookmarkManager = bypass;
316+
return this;
317+
}
318+
295319
public SessionConfig build() {
296320
return new SessionConfig(this);
297321
}

driver/src/main/java/org/neo4j/driver/internal/BookmarksHolder.java renamed to driver/src/main/java/org/neo4j/driver/internal/DatabaseBookmark.java

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,6 @@
1818
*/
1919
package org.neo4j.driver.internal;
2020

21-
import java.util.Collections;
22-
import java.util.Set;
2321
import org.neo4j.driver.Bookmark;
2422

25-
public interface BookmarksHolder {
26-
Set<Bookmark> getBookmarks();
27-
28-
void setBookmark(Bookmark bookmark);
29-
30-
BookmarksHolder NO_OP = new BookmarksHolder() {
31-
@Override
32-
public Set<Bookmark> getBookmarks() {
33-
return Collections.emptySet();
34-
}
35-
36-
@Override
37-
public void setBookmark(Bookmark bookmark) {}
38-
};
39-
}
23+
public record DatabaseBookmark(String databaseName, Bookmark bookmark) {}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,8 @@ protected InternalDriver createRoutingDriver(
260260
*/
261261
protected InternalDriver createDriver(
262262
SecurityPlan securityPlan, SessionFactory sessionFactory, MetricsProvider metricsProvider, Config config) {
263-
return new InternalDriver(securityPlan, sessionFactory, metricsProvider, config.logging());
263+
return new InternalDriver(
264+
securityPlan, sessionFactory, metricsProvider, config.logging(), config.bookmarkManager());
264265
}
265266

266267
/**

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import java.util.concurrent.CompletionStage;
2424
import java.util.concurrent.atomic.AtomicBoolean;
25+
import org.neo4j.driver.BookmarkManager;
2526
import org.neo4j.driver.Driver;
2627
import org.neo4j.driver.Logger;
2728
import org.neo4j.driver.Logging;
@@ -50,15 +51,19 @@ public class InternalDriver implements Driver {
5051
private AtomicBoolean closed = new AtomicBoolean(false);
5152
private final MetricsProvider metricsProvider;
5253

54+
private final BookmarkManager bookmarkManager;
55+
5356
InternalDriver(
5457
SecurityPlan securityPlan,
5558
SessionFactory sessionFactory,
5659
MetricsProvider metricsProvider,
57-
Logging logging) {
60+
Logging logging,
61+
BookmarkManager bookmarkManager) {
5862
this.securityPlan = securityPlan;
5963
this.sessionFactory = sessionFactory;
6064
this.metricsProvider = metricsProvider;
6165
this.log = logging.getLog(getClass());
66+
this.bookmarkManager = bookmarkManager != null ? bookmarkManager : new NoOpBookmarkManager();
6267
}
6368

6469
@Override
@@ -164,7 +169,8 @@ private static RuntimeException driverCloseException() {
164169

165170
public NetworkSession newSession(SessionConfig config) {
166171
assertOpen();
167-
NetworkSession session = sessionFactory.newInstance(config);
172+
var bookmarkManager = config.bypassBookmarkManager() ? new NoOpBookmarkManager() : this.bookmarkManager;
173+
NetworkSession session = sessionFactory.newInstance(config, bookmarkManager);
168174
if (closed.get()) {
169175
// session does not immediately acquire connection, it is fine to just throw
170176
throw driverCloseException();

0 commit comments

Comments
 (0)