GH-2515 - Add a Noop bookmark manager.

Closes #2515.
This commit is contained in:
Michael Simons
2022-03-31 13:01:30 +02:00
parent ed1d47bf37
commit 2bd53c6296
4 changed files with 109 additions and 1 deletions

View File

@@ -141,6 +141,8 @@ interface and its only implementation `org.springframework.data.neo4j.bookmark.C
SDN uses bookmarks for all transactions, without configuration.
You can remove the bean declaration of `CaffeineBookmarkManager` as well as the dependency to `com.github.ben-manes.caffeine:caffeine`.
If you absolutely must, you can disable the automatic bookmark management by following <<faq.bookmarks.noop, these instructions>>.
[[migrating.autoindex]]
=== Automatic creation of constraints and indexes

View File

@@ -458,7 +458,7 @@ No, you don't.
SDN uses Neo4j Causal Cluster bookmarks internally without any configuration on your side required.
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.
[[faq.bookmarks]]
[[faq.bookmarks.seeding]]
== Can I retrieve the latest Bookmarks or seed the transaction manager?
As mentioned briefly in <<migrating.bookmarks>>, there is no need to configure anything with regard to bookmarks.
@@ -498,6 +498,8 @@ import org.neo4j.driver.Driver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
@@ -528,6 +530,46 @@ public class BookmarkSeedingConfig {
WARNING: There is *no* need to do any of these things above, unless your application has the need to access or provide
this data. If in doubt, don't do either.
[[faq.bookmarks.noop]]
== Can I disable bookmark management?
We provide a Noop bookmark manager that effectively disables bookmark management.
WARNING: Use this bookmark manager at your own risk, it will effectively disable any bookmark management by dropping all
bookmarks and never supplying any. In a cluster you will be at a high risk of experiencing stale reads. In a single
instance it will most likely not make any difference.
+
In a cluster this can be a sensible approach only and if only you can tolerate stale reads and are not in danger of
overwriting old data.
You need to provide the following configuration in your system and make sure that SDN uses the transaction manager:
[source,java,indent=0,tabsize=4]
.BookmarksDisabledConfig.java
----
import org.neo4j.driver.Driver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
@Configuration
public class BookmarksDisabledConfig {
@Bean
public PlatformTransactionManager transactionManager(
Driver driver, DatabaseSelectionProvider databaseNameProvider) {
Neo4jBookmarkManager bookmarkManager = Neo4jBookmarkManager.noop(); // <.>
return new Neo4jTransactionManager(
driver, databaseNameProvider, bookmarkManager);
}
}
----
<.> Get an instance of the Noop bookmark manager
[[faq.annotations.specific]]
== Do I need to use Neo4j specific annotations?

View File

@@ -60,6 +60,24 @@ public final class Neo4jBookmarkManager {
return new Neo4jBookmarkManager(bookmarksSupplier);
}
/**
* Use this bookmark manager at your own risk, it will effectively disable any bookmark management by dropping all
* bookmarks and never supplying any. In a cluster you will be at a high risk of experiencing stale reads. In a single
* instance it will most likely not make any difference.
* <p>
* In a cluster this can be a sensible approach only and if only you can tolerate stale reads and are not in danger of
* overwriting old data.
*
* @return A noop bookmark manager, dropping new bookmarks immediately, never supplying bookmarks.
* @since 6.1.11
*/
@API(status = API.Status.STABLE, since = "6.1.11")
public static Neo4jBookmarkManager noop() {
return new Neo4jBookmarkManager(null, true);
}
private final boolean noop;
private final Set<Bookmark> bookmarks = new HashSet<>();
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
@@ -72,11 +90,20 @@ public final class Neo4jBookmarkManager {
private ApplicationEventPublisher applicationEventPublisher;
private Neo4jBookmarkManager(@Nullable Supplier<Set<Bookmark>> bookmarksSupplier) {
this(bookmarksSupplier, false);
}
private Neo4jBookmarkManager(@Nullable Supplier<Set<Bookmark>> bookmarksSupplier, boolean noop) {
this.bookmarksSupplier = bookmarksSupplier == null ? () -> Collections.emptySet() : bookmarksSupplier;
this.noop = noop;
}
Collection<Bookmark> getBookmarks() {
if (noop) {
return Collections.emptyList();
}
try {
read.lock();
HashSet<Bookmark> bookmarksToUse = new HashSet<>(this.bookmarks);
@@ -88,6 +115,11 @@ public final class Neo4jBookmarkManager {
}
void updateBookmarks(Collection<Bookmark> usedBookmarks, Bookmark lastBookmark) {
if (noop) {
return;
}
try {
write.lock();
bookmarks.removeAll(usedBookmarks);

View File

@@ -24,6 +24,7 @@ import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.neo4j.driver.Bookmark;
@@ -119,6 +120,37 @@ class Neo4jBookmarkManagerTest {
assertThat(bookmarkManager.getBookmarks()).containsExactly(newBookmark);
}
@Nested
class NoopTests {
@Test
void shouldAlwaysReturnEmptyList() {
Neo4jBookmarkManager bookmarkManager = Neo4jBookmarkManager.noop();
assertThat(bookmarkManager.getBookmarks())
.isSameAs(Collections.emptyList()) // Might not be that sane to check that but alas
.isEmpty();
}
@Test
void shouldNeverAcceptBookmarks() {
BookmarkForTesting bookmark = new BookmarkForTesting(Collections.singleton("a"));
AtomicBoolean asserted = new AtomicBoolean(false);
final Neo4jBookmarkManager bookmarkManager = Neo4jBookmarkManager.noop();
bookmarkManager.setApplicationEventPublisher(event -> {
assertThat(((Neo4jBookmarksUpdatedEvent) event).getBookmarks()).containsExactly(bookmark);
asserted.set(true);
});
bookmarkManager.updateBookmarks(new HashSet<>(), bookmark);
assertThat(asserted).isFalse();
}
}
static private class BookmarkForTesting implements Bookmark {
private final Set<String> values;