This is an automated email from the ASF dual-hosted git repository.
baedke pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
The following commit(s) were added to refs/heads/trunk by this push:
new d96c9d0174 OAK-10660: DocumentNodeStore: avoid repeated commits of
:childOrder in branch commits (#1317)
d96c9d0174 is described below
commit d96c9d0174120ef9998ff17e93e313c6430668f7
Author: Julian Reschke <[email protected]>
AuthorDate: Wed Feb 28 16:32:03 2024 +0100
OAK-10660: DocumentNodeStore: avoid repeated commits of :childOrder in
branch commits (#1317)
Patched Commit#applyToDocumentStore(RevisionVector) to remove redundant
branch commits to the :childorder property.
Added test cases.
Added feature toggle to optionally disable the new behavior.
Co-Authored-by: stefan-egli <[email protected]>
---
.../apache/jackrabbit/oak/jcr/ManyChildrenIT.java | 65 -----------------
.../jackrabbit/oak/jcr/OrderableNodesTest.java | 85 ++++++++++++++++++++++
.../jackrabbit/oak/plugins/document/Commit.java | 39 ++++++++++
.../oak/plugins/document/DocumentNodeStore.java | 8 ++
.../plugins/document/DocumentNodeStoreBuilder.java | 11 +++
.../plugins/document/DocumentNodeStoreService.java | 5 ++
.../oak/fixture/DocumentMemoryFixture.java | 5 +-
.../oak/fixture/DocumentMongoFixture.java | 2 +
.../jackrabbit/oak/fixture/NodeStoreFixture.java | 8 ++
9 files changed, 162 insertions(+), 66 deletions(-)
diff --git
a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/ManyChildrenIT.java
b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/ManyChildrenIT.java
index eeac7c5cc8..a2bb7c0fa6 100644
--- a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/ManyChildrenIT.java
+++ b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/ManyChildrenIT.java
@@ -24,11 +24,8 @@ import javax.jcr.NodeIterator;
import javax.jcr.Session;
import org.apache.jackrabbit.oak.fixture.NodeStoreFixture;
-import org.junit.Ignore;
import org.junit.Test;
-import java.util.UUID;
-
/**
* Test nodes with many child nodes.
*/
@@ -80,66 +77,4 @@ public class ManyChildrenIT extends AbstractRepositoryTest {
writer.save();
assertTrue(test.hasNode("node-x"));
}
-
- @Test
- @Ignore //OAK-10646
- public void orderableAddManyChildrenWithSave() throws Exception {
- int childCount = 1000;
- StringBuilder prefix = new StringBuilder("");
- for (int k = 0; k < 90; k++) {
- prefix.append("0123456789");
- }
- Session session = getAdminSession();
- Node test = session.getRootNode().addNode("test", "nt:unstructured");
- session.save();
- for (int k = 0; k < childCount; k++) {
- test.addNode(prefix.toString() + k, "nt:unstructured");
- }
- }
-
- @Test
- @Ignore //OAK-10646
- public void moveOrderableWithManyChildren() throws Exception {
- int childCount = 1000;
- int moveCount = 1;
- StringBuilder prefix = new StringBuilder("");
- for (int k = 0; k < 90; k++) {
- prefix.append("0123456789");
- }
- Session session = getAdminSession();
- Node test = session.getRootNode().addNode("test-0", "nt:unstructured");
- session.save();
- for (int k = 0; k < childCount; k++) {
- test.addNode(prefix.toString() + k, "nt:unstructured");
- if (k % 100 == 0) {
- session.save();
- }
- }
- session.save();
- session.move("/test-0", "/test-1");
- session.save();
- }
-
- @Test
- @Ignore //OAK-10646
- public void copyOrderableWithManyChildren() throws Exception {
- int childCount = 1000;
- int copyCount = 1;
- StringBuilder prefix = new StringBuilder("");
- for (int k = 0; k < 90; k++) {
- prefix.append("0123456789");
- }
- Session session = getAdminSession();
- Node test = session.getRootNode().addNode("test-0", "nt:unstructured");
- session.save();
- for (int k = 0; k < childCount; k++) {
- test.addNode(prefix.toString() + k, "nt:unstructured");
- if (k % 100 == 0) {
- session.save();
- }
- }
- session.save();
- session.getWorkspace().copy("/test-0", "/test-1");
- session.save();
- }
}
diff --git
a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/OrderableNodesTest.java
b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/OrderableNodesTest.java
index d2bf0674b6..2e9c17ec89 100644
---
a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/OrderableNodesTest.java
+++
b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/OrderableNodesTest.java
@@ -18,6 +18,7 @@ package org.apache.jackrabbit.oak.jcr;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
@@ -30,6 +31,10 @@ import javax.jcr.Session;
import org.apache.jackrabbit.commons.iterator.NodeIterable;
import org.apache.jackrabbit.oak.fixture.NodeStoreFixture;
+import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.toggle.FeatureToggle;
+import org.apache.jackrabbit.oak.spi.whiteboard.Tracker;
import org.junit.Test;
public class OrderableNodesTest extends AbstractRepositoryTest {
@@ -111,6 +116,86 @@ public class OrderableNodesTest extends
AbstractRepositoryTest {
assertEquals("b", it.nextNode().getName());
}
+ @Test
+ public void orderableAddManyChildrenWithSave() throws Exception {
+ int childCount = 1000;
+ StringBuilder prefix = new StringBuilder("");
+ for (int k = 0; k < 90; k++) {
+ prefix.append("0123456789");
+ }
+ Session session = getAdminSession();
+ Node test = session.getRootNode().addNode("test", "nt:unstructured");
+ session.save();
+ for (int k = 0; k < childCount; k++) {
+ test.addNode(prefix.toString() + k, "nt:unstructured");
+ }
+ }
+
+ @Test
+ public void moveOrderableWithManyChildren() throws Exception {
+ int childCount = 1000;
+ StringBuilder prefix = new StringBuilder("");
+ for (int k = 0; k < 90; k++) {
+ prefix.append("0123456789");
+ }
+ Session session = getAdminSession();
+ Node test = session.getRootNode().addNode("test-0", "nt:unstructured");
+ session.save();
+ for (int k = 0; k < childCount; k++) {
+ test.addNode(prefix.toString() + k, "nt:unstructured");
+ if (k % 100 == 0) {
+ session.save();
+ }
+ }
+ session.save();
+ session.move("/test-0", "/test-1");
+ session.save();
+ }
+
+ @Test
+ public void copyOrderableWithManyChildren() throws Exception {
+ int childCount = 1000;
+ StringBuilder prefix = new StringBuilder("");
+ for (int k = 0; k < 90; k++) {
+ prefix.append("0123456789");
+ }
+ Session session = getAdminSession();
+ Node test = session.getRootNode().addNode("test-0", "nt:unstructured");
+ session.save();
+ for (int k = 0; k < childCount; k++) {
+ test.addNode(prefix.toString() + k, "nt:unstructured");
+ if (k % 100 == 0) {
+ session.save();
+ }
+ }
+ session.save();
+ session.getWorkspace().copy("/test-0", "/test-1");
+ session.save();
+ }
+
+ @Test
+ public void childOrderCleanupFeatureToggleTest() throws
RepositoryException {
+ Tracker<FeatureToggle> track =
fixture.getWhiteboard().track(FeatureToggle.class);
+ NodeStore nodeStore = createNodeStore(fixture);
+ assertNotNull(nodeStore);
+ if (nodeStore instanceof DocumentNodeStore) {
+ DocumentNodeStore documentNodeStore = (DocumentNodeStore)
nodeStore;
+ assertTrue(documentNodeStore.isChildOrderCleanupEnabled());
+ for (FeatureToggle toggle : track.getServices()) {
+ if ("FT_NOCOCLEANUP_OAK-10660".equals(toggle.getName())) {
+ assertFalse(toggle.isEnabled());
+ assertTrue(documentNodeStore.isChildOrderCleanupEnabled());
+ toggle.setEnabled(true);
+ assertTrue(toggle.isEnabled());
+
assertFalse(documentNodeStore.isChildOrderCleanupEnabled());
+ toggle.setEnabled(false);
+ assertFalse(toggle.isEnabled());
+ assertTrue(documentNodeStore.isChildOrderCleanupEnabled());
+ }
+ }
+ }
+ }
+
private void doTest(String nodeType) throws RepositoryException {
Session session = getAdminSession();
Node root = session.getRootNode().addNode("test", nodeType);
diff --git
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java
index a22e8adb1d..66d6928d04 100644
---
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java
+++
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/Commit.java
@@ -24,7 +24,9 @@ import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
+import java.util.NavigableSet;
import java.util.Set;
+import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
import org.apache.jackrabbit.guava.common.base.Function;
@@ -32,6 +34,8 @@ import org.apache.jackrabbit.guava.common.collect.Iterables;
import org.apache.jackrabbit.guava.common.collect.Sets;
import org.apache.jackrabbit.oak.commons.json.JsopStream;
import org.apache.jackrabbit.oak.commons.json.JsopWriter;
+import org.apache.jackrabbit.oak.plugins.document.UpdateOp.Key;
+import org.apache.jackrabbit.oak.plugins.document.UpdateOp.Operation;
import org.apache.jackrabbit.oak.plugins.document.util.Utils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -57,6 +61,8 @@ public class Commit {
private static final Logger LOG = LoggerFactory.getLogger(Commit.class);
+ private static final String PROPERTY_NAME_CHILDORDER = ":childOrder";
+
protected final DocumentNodeStore nodeStore;
private final RevisionVector baseRevision;
private final RevisionVector startRevisions;
@@ -354,6 +360,39 @@ public class Commit {
boolean commitRootHasChanges = operations.containsKey(commitRootPath);
for (UpdateOp op : operations.values()) {
NodeDocument.setCommitRoot(op, revision, commitRootDepth);
+
+ // special case for :childOrder updates
+ if (nodeStore.isChildOrderCleanupEnabled()) {
+ final Branch localBranch = getBranch();
+ if (localBranch != null) {
+ final NavigableSet<Revision> commits = new
TreeSet<>(localBranch.getCommits());
+ boolean removePreviousSetOperations = false;
+ for (Map.Entry<Key, Operation> change :
op.getChanges().entrySet()) {
+ if
(PROPERTY_NAME_CHILDORDER.equals(change.getKey().getName()) &&
Operation.Type.SET_MAP_ENTRY == change.getValue().type) {
+ // we are setting child order, so we should remove
previous set operations from the same branch
+ removePreviousSetOperations = true;
+ // branch.getCommits contains all revisions of the
branch
+ // including the new one we're about to make
+ // so don't do a removeMapEntry for that
+
commits.remove(change.getKey().getRevision().asBranchRevision());
+ }
+ }
+ if (removePreviousSetOperations) {
+ if (!commits.isEmpty()) {
+ int countRemoves = 0;
+ for (Revision rev : commits.descendingSet()) {
+ op.removeMapEntry(PROPERTY_NAME_CHILDORDER,
rev.asTrunkRevision());
+ if (++countRemoves >= 256) {
+ LOG.debug("applyToDocumentStore : only
cleaning up last {} branch commits.",
+ countRemoves);
+ break;
+ }
+ }
+ LOG.debug("applyToDocumentStore :
childOrder-edited op is: {}", op);
+ }
+ }
+ }
+ }
changedNodes.add(op);
}
// create a "root of the commit" if there is none
diff --git
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
index 85c210c926..481cccbe82 100644
---
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
+++
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStore.java
@@ -571,6 +571,8 @@ public final class DocumentNodeStore
private final Feature cancelInvalidationFeature;
+ private final Feature noChildOrderCleanupFeature;
+
private Boolean cancelInvalidationLogged;
private CacheWarming cacheWarming;
@@ -648,6 +650,7 @@ public final class DocumentNodeStore
this.prefetchFeature = builder.getPrefetchFeature();
this.cancelInvalidationFeature =
builder.getCancelInvalidationFeature();
+ this.noChildOrderCleanupFeature =
builder.getNoChildOrderCleanupFeature();
this.cacheWarming = new CacheWarming(s);
this.journalPropertyHandlerFactory =
builder.getJournalPropertyHandlerFactory();
@@ -869,6 +872,11 @@ public final class DocumentNodeStore
}
}
+
+ public boolean isChildOrderCleanupEnabled() {
+ return noChildOrderCleanupFeature == null ||
!noChildOrderCleanupFeature.isEnabled();
+ }
+
public void dispose() {
LOG.info("Starting disposal of DocumentNodeStore with clusterNodeId:
{} ({})", clusterId,
getClusterNodeInfoDisplayString());
diff --git
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java
index 070db7ad08..8c05d8b852 100644
---
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java
+++
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreBuilder.java
@@ -127,6 +127,7 @@ public class DocumentNodeStoreBuilder<T extends
DocumentNodeStoreBuilder<T>> {
private boolean isReadOnlyMode = false;
private Feature prefetchFeature;
private Feature docStoreThrottlingFeature;
+ private Feature noChildOrderCleanupFeature;
private Feature cancelInvalidationFeature;
private Weigher<CacheValue, CacheValue> weigher = new EmpiricalWeigher();
private long memoryCacheSize = DEFAULT_MEMORY_CACHE_SIZE;
@@ -318,6 +319,16 @@ public class DocumentNodeStoreBuilder<T extends
DocumentNodeStoreBuilder<T>> {
return docStoreThrottlingFeature;
}
+ public T setNoChildOrderCleanupFeature(@Nullable Feature
noChildOrderCleanupFeature) {
+ this.noChildOrderCleanupFeature = noChildOrderCleanupFeature;
+ return thisBuilder();
+ }
+
+ @Nullable
+ public Feature getNoChildOrderCleanupFeature() {
+ return noChildOrderCleanupFeature;
+ }
+
public T setCancelInvalidationFeature(@Nullable Feature
cancelInvalidation) {
this.cancelInvalidationFeature = cancelInvalidation;
return thisBuilder();
diff --git
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
index e0f1222cd0..556c54e882 100644
---
a/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
+++
b/oak-store-document/src/main/java/org/apache/jackrabbit/oak/plugins/document/DocumentNodeStoreService.java
@@ -181,6 +181,8 @@ public class DocumentNodeStoreService {
*/
private static final String FT_NAME_DOC_STORE_THROTTLING =
"FT_THROTTLING_OAK-9909";
+ private static final String FT_NAME_DOC_STORE_NOCOCLEANUP =
"FT_NOCOCLEANUP_OAK-10660";
+
/**
* Feature toggle name to enable invalidation on cancel (due to a merge
collision)
*/
@@ -221,6 +223,7 @@ public class DocumentNodeStoreService {
private JournalPropertyHandlerFactory journalPropertyHandlerFactory = new
JournalPropertyHandlerFactory();
private Feature prefetchFeature;
private Feature docStoreThrottlingFeature;
+ private Feature noChildOrderCleanupFeature;
private Feature cancelInvalidationFeature;
private ComponentContext context;
private Whiteboard whiteboard;
@@ -256,6 +259,7 @@ public class DocumentNodeStoreService {
documentStoreType =
DocumentStoreType.fromString(this.config.documentStoreType());
prefetchFeature = Feature.newFeature(FT_NAME_PREFETCH, whiteboard);
docStoreThrottlingFeature =
Feature.newFeature(FT_NAME_DOC_STORE_THROTTLING, whiteboard);
+ noChildOrderCleanupFeature =
Feature.newFeature(FT_NAME_DOC_STORE_NOCOCLEANUP, whiteboard);
cancelInvalidationFeature =
Feature.newFeature(FT_NAME_CANCEL_INVALIDATION, whiteboard);
registerNodeStoreIfPossible();
@@ -474,6 +478,7 @@ public class DocumentNodeStoreService {
setLeaseCheckMode(ClusterNodeInfo.DEFAULT_LEASE_CHECK_DISABLED
? LeaseCheckMode.DISABLED : LeaseCheckMode.valueOf(config.leaseCheckMode())).
setPrefetchFeature(prefetchFeature).
setDocStoreThrottlingFeature(docStoreThrottlingFeature).
+ setNoChildOrderCleanupFeature(noChildOrderCleanupFeature).
setCancelInvalidationFeature(cancelInvalidationFeature).
setThrottlingEnabled(config.throttlingEnabled()).
setSuspendTimeoutMillis(config.suspendTimeoutMillis()).
diff --git
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/fixture/DocumentMemoryFixture.java
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/fixture/DocumentMemoryFixture.java
index 411814db1d..1b534b13b9 100644
---
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/fixture/DocumentMemoryFixture.java
+++
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/fixture/DocumentMemoryFixture.java
@@ -22,12 +22,15 @@ package org.apache.jackrabbit.oak.fixture;
import org.apache.jackrabbit.oak.plugins.document.DocumentMK;
import org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.toggle.Feature;
public class DocumentMemoryFixture extends NodeStoreFixture {
@Override
public NodeStore createNodeStore() {
- return new DocumentMK.Builder().getNodeStore();
+ DocumentMK.Builder builder = new DocumentMK.Builder();
+
builder.setNoChildOrderCleanupFeature(Feature.newFeature("FT_NOCOCLEANUP_OAK-10660",
getWhiteboard()));
+ return builder.getNodeStore();
}
@Override
diff --git
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/fixture/DocumentMongoFixture.java
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/fixture/DocumentMongoFixture.java
index 8b4d4305d1..1f66cc4113 100644
---
a/oak-store-document/src/test/java/org/apache/jackrabbit/oak/fixture/DocumentMongoFixture.java
+++
b/oak-store-document/src/test/java/org/apache/jackrabbit/oak/fixture/DocumentMongoFixture.java
@@ -28,6 +28,7 @@ import
org.apache.jackrabbit.oak.plugins.document.DocumentNodeStore;
import org.apache.jackrabbit.oak.plugins.document.MongoUtils;
import org.apache.jackrabbit.oak.spi.blob.BlobStore;
import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.toggle.Feature;
import org.junit.AssumptionViolatedException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -69,6 +70,7 @@ public class DocumentMongoFixture extends NodeStoreFixture {
}
builder.setPersistentCache("target/persistentCache,time");
builder.setMongoDB(createClient(), getDBName(suffix));
+
builder.setNoChildOrderCleanupFeature(Feature.newFeature("FT_NOCOCLEANUP_OAK-10660",
getWhiteboard()));
DocumentNodeStore ns = builder.getNodeStore();
suffixes.put(ns, suffix);
return ns;
diff --git
a/oak-store-spi/src/test/java/org/apache/jackrabbit/oak/fixture/NodeStoreFixture.java
b/oak-store-spi/src/test/java/org/apache/jackrabbit/oak/fixture/NodeStoreFixture.java
index 2ab4831744..edc2784520 100644
---
a/oak-store-spi/src/test/java/org/apache/jackrabbit/oak/fixture/NodeStoreFixture.java
+++
b/oak-store-spi/src/test/java/org/apache/jackrabbit/oak/fixture/NodeStoreFixture.java
@@ -20,12 +20,16 @@
package org.apache.jackrabbit.oak.fixture;
import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.whiteboard.DefaultWhiteboard;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
/**
* NodeStore fixture for parametrized tests.
*/
public abstract class NodeStoreFixture {
+ private final Whiteboard whiteboard = new DefaultWhiteboard();
+
/**
* Creates a new empty {@link NodeStore} instance. An implementation must
* ensure the returned node store is indeed empty and is independent from
@@ -52,4 +56,8 @@ public abstract class NodeStoreFixture {
return true;
}
+ public Whiteboard getWhiteboard() {
+ return whiteboard;
+ }
+
}
\ No newline at end of file