Skip to content

Commit 4ea4905

Browse files
committed
GH-2755 - Use bookmark manager in Client.
Also make the `AbstractNeo4jConfig` and its reactive counter-part use the same defined bookmark manager in transaction manager and client. Closes #2755 (cherry picked from commit 21c8a42)
1 parent aecf61e commit 4ea4905

40 files changed

+271
-90
lines changed

pom.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
<archunit.version>0.23.1</archunit.version>
7373
<asciidoctor-maven-plugin.version>2.1.0</asciidoctor-maven-plugin.version>
7474
<asciidoctorj-diagram.version>2.1.0</asciidoctorj-diagram.version>
75+
<blockhound.version>1.0.8.RELEASE</blockhound.version>
7576
<byte-buddy.version>1.14.3</byte-buddy.version>
7677
<cdi>3.0.1</cdi>
7778
<checkstyle.skip>${skipTests}</checkstyle.skip>
@@ -237,6 +238,11 @@
237238
<type>pom</type>
238239
<scope>import</scope>
239240
</dependency>
241+
<dependency>
242+
<groupId>io.projectreactor.tools</groupId>
243+
<artifactId>blockhound</artifactId>
244+
<version>${blockhound.version}</version>
245+
</dependency>
240246
</dependencies>
241247
</dependencyManagement>
242248

@@ -449,6 +455,11 @@
449455
</exclusion>
450456
</exclusions>
451457
</dependency>
458+
<dependency>
459+
<groupId>io.projectreactor.tools</groupId>
460+
<artifactId>blockhound</artifactId>
461+
<scope>test</scope>
462+
</dependency>
452463
</dependencies>
453464

454465
<repositories>

