Skip to content

Commit c96f12f

Browse files
GH-2515 - Add a Noop bookmark manager.
Closes #2515.
1 parent c4aa621 commit c96f12f

File tree

4 files changed

+109
-1
lines changed

4 files changed

+109
-1
lines changed

src/main/asciidoc/appendix/migrating.adoc

+2
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ interface and its only implementation `org.springframework.data.neo4j.bookmark.C
138138
SDN uses Bookmarks for all transactions, without configuration.
139139
You can remove the bean declaration of `CaffeineBookmarkManager` as well as the dependency to `com.github.ben-manes.caffeine:caffeine`.
140140

141+
If you absolutely must, you can disable the automatic bookmark management by following <<faq.bookmarks.noop, these instructions>>.
142+
141143
[[migrating.autoindex]]
142144
=== Automatic creation of constraints and indexes
143145

src/main/asciidoc/faq/faq.adoc

+43-1
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ No, you don't.
382382
SDN uses Neo4j Causal Cluster bookmarks internally without any configuration on your side required.
383383
Transactions in the same thread or the same reactive stream following each other will be able to read their previously changed values as you would expect.
384384

385-
[[faq.bookmarks]]
385+
[[faq.bookmarks.seeding]]
386386
== Can I retrieve the latest Bookmarks or seed the transaction manager?
387387

388388
As mentioned briefly in <<migrating.bookmarks>>, there is no need to configure anything with regard to bookmarks.
@@ -422,6 +422,8 @@ import org.neo4j.driver.Driver;
422422
import org.springframework.context.annotation.Bean;
423423
import org.springframework.context.annotation.Configuration;
424424
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
425+
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
426+
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
425427
import org.springframework.transaction.PlatformTransactionManager;
426428
427429
@Configuration
@@ -452,6 +454,46 @@ public class BookmarkSeedingConfig {
452454
WARNING: There is *no* need to do any of these things above, unless your application has the need to access or provide
453455
this data. If in doubt, don't do either.
454456

457+
[[faq.bookmarks.noop]]
458+
== Can I disable bookmark management?
459+
460+
We provide a Noop bookmark manager that effectively disables bookmark management.
461+
462+
WARNING: Use this bookmark manager at your own risk, it will effectively disable any bookmark management by dropping all
463+
bookmarks and never supplying any. In a cluster you will be at a high risk of experiencing stale reads. In a single
464+
instance it will most likely not make any difference.
465+
+
466+
In a cluster this can be a sensible approach only and if only you can tolerate stale reads and are not in danger of
467+
overwriting old data.
468+
469+
You need to provide the following configuration in your system and make sure that SDN uses the transaction manager:
470+
471+
[source,java,indent=0,tabsize=4]
472+
.BookmarksDisabledConfig.java
473+
----
474+
import org.neo4j.driver.Driver;
475+
import org.springframework.context.annotation.Bean;
476+
import org.springframework.context.annotation.Configuration;
477+
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
478+
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
479+
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
480+
import org.springframework.transaction.PlatformTransactionManager;
481+
482+
@Configuration
483+
public class BookmarksDisabledConfig {
484+
485+
@Bean
486+
public PlatformTransactionManager transactionManager(
487+
Driver driver, DatabaseSelectionProvider databaseNameProvider) {
488+
489+
Neo4jBookmarkManager bookmarkManager = Neo4jBookmarkManager.noop(); // <.>
490+
return new Neo4jTransactionManager(
491+
driver, databaseNameProvider, bookmarkManager);
492+
}
493+
}
494+
----
495+
<.> Get an instance of the Noop bookmark manager
496+
455497
[[faq.annotations.specific]]
456498
== Do I need to use Neo4j specific annotations?
457499

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

+32
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,24 @@ public static Neo4jBookmarkManager create(@Nullable Supplier<Set<Bookmark>> book
6060
return new Neo4jBookmarkManager(bookmarksSupplier);
6161
}
6262

63+
/**
64+
* Use this bookmark manager at your own risk, it will effectively disable any bookmark management by dropping all
65+
* bookmarks and never supplying any. In a cluster you will be at a high risk of experiencing stale reads. In a single
66+
* instance it will most likely not make any difference.
67+
* <p>
68+
* In a cluster this can be a sensible approach only and if only you can tolerate stale reads and are not in danger of
69+
* overwriting old data.
70+
*
71+
* @return A noop bookmark manager, dropping new bookmarks immediately, never supplying bookmarks.
72+
* @since 6.1.11
73+
*/
74+
@API(status = API.Status.STABLE, since = "6.1.11")
75+
public static Neo4jBookmarkManager noop() {
76+
return new Neo4jBookmarkManager(null, true);
77+
}
78+
79+
private final boolean noop;
80+
6381
private final Set<Bookmark> bookmarks = new HashSet<>();
6482

6583
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
@@ -72,11 +90,20 @@ public static Neo4jBookmarkManager create(@Nullable Supplier<Set<Bookmark>> book
7290
private ApplicationEventPublisher applicationEventPublisher;
7391

7492
private Neo4jBookmarkManager(@Nullable Supplier<Set<Bookmark>> bookmarksSupplier) {
93+
this(bookmarksSupplier, false);
94+
}
95+
96+
private Neo4jBookmarkManager(@Nullable Supplier<Set<Bookmark>> bookmarksSupplier, boolean noop) {
7597
this.bookmarksSupplier = bookmarksSupplier == null ? () -> Collections.emptySet() : bookmarksSupplier;
98+
this.noop = noop;
7699
}
77100

78101
Collection<Bookmark> getBookmarks() {
79102

103+
if (noop) {
104+
return Collections.emptyList();
105+
}
106+
80107
try {
81108
read.lock();
82109
HashSet<Bookmark> bookmarksToUse = new HashSet<>(this.bookmarks);
@@ -88,6 +115,11 @@ Collection<Bookmark> getBookmarks() {
88115
}
89116

90117
void updateBookmarks(Collection<Bookmark> usedBookmarks, Bookmark lastBookmark) {
118+
119+
if (noop) {
120+
return;
121+
}
122+
91123
try {
92124
write.lock();
93125
bookmarks.removeAll(usedBookmarks);

src/test/java/org/springframework/data/neo4j/core/transaction/Neo4jBookmarkManagerTest.java

+32
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Set;
2525
import java.util.concurrent.atomic.AtomicBoolean;
2626

27+
import org.junit.jupiter.api.Nested;
2728
import org.junit.jupiter.api.Test;
2829
import org.neo4j.driver.Bookmark;
2930

@@ -119,6 +120,37 @@ void updatesPreviouslyUnknownBookmarks() {
119120
assertThat(bookmarkManager.getBookmarks()).containsExactly(newBookmark);
120121
}
121122

123+
@Nested
124+
class NoopTests {
125+
126+
127+
@Test
128+
void shouldAlwaysReturnEmptyList() {
129+
130+
Neo4jBookmarkManager bookmarkManager = Neo4jBookmarkManager.noop();
131+
assertThat(bookmarkManager.getBookmarks())
132+
.isSameAs(Collections.emptyList()) // Might not be that sane to check that but alas
133+
.isEmpty();
134+
}
135+
136+
137+
@Test
138+
void shouldNeverAcceptBookmarks() {
139+
140+
BookmarkForTesting bookmark = new BookmarkForTesting(Collections.singleton("a"));
141+
AtomicBoolean asserted = new AtomicBoolean(false);
142+
143+
final Neo4jBookmarkManager bookmarkManager = Neo4jBookmarkManager.noop();
144+
bookmarkManager.setApplicationEventPublisher(event -> {
145+
assertThat(((Neo4jBookmarksUpdatedEvent) event).getBookmarks()).containsExactly(bookmark);
146+
asserted.set(true);
147+
});
148+
149+
bookmarkManager.updateBookmarks(new HashSet<>(), bookmark);
150+
assertThat(asserted).isFalse();
151+
}
152+
}
153+
122154
static private class BookmarkForTesting implements Bookmark {
123155
private final Set<String> values;
124156

0 commit comments

Comments
 (0)