This is an automated email from the ASF dual-hosted git repository.

fortino 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 57d124c6fa OAK-10811: (oak-search-elastic) reduce contention in 
IndexTracker (#1466)
57d124c6fa is described below

commit 57d124c6fa9f4b3683a45ff96b65259ab21d09ec
Author: Fabrizio Fortino <fabrizio.fort...@gmail.com>
AuthorDate: Thu May 23 09:23:26 2024 +0200

    OAK-10811: (oak-search-elastic) reduce contention in IndexTracker (#1466)
    
    * OAK-10811: (oak-search-elastic) reduce contention in IndexTracker
    
    * OAK-10811: ElasticIndexInfoProvider uses node api to get :status info
    
    * OAK-10811: rolled back unneeded change
---
 .../index/elastic/ElasticIndexInfoProvider.java    | 17 +++++--
 .../plugins/index/elastic/ElasticIndexNode.java    |  5 ++
 .../index/elastic/ElasticIndexProviderService.java |  2 +-
 .../plugins/index/elastic/ElasticIndexTracker.java | 37 +++++++++++++-
 .../index/elastic/index/ElasticIndexWriter.java    |  6 +--
 .../index/elastic/ElasticAbstractQueryTest.java    |  3 +-
 .../index/elastic/index/ElasticIndexTest.java      | 58 ++++++++++++++++++++++
 7 files changed, 116 insertions(+), 12 deletions(-)

diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexInfoProvider.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexInfoProvider.java
index 4df7a0bf07..f552962d7c 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexInfoProvider.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexInfoProvider.java
@@ -27,6 +27,8 @@ import 
org.apache.jackrabbit.oak.plugins.index.IndexInfoProvider;
 import org.apache.jackrabbit.oak.plugins.index.IndexUtils;
 import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.apache.jackrabbit.util.ISO8601;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -38,12 +40,16 @@ import 
co.elastic.clients.elasticsearch.cluster.HealthResponse;
 
 class ElasticIndexInfoProvider implements IndexInfoProvider {
 
+    private final NodeStore nodeStore;
+
     private final ElasticIndexTracker indexTracker;
 
     private final AsyncIndexInfoService asyncIndexInfoService;
 
-    ElasticIndexInfoProvider(@NotNull ElasticIndexTracker indexTracker,
-                                    @NotNull AsyncIndexInfoService 
asyncIndexInfoService) {
+    ElasticIndexInfoProvider(@NotNull NodeStore nodeStore,
+                             @NotNull ElasticIndexTracker indexTracker,
+                             @NotNull AsyncIndexInfoService 
asyncIndexInfoService) {
+        this.nodeStore = nodeStore;
         this.indexTracker = indexTracker;
         this.asyncIndexInfoService = asyncIndexInfoService;
     }
@@ -57,18 +63,19 @@ class ElasticIndexInfoProvider implements IndexInfoProvider 
{
     public @Nullable IndexInfo getInfo(String indexPath) {
         ElasticIndexNode node = indexTracker.acquireIndexNode(indexPath);
         try {
-            String asyncName = 
IndexUtils.getAsyncLaneName(node.getDefinition().getDefinitionNodeState(), 
indexPath);
+            NodeState idxState = NodeStateUtils.getNode(nodeStore.getRoot(), 
indexPath);
+            String asyncName = IndexUtils.getAsyncLaneName(idxState, 
indexPath);
             AsyncIndexInfo asyncInfo = 
asyncIndexInfoService.getInfo(asyncName);
 
             return new ElasticIndexInfo(
                     indexPath,
                     asyncName,
                     asyncInfo != null ? asyncInfo.getLastIndexedTo() : -1L,
-                    
getStatusTimestamp(node.getDefinition().getDefinitionNodeState(), 
IndexDefinition.STATUS_LAST_UPDATED),
+                    getStatusTimestamp(idxState, 
IndexDefinition.STATUS_LAST_UPDATED),
                     node.getIndexStatistics().numDocs(),
                     node.getIndexStatistics().primaryStoreSize(),
                     node.getIndexStatistics().creationDate(),
-                    
getStatusTimestamp(node.getDefinition().getDefinitionNodeState(), 
IndexDefinition.REINDEX_COMPLETION_TIMESTAMP)
+                    getStatusTimestamp(idxState, 
IndexDefinition.REINDEX_COMPLETION_TIMESTAMP)
             );
         } finally {
             node.release();
diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexNode.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexNode.java
index c5179348f2..4bb3722876 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexNode.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexNode.java
@@ -40,6 +40,11 @@ public class ElasticIndexNode implements IndexNode {
         // do nothing
     }
 
+    /**
+     * Returns the index definition for this index node. This method ensures 
that the index definition is always
+     * up-to-date with the latest changes in the repository, except for the 
:status node.
+     * For latest changes in the :status node, the node api should be used.
+     */
     @Override
     public ElasticIndexDefinition getDefinition() {
         return indexDefinition;
diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexProviderService.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexProviderService.java
index 58c62c0fae..f2aebf1670 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexProviderService.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexProviderService.java
@@ -181,7 +181,7 @@ public class ElasticIndexProviderService {
 
         // register info provider for oak index stats
         
regs.add(bundleContext.registerService(IndexInfoProvider.class.getName(),
-                new ElasticIndexInfoProvider(indexTracker, 
asyncIndexInfoService), null));
+                new ElasticIndexInfoProvider(nodeStore, indexTracker, 
asyncIndexInfoService), null));
 
         // register mbean for detailed elastic stats and utility actions
         ElasticIndexMBean mBean = new ElasticIndexMBean(indexTracker);
diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexTracker.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexTracker.java
index fe924b2675..079666b676 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexTracker.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexTracker.java
@@ -16,6 +16,7 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.elastic;
 
+import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition;
 import 
org.apache.jackrabbit.oak.plugins.index.search.spi.query.FulltextIndexTracker;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.commit.Observer;
@@ -35,12 +36,12 @@ public class ElasticIndexTracker extends 
FulltextIndexTracker<ElasticIndexNodeMa
 
     @Override
     public boolean isUpdateNeeded(NodeState before, NodeState after) {
-        // for Elastic we are not interested in checking for updates on 
:status, index definition is enough.
+        // for Elastic, we are not interested in checking for updates on 
:status, index definition is enough.
         // The :status gets updated every time the indexed content is changed 
(with properties like last_update_ts),
         // removing the check on :status reduces drastically the contention 
between queries (that need to acquire the
         // read lock) and updates (need to acquire the write lock).
         // Moreover, we don't check diffs in stored index definitions since 
are not created for elastic.
-        return !EqualsDiff.equals(before, after);
+        return !IgnoreStatusDiff.equals(before, after);
     }
 
     @Override
@@ -60,4 +61,36 @@ public class ElasticIndexTracker extends 
FulltextIndexTracker<ElasticIndexNodeMa
     public ElasticMetricHandler getElasticMetricHandler() {
         return elasticMetricHandler;
     }
+
+    static class IgnoreStatusDiff extends EqualsDiff {
+
+        public static boolean equals(NodeState before, NodeState after) {
+            return before.exists() == after.exists()
+                    && after.compareAgainstBaseState(before, new 
IgnoreStatusDiff());
+        }
+
+        /**
+         * The behaviour of this method is not immediately obvious from the 
signature.
+         * For this reason, the javadoc from NodeStateDiff is copied below.
+         * When false is returned, the comparison is aborted because something 
has changed.
+         * If the changed node is the status node, the comparison is continued 
otherwise we call the super method.
+         * The super method will return false if the node is not the status 
node, and it has changed.
+         * <p>
+         * Called for all child nodes that may contain changes between the 
before
+         * and after states. The comparison implementation is expected to make 
an
+         * effort to avoid calling this method on child nodes under which 
nothing
+         * has changed.
+         *
+         * @param name name of the changed child node
+         * @param before child node state before the change
+         * @param after child node state after the change
+         * @return {@code true} to continue the comparison, {@code false} to 
abort.
+         *         Abort will stop comparing completely, that means sibling 
nodes
+         *         and sibling nodes of all parents are not further compared.
+         */
+        @Override
+        public boolean childNodeChanged(String name, NodeState before, 
NodeState after) {
+            return IndexDefinition.STATUS_NODE.equals(name) || 
super.childNodeChanged(name, before, after);
+        }
+    }
 }
diff --git 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexWriter.java
 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexWriter.java
index 3bbd7d3daf..85540b1f47 100644
--- 
a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexWriter.java
+++ 
b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexWriter.java
@@ -150,12 +150,12 @@ class ElasticIndexWriter implements 
FulltextIndexWriter<ElasticDocument> {
     private void saveMetrics() {
         ElasticIndexNode indexNode = 
indexTracker.acquireIndexNode(indexDefinition.getIndexPath());
         if (indexNode != null) {
-            ElasticIndexStatistics stats = indexNode.getIndexStatistics();
             try {
-                
indexTracker.getElasticMetricHandler().markDocuments(indexName, 
indexNode.getIndexStatistics().numDocs());
+                ElasticIndexStatistics stats = indexNode.getIndexStatistics();
+                
indexTracker.getElasticMetricHandler().markDocuments(indexName, 
stats.numDocs());
                 indexTracker.getElasticMetricHandler().markSize(indexName, 
stats.primaryStoreSize(), stats.storeSize());
             } catch (Exception e) {
-                LOG.warn("Unable to store metrics for {}", 
indexNode.getDefinition().getIndexPath(), e);
+                LOG.warn("Unable to store metrics for {}", 
indexDefinition.getIndexPath(), e);
             } finally {
                 indexNode.release();
             }
diff --git 
a/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticAbstractQueryTest.java
 
b/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticAbstractQueryTest.java
index 976ae264dd..9ffb7aa292 100644
--- 
a/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticAbstractQueryTest.java
+++ 
b/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticAbstractQueryTest.java
@@ -70,6 +70,7 @@ public abstract class ElasticAbstractQueryTest extends 
AbstractQueryTest {
     private static final String elasticConnectionString = 
System.getProperty("elasticConnectionString");
     protected ElasticConnection esConnection;
 
+    protected ElasticIndexTracker indexTracker;
     // This is instantiated during repo creation but not hooked up to the 
async indexing lane
     // This can be used by the extending classes to trigger the async index 
update as per need (not having to wait for async indexing cycle)
     protected AsyncIndexUpdate asyncIndexUpdate;
@@ -139,7 +140,7 @@ public abstract class ElasticAbstractQueryTest extends 
AbstractQueryTest {
     @Override
     protected ContentRepository createRepository() {
         esConnection = getElasticConnection();
-        ElasticIndexTracker indexTracker = new 
ElasticIndexTracker(esConnection, getMetricHandler());
+        indexTracker = new ElasticIndexTracker(esConnection, 
getMetricHandler());
         ElasticIndexEditorProvider editorProvider = new 
ElasticIndexEditorProvider(indexTracker, esConnection,
                 new ExtractedTextCache(10 * FileUtils.ONE_MB, 100));
         ElasticIndexProvider indexProvider = new 
ElasticIndexProvider(indexTracker);
diff --git 
a/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexTest.java
 
b/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexTest.java
index 92309e7dd6..db137bdbfc 100644
--- 
a/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexTest.java
+++ 
b/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexTest.java
@@ -18,12 +18,17 @@ package 
org.apache.jackrabbit.oak.plugins.index.elastic.index;
 
 import org.apache.jackrabbit.oak.api.Tree;
 import 
org.apache.jackrabbit.oak.plugins.index.elastic.ElasticAbstractQueryTest;
+import org.apache.jackrabbit.oak.plugins.index.elastic.ElasticIndexDefinition;
+import org.apache.jackrabbit.oak.plugins.index.elastic.ElasticIndexNode;
+import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
 import 
org.apache.jackrabbit.oak.plugins.index.search.util.IndexDefinitionBuilder;
 import org.junit.Test;
 
 import java.util.UUID;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
 
 public class ElasticIndexTest extends ElasticAbstractQueryTest {
 
@@ -37,4 +42,57 @@ public class ElasticIndexTest extends 
ElasticAbstractQueryTest {
         assertEventually(() -> assertEquals(ElasticIndexHelper.MAPPING_VERSION,
                 getElasticIndexDefinition(index).getMappingVersion()));
     }
+
+    @Test
+    public void indexTrackerHandlesIndexDefinitionChanges() throws Exception {
+        IndexDefinitionBuilder builder = createIndex("a");
+        builder.indexRule("nt:base").property("a").propertyIndex();
+        Tree index = setIndex(UUID.randomUUID().toString(), builder);
+        root.commit();
+
+        ElasticIndexDefinition elasticIndexDefinitionT0;
+        ElasticIndexNode indexNodeT0 = 
indexTracker.acquireIndexNode(index.getPath());
+        try {
+            elasticIndexDefinitionT0 = indexNodeT0.getDefinition();
+            assertNotNull(elasticIndexDefinitionT0);
+        } finally {
+            indexNodeT0.release();
+        }
+
+        Tree test = root.getTree("/").addChild("test");
+        test.addChild("t").setProperty("a", "foo");
+        root.commit();
+
+        ElasticIndexDefinition elasticIndexDefinitionT1;
+        ElasticIndexNode indexNodeT1 = 
indexTracker.acquireIndexNode(index.getPath());
+        try {
+            elasticIndexDefinitionT1 = indexNodeT1.getDefinition();
+            assertNotNull(elasticIndexDefinitionT1);
+        } finally {
+            indexNodeT1.release();
+        }
+
+        // no changes in the index node and definition when the index content 
gets updated
+        assertEquals(indexNodeT0, indexNodeT1);
+        assertEquals(elasticIndexDefinitionT0, elasticIndexDefinitionT1);
+
+        Tree b = 
index.getChild("indexRules").getChild("nt:base").getChild("properties").addChild("b");
+        b.setProperty(FulltextIndexConstants.PROP_PROPERTY_INDEX, true);
+        b.setProperty(FulltextIndexConstants.PROP_ANALYZED, true);
+        root.commit();
+
+        ElasticIndexDefinition elasticIndexDefinitionT2;
+        ElasticIndexNode indexNodeT2 = 
indexTracker.acquireIndexNode(index.getPath());
+        try {
+            elasticIndexDefinitionT2 = indexNodeT2.getDefinition();
+            assertNotNull(elasticIndexDefinitionT2);
+        } finally {
+            indexNodeT2.release();
+        }
+
+        // index node and definition are different after the index definition 
change
+        assertNotEquals(indexNodeT1, indexNodeT2);
+        assertNotEquals(elasticIndexDefinitionT1, elasticIndexDefinitionT2);
+        assertNotNull(elasticIndexDefinitionT2.getPropertiesByName().get("b"));
+    }
 }

Reply via email to