src/main/java/org/springframework/data/neo4j/config/AbstractNeo4jConfig.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.data.neo4j.core.Neo4jTemplate;
2828
import org.springframework.data.neo4j.core.UserSelectionProvider;
2929
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
30+
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
3031
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
3132
import org.springframework.data.neo4j.repository.config.Neo4jRepositoryConfigurationExtension;
3233
import org.springframework.lang.Nullable;
@@ -47,6 +48,9 @@ public abstract class AbstractNeo4jConfig extends Neo4jConfigurationSupport {
4748
@Autowired
4849
private ObjectProvider<UserSelectionProvider> userSelectionProviders;
4950

51+
@Autowired
52+
private Neo4jBookmarkManager bookmarkManager;
53+
5054
/**
5155
* The driver to be used for interacting with Neo4j.
5256
*
@@ -66,6 +70,7 @@ public Neo4jClient neo4jClient(Driver driver, DatabaseSelectionProvider database
6670
return Neo4jClient.with(driver)
6771
.withDatabaseSelectionProvider(databaseSelectionProvider)
6872
.withUserSelectionProvider(getUserSelectionProvider())
73+
.withNeo4jBookmarkManager(bookmarkManager)
6974
.build();
7075
}
7176

@@ -94,9 +99,15 @@ public PlatformTransactionManager transactionManager(Driver driver, DatabaseSele
9499
.with(driver)
95100
.withDatabaseSelectionProvider(databaseSelectionProvider)
96101
.withUserSelectionProvider(getUserSelectionProvider())
102+
.withBookmarkManager(bookmarkManager)
97103
.build();
98104
}
99105

106+
@Bean
107+
public Neo4jBookmarkManager bookmarkManager() {
108+
return Neo4jBookmarkManager.create();
109+
}
110+
100111
/**
101112
* Configures the database selection provider.
102113
*

src/main/java/org/springframework/data/neo4j/config/AbstractReactiveNeo4jConfig.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.data.neo4j.core.ReactiveNeo4jTemplate;
2727
import org.springframework.data.neo4j.core.ReactiveUserSelectionProvider;
2828
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
29+
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
2930
import org.springframework.data.neo4j.core.transaction.ReactiveNeo4jTransactionManager;
3031
import org.springframework.data.neo4j.repository.config.ReactiveNeo4jRepositoryConfigurationExtension;
3132
import org.springframework.lang.Nullable;
@@ -47,6 +48,9 @@ public abstract class AbstractReactiveNeo4jConfig extends Neo4jConfigurationSupp
4748
@Autowired
4849
private ObjectProvider<ReactiveUserSelectionProvider> userSelectionProviders;
4950

51+
@Autowired
52+
private Neo4jBookmarkManager bookmarkManager;
53+
5054
/**
5155
* The driver to be used for interacting with Neo4j.
5256
*
@@ -66,6 +70,7 @@ public ReactiveNeo4jClient neo4jClient(Driver driver, ReactiveDatabaseSelectionP
6670
return ReactiveNeo4jClient.with(driver)
6771
.withDatabaseSelectionProvider(databaseSelectionProvider)
6872
.withUserSelectionProvider(getUserSelectionProvider())
73+
.withNeo4jBookmarkManager(bookmarkManager)
6974
.build();
7075
}
7176

@@ -94,9 +99,15 @@ public ReactiveTransactionManager reactiveTransactionManager(Driver driver,
9499
return ReactiveNeo4jTransactionManager.with(driver)
95100
.withDatabaseSelectionProvider(databaseSelectionProvider)
96101
.withUserSelectionProvider(getUserSelectionProvider())
102+
.withBookmarkManager(bookmarkManager)
97103
.build();
98104
}
99105

106+
@Bean
107+
public Neo4jBookmarkManager bookmarkManager() {
108+
return Neo4jBookmarkManager.createReactive();
109+
}
110+
100111
/**
101112
* Configures the database name provider.
102113
*

src/main/java/org/springframework/data/neo4j/core/DefaultNeo4jClient.java

Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,9 @@
1616
package org.springframework.data.neo4j.core;
1717

1818
import java.util.Collection;
19-
import java.util.Collections;
20-
import java.util.HashSet;
2119
import java.util.Map;
2220
import java.util.Objects;
2321
import java.util.Optional;
24-
import java.util.Set;
25-
import java.util.concurrent.locks.ReentrantReadWriteLock;
2622
import java.util.function.BiConsumer;
2723
import java.util.function.BiFunction;
2824
import java.util.function.Function;
@@ -45,6 +41,7 @@
4541
import org.springframework.dao.DataAccessException;
4642
import org.springframework.dao.support.PersistenceExceptionTranslator;
4743
import org.springframework.data.neo4j.core.convert.Neo4jConversions;
44+
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
4845
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
4946
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionUtils;
5047
import org.springframework.lang.Nullable;
@@ -67,15 +64,15 @@ final class DefaultNeo4jClient implements Neo4jClient {
6764
private final ConversionService conversionService;
6865
private final Neo4jPersistenceExceptionTranslator persistenceExceptionTranslator = new Neo4jPersistenceExceptionTranslator();
6966

70-
// Basically a local bookmark manager
71-
private final Set<Bookmark> bookmarks = new HashSet<>();
72-
private final ReentrantReadWriteLock bookmarksLock = new ReentrantReadWriteLock();
67+
// Local bookmark manager when using outside managed transactions
68+
private final Neo4jBookmarkManager bookmarkManager;
7369

7470
DefaultNeo4jClient(Builder builder) {
7571

7672
this.driver = builder.driver;
7773
this.databaseSelectionProvider = builder.databaseSelectionProvider;
7874
this.userSelectionProvider = builder.userSelectionProvider;
75+
this.bookmarkManager = builder.bookmarkManager != null ? builder.bookmarkManager : Neo4jBookmarkManager.create();
7976

8077
this.conversionService = new DefaultConversionService();
8178
Optional.ofNullable(builder.neo4jConversions).orElseGet(Neo4jConversions::new).registerConvertersIn((ConverterRegistry) conversionService);
@@ -85,29 +82,13 @@ final class DefaultNeo4jClient implements Neo4jClient {
8582
public QueryRunner getQueryRunner(DatabaseSelection databaseSelection, UserSelection impersonatedUser) {
8683

8784
QueryRunner queryRunner = Neo4jTransactionManager.retrieveTransaction(driver, databaseSelection, impersonatedUser);
88-
Collection<Bookmark> lastBookmarks = Collections.emptySet();
85+
Collection<Bookmark> lastBookmarks = bookmarkManager.getBookmarks();
86+
8987
if (queryRunner == null) {
90-
ReentrantReadWriteLock.ReadLock lock = bookmarksLock.readLock();
91-
try {
92-
lock.lock();
93-
lastBookmarks = new HashSet<>(bookmarks);
94-
queryRunner = driver.session(Neo4jTransactionUtils.sessionConfig(false, lastBookmarks, databaseSelection, impersonatedUser));
95-
} finally {
96-
lock.unlock();
97-
}
88+
queryRunner = driver.session(Neo4jTransactionUtils.sessionConfig(false, lastBookmarks, databaseSelection, impersonatedUser));
9889
}
9990

100-
return new DelegatingQueryRunner(queryRunner, lastBookmarks, (usedBookmarks, newBookmarks) -> {
101-
102-
ReentrantReadWriteLock.WriteLock lock = bookmarksLock.writeLock();
103-
try {
104-
lock.lock();
105-
bookmarks.removeAll(usedBookmarks);
106-
bookmarks.addAll(newBookmarks);
107-
} finally {
108-
lock.unlock();
109-
}
110-
});
91+
return new DelegatingQueryRunner(queryRunner, lastBookmarks, bookmarkManager::updateBookmarks);
11192
}
11293

11394
private static class DelegatingQueryRunner implements QueryRunner {

src/main/java/org/springframework/data/neo4j/core/DefaultReactiveNeo4jClient.java

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.springframework.core.convert.support.DefaultConversionService;
3232
import org.springframework.dao.DataAccessException;
3333
import org.springframework.data.neo4j.core.convert.Neo4jConversions;
34+
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
3435
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionUtils;
3536
import org.springframework.data.neo4j.core.transaction.ReactiveNeo4jTransactionManager;
3637
import org.springframework.lang.Nullable;
@@ -43,12 +44,8 @@
4344
import reactor.util.function.Tuples;
4445

4546
import java.util.Collection;
46-
import java.util.Collections;
47-
import java.util.HashSet;
4847
import java.util.Map;
4948
import java.util.Optional;
50-
import java.util.Set;
51-
import java.util.concurrent.locks.ReentrantReadWriteLock;
5249
import java.util.function.BiConsumer;
5350
import java.util.function.BiFunction;
5451
import java.util.function.Function;
@@ -70,9 +67,8 @@ final class DefaultReactiveNeo4jClient implements ReactiveNeo4jClient {
7067
private final ConversionService conversionService;
7168
private final Neo4jPersistenceExceptionTranslator persistenceExceptionTranslator = new Neo4jPersistenceExceptionTranslator();
7269

73-
// Basically a local bookmark manager
74-
private final Set<Bookmark> bookmarks = new HashSet<>();
75-
private final ReentrantReadWriteLock bookmarksLock = new ReentrantReadWriteLock();
70+
// Local bookmark manager when using outside managed transactions
71+
private final Neo4jBookmarkManager bookmarkManager;
7672

7773
DefaultReactiveNeo4jClient(Builder builder) {
7874

@@ -82,6 +78,7 @@ final class DefaultReactiveNeo4jClient implements ReactiveNeo4jClient {
8278

8379
this.conversionService = new DefaultConversionService();
8480
Optional.ofNullable(builder.neo4jConversions).orElseGet(Neo4jConversions::new).registerConvertersIn((ConverterRegistry) conversionService);
81+
this.bookmarkManager = builder.bookmarkManager != null ? builder.bookmarkManager : Neo4jBookmarkManager.createReactive();
8582
}
8683

8784
@Override
@@ -91,27 +88,12 @@ public Mono<ReactiveQueryRunner> getQueryRunner(Mono<DatabaseSelection> database
9188
.flatMap(targetDatabaseAndUser ->
9289
ReactiveNeo4jTransactionManager.retrieveReactiveTransaction(driver, targetDatabaseAndUser.getT1(), targetDatabaseAndUser.getT2())
9390
.map(ReactiveQueryRunner.class::cast)
94-
.zipWith(Mono.just(Collections.<Bookmark>emptySet()))
91+
.zipWith(Mono.just(bookmarkManager.getBookmarks()))
9592
.switchIfEmpty(Mono.fromSupplier(() -> {
96-
ReentrantReadWriteLock.ReadLock lock = bookmarksLock.readLock();
97-
try {
98-
lock.lock();
99-
Set<Bookmark> lastBookmarks = new HashSet<>(bookmarks);
100-
return Tuples.of(driver.session(ReactiveSession.class, Neo4jTransactionUtils.sessionConfig(false, lastBookmarks, targetDatabaseAndUser.getT1(), targetDatabaseAndUser.getT2())), lastBookmarks);
101-
} finally {
102-
lock.unlock();
103-
}
93+
Collection<Bookmark> lastBookmarks = bookmarkManager.getBookmarks();
94+
return Tuples.of(driver.session(ReactiveSession.class, Neo4jTransactionUtils.sessionConfig(false, lastBookmarks, targetDatabaseAndUser.getT1(), targetDatabaseAndUser.getT2())), lastBookmarks);
10495
})))
105-
.map(t -> new DelegatingQueryRunner(t.getT1(), t.getT2(), (usedBookmarks, newBookmarks) -> {
106-
ReentrantReadWriteLock.WriteLock lock = bookmarksLock.writeLock();
107-
try {
108-
lock.lock();
109-
bookmarks.removeAll(usedBookmarks);
110-
bookmarks.addAll(newBookmarks);
111-
} finally {
112-
lock.unlock();
113-
}
114-
}));
96+
.map(t -> new DelegatingQueryRunner(t.getT1(), t.getT2(), bookmarkManager::updateBookmarks));
11597
}
11698

11799
private static class DelegatingQueryRunner implements ReactiveQueryRunner {

src/main/java/org/springframework/data/neo4j/core/Neo4jClient.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.neo4j.driver.types.TypeSystem;
3232
import org.springframework.core.log.LogAccessor;
3333
import org.springframework.data.neo4j.core.convert.Neo4jConversions;
34+
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
3435
import org.springframework.lang.Nullable;
3536

3637
/**
@@ -79,6 +80,9 @@ class Builder {
7980
@Nullable
8081
Neo4jConversions neo4jConversions;
8182

83+
@Nullable
84+
Neo4jBookmarkManager bookmarkManager;
85+
8286
private Builder(Driver driver) {
8387
this.driver = driver;
8488
}
@@ -121,6 +125,20 @@ public Builder withNeo4jConversions(Neo4jConversions neo4jConversions) {
121125
return this;
122126
}
123127

128+
/**
129+
* Configures the {@link Neo4jBookmarkManager} to use.
130+
* This should be the same instance as provided for the {@link org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager}
131+
* respectively the {@link org.springframework.data.neo4j.core.transaction.ReactiveNeo4jTransactionManager}.
132+
*
133+
* @param bookmarkManager Neo4jBookmarkManager instance that is shared with the transaction manager.
134+
* @return The builder
135+
* @since 7.1.2
136+
*/
137+
public Builder withNeo4jBookmarkManager(Neo4jBookmarkManager bookmarkManager) {
138+
this.bookmarkManager = bookmarkManager;
139+
return this;
140+
}
141+
124142
public Neo4jClient build() {
125143
return new DefaultNeo4jClient(this);
126144
}

src/main/java/org/springframework/data/neo4j/core/ReactiveNeo4jClient.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.springframework.data.neo4j.core;
1717

18+
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
1819
import reactor.core.publisher.Flux;
1920
import reactor.core.publisher.Mono;
2021

@@ -82,6 +83,9 @@ class Builder {
8283
@Nullable
8384
Neo4jConversions neo4jConversions;
8485

86+
@Nullable
87+
Neo4jBookmarkManager bookmarkManager;
88+
8589
private Builder(Driver driver) {
8690
this.driver = driver;
8791
}
@@ -124,6 +128,20 @@ public Builder withNeo4jConversions(Neo4jConversions neo4jConversions) {
124128
return this;
125129
}
126130

131+
/**
132+
* Configures the {@link Neo4jBookmarkManager} to use.
133+
* This should be the same instance as provided for the {@link org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager}
134+
* respectively the {@link org.springframework.data.neo4j.core.transaction.ReactiveNeo4jTransactionManager}.
135+
*
136+
* @param bookmarkManager Neo4jBookmarkManager instance that is shared with the transaction manager.
137+
* @return The builder
138+
* @since 7.1.2
139+
*/
140+
public Builder withNeo4jBookmarkManager(Neo4jBookmarkManager bookmarkManager) {
141+
this.bookmarkManager = bookmarkManager;
142+
return this;
143+
}
144+
127145
public ReactiveNeo4jClient build() {
128146
return new DefaultReactiveNeo4jClient(this);
129147
}

src/main/java/org/springframework/data/neo4j/core/transaction/Neo4jBookmarkManager.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ static Neo4jBookmarkManager create() {
4141
return new DefaultBookmarkManager(null);
4242
}
4343

44+
/**
45+
* @return default reactive version of bookmark manager
46+
*/
47+
static Neo4jBookmarkManager createReactive() {
48+
return new ReactiveDefaultBookmarkManager(null);
49+
}
50+
4451
/**
4552
* Use this factory method to add supplier of initial "seeding" bookmarks to the transaction managers
4653
* <p>
@@ -55,6 +62,20 @@ static Neo4jBookmarkManager create(@Nullable Supplier<Set<Bookmark>> bookmarksSu
5562
return new DefaultBookmarkManager(bookmarksSupplier);
5663
}
5764

65+
/**
66+
* Use this factory method to add supplier of initial "seeding" bookmarks to the transaction managers
67+
* <p>
68+
* While this class will make sure that the supplier will be accessed in a thread-safe manner,
69+
* it is the caller's duty to provide a thread safe supplier (not changing the seed during a call, etc.).
70+
*
71+
* @param bookmarksSupplier A supplier for seeding bookmarks, can be null. The supplier is free to provide different
72+
* bookmarks on each call.
73+
* @return A reactive bookmark manager
74+
*/
75+
static Neo4jBookmarkManager createReactive(@Nullable Supplier<Set<Bookmark>> bookmarksSupplier) {
76+
return new ReactiveDefaultBookmarkManager(bookmarksSupplier);
77+
}
78+
5879
/**
5980
* Use this bookmark manager at your own risk, it will effectively disable any bookmark management by dropping all
6081
* bookmarks and never supplying any. In a cluster you will be at a high risk of experiencing stale reads. In a single

0 commit comments

Comments
 (0)