You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by fo...@apache.org on 2020/08/19 07:27:37 UTC

svn commit: r1880981 - in /jackrabbit/oak/trunk: oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/ oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ oak-search-elastic/src/test/java/org/apache/jackrabbit/oa...

Author: fortino
Date: Wed Aug 19 07:27:36 2020
New Revision: 1880981

URL: http://svn.apache.org/viewvc?rev=1880981&view=rev
Log:
OAK-9169 - Remove remote elastic indexes when index definition is removed (patch by Amrit Verma)

Added:
    jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexCleaner.java
    jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexCleanerTest.java
Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java
    jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticConnection.java
    jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexNameHelper.java
    jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexProviderService.java
    jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticAbstractQueryTest.java
    jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticConnectionRule.java
    jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticFunctionIndexCommonTest.java
    jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDescendantSpellcheckCommonTest.java
    jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDescendantSuggestionCommonTest.java
    jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexQueryCommonTest.java
    jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexCommonTest.java
    jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticStrictPathRestrictionEnableCommonTest.java
    jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticStrictPathRestrictionWarnCommonTest.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java?rev=1880981&r1=1880980&r2=1880981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/AsyncIndexUpdate.java Wed Aug 19 07:27:36 2020
@@ -225,7 +225,7 @@ public class AsyncIndexUpdate implements
     }
 
     public AsyncIndexUpdate(@NotNull String name, @NotNull NodeStore store,
-            @NotNull IndexEditorProvider provider) {
+                            @NotNull IndexEditorProvider provider) {
         this(name, store, provider, false);
     }
 
@@ -286,8 +286,8 @@ public class AsyncIndexUpdate implements
         private boolean hasLease = false;
 
         public AsyncUpdateCallback(NodeStore store, String name,
-                long leaseTimeOut, String checkpoint,
-                AsyncIndexStats indexStats, AtomicBoolean forcedStop) {
+                                   long leaseTimeOut, String checkpoint,
+                                   AsyncIndexStats indexStats, AtomicBoolean forcedStop) {
             this.store = store;
             this.name = name;
             this.forcedStop = forcedStop;
@@ -345,7 +345,7 @@ public class AsyncIndexUpdate implements
         }
 
         private void updateTempCheckpoints(NodeBuilder async,
-                String checkpoint, String afterCheckpoint) {
+                                           String checkpoint, String afterCheckpoint) {
 
             indexStats.setReferenceCheckpoint(checkpoint);
             indexStats.setProcessedCheckpoint(afterCheckpoint);
@@ -599,7 +599,7 @@ public class AsyncIndexUpdate implements
             // and skip release if this cp was used in a split operation
             if (checkpointToRelease != null
                     && !checkpointToRelease.equals(taskSplitter
-                            .getLastReferencedCp())) {
+                    .getLastReferencedCp())) {
                 if (!store.release(checkpointToRelease)) {
                     log.debug("[{}] Unable to release checkpoint {}", name,
                             checkpointToRelease);
@@ -710,8 +710,8 @@ public class AsyncIndexUpdate implements
     }
 
     protected boolean updateIndex(NodeState before, String beforeCheckpoint,
-            NodeState after, String afterCheckpoint, String afterTime,
-            AsyncUpdateCallback callback) throws CommitFailedException {
+                                  NodeState after, String afterCheckpoint, String afterTime,
+                                  AsyncUpdateCallback callback) throws CommitFailedException {
         Stopwatch watch = Stopwatch.createStarted();
         boolean updatePostRunStatus = true;
         boolean progressLogged = false;
@@ -734,7 +734,7 @@ public class AsyncIndexUpdate implements
                     ImmutableMap.of(IndexConstants.CHECKPOINT_CREATION_TIME, afterTime));
             indexUpdate =
                     new IndexUpdate(provider, name, after, builder, callback, callback, info, corruptIndexHandler)
-                    .withMissingProviderStrategy(missingStrategy);
+                            .withMissingProviderStrategy(missingStrategy);
             configureRateEstimator(indexUpdate);
             CommitFailedException exception =
                     EditorDiff.process(VisibleEditor.wrap(indexUpdate), before, after);
@@ -784,7 +784,7 @@ public class AsyncIndexUpdate implements
 
             if (indexUpdate.isReindexingPerformed()) {
                 log.info("[{}] Reindexing completed for indexes: {} in {} ({} ms)",
-                        name, indexUpdate.getReindexStats(), 
+                        name, indexUpdate.getReindexStats(),
                         watch, watch.elapsed(TimeUnit.MILLISECONDS));
                 progressLogged = true;
             }
@@ -852,8 +852,8 @@ public class AsyncIndexUpdate implements
                 // check for concurrent updates by this async task
                 NodeState async = before.getChildNode(ASYNC);
                 if ((checkpoint == null || Objects.equal(checkpoint, async.getString(name)))
-                    &&
-                    (lease == null      || lease == async.getLong(leasify(name)))) {
+                        &&
+                        (lease == null      || lease == async.getLong(leasify(name)))) {
                     return after;
                 } else {
                     throw newConcurrentUpdateException();
@@ -1236,9 +1236,9 @@ public class AsyncIndexUpdate implements
                 lastIndexedTime = statsProvider.getCounterStats(stats("LAST_INDEXED_TIME"), StatsOptions.DEFAULT);
                 try {
                     consolidatedType = new CompositeType("ConsolidatedStats",
-                        "Consolidated stats", names,
-                        names,
-                        new OpenType[] {SimpleType.LONG, SimpleType.LONG});
+                            "Consolidated stats", names,
+                            names,
+                            new OpenType[] {SimpleType.LONG, SimpleType.LONG});
                 } catch (OpenDataException e) {
                     log.warn("[{}] Error in creating CompositeType for consolidated stats", AsyncIndexUpdate.this.name, e);
                 }
@@ -1298,7 +1298,7 @@ public class AsyncIndexUpdate implements
         }
 
         private void splitIndexingTask(Set<String> paths,
-                String newIndexTaskName) {
+                                       String newIndexTaskName) {
             taskSplitter.registerSplit(paths, newIndexTaskName);
         }
 

Modified: jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticConnection.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticConnection.java?rev=1880981&r1=1880980&r2=1880981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticConnection.java (original)
+++ jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticConnection.java Wed Aug 19 07:27:36 2020
@@ -239,7 +239,7 @@ public class ElasticConnection implement
             @Override
             public ElasticConnection build() {
                 return new ElasticConnection(
-                        Objects.requireNonNull(indexPrefix, "indexPrefix must be not null"),
+                        Objects.requireNonNull(ElasticIndexNameHelper.getElasticSafeName(indexPrefix), "indexPrefix must be not null"),
                         Objects.requireNonNull(scheme, "scheme must be not null"),
                         Objects.requireNonNull(host, "host must be not null"),
                         Objects.requireNonNull(port, "port must be not null"),

Added: jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexCleaner.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexCleaner.java?rev=1880981&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexCleaner.java (added)
+++ jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexCleaner.java Wed Aug 19 07:27:36 2020
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.index.elastic;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
+import org.elasticsearch.action.support.IndicesOptions;
+import org.elasticsearch.action.support.master.AcknowledgedResponse;
+import org.elasticsearch.client.RequestOptions;
+import org.elasticsearch.client.indices.GetIndexRequest;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+
+/**
+ * Deletes those remote elastic indexes for which no index definitions exist in repository. The remote indexes are not deleted
+ * the first time they are discovered. A dangling remote index is deleted in subsequent runs of this cleaner only after a
+ * given threshold of time has passed since the dangling index was first discovered.
+ */
+public class ElasticIndexCleaner implements Runnable {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ElasticIndexCleaner.class);
+
+    private final ElasticConnection elasticConnection;
+    private final NodeStore nodeStore;
+    private final String indexPrefix;
+    private final Map<String, Long> danglingRemoteIndicesMap;
+    private final int threshold;
+
+    /**
+     * Constructs a new instance of index cleaner with the given parameters.
+     * @param elasticConnection elastic connection to use
+     * @param nodeStore node store where index definitions exist
+     * @param thresholdInSeconds time in seconds before which a dangling remote index won't be deleted.
+     */
+    public ElasticIndexCleaner(ElasticConnection elasticConnection, NodeStore nodeStore, int thresholdInSeconds) {
+        this.elasticConnection = elasticConnection;
+        this.nodeStore = nodeStore;
+        this.indexPrefix = elasticConnection.getIndexPrefix();
+        danglingRemoteIndicesMap = new HashMap<>();
+        this.threshold = thresholdInSeconds;
+    }
+
+    public void run() {
+        try {
+            NodeState root = nodeStore.getRoot();
+            GetIndexRequest getIndexRequest = new GetIndexRequest(elasticConnection.getIndexPrefix() + "*")
+                    .indicesOptions(IndicesOptions.lenientExpandOpen());
+            String[] remoteIndices = elasticConnection.getClient().indices()
+                    .get(getIndexRequest, RequestOptions.DEFAULT).getIndices();
+            if (remoteIndices == null || remoteIndices.length == 0) {
+                LOG.debug("No remote index found with prefix {}", indexPrefix);
+                return;
+            }
+            // remove entry of remote index names which don't exist now
+            List<String> externallyDeletedIndices = danglingRemoteIndicesMap.keySet().stream().
+                    filter(index -> Arrays.stream(remoteIndices).noneMatch(remoteIndex -> remoteIndex.equals(index))).collect(Collectors.toList());
+            externallyDeletedIndices.forEach(danglingRemoteIndicesMap::remove);
+            Set<String> existingIndices = new HashSet<>();
+            root.getChildNode(INDEX_DEFINITIONS_NAME).getChildNodeEntries().forEach(childNodeEntry -> {
+                PropertyState typeProperty = childNodeEntry.getNodeState().getProperty(IndexConstants.TYPE_PROPERTY_NAME);
+                if (typeProperty != null && typeProperty.getValue(Type.STRING).equals(ElasticIndexDefinition.TYPE_ELASTICSEARCH)) {
+                    String indexPath = "/" + INDEX_DEFINITIONS_NAME + "/" + childNodeEntry.getName();
+                    String remoteIndexName = ElasticIndexNameHelper.getRemoteIndexName(indexPrefix, childNodeEntry.getNodeState(), indexPath);
+                    if (remoteIndexName != null) {
+                        existingIndices.add(remoteIndexName);
+                    }
+                }
+            });
+
+            List<String> indicesToDelete = new ArrayList<>();
+            for (String remoteIndexName : remoteIndices) {
+                if (!existingIndices.contains(remoteIndexName)) {
+                    Long curTime = System.currentTimeMillis();
+                    Long oldTime = danglingRemoteIndicesMap.putIfAbsent(remoteIndexName, curTime);
+                    if (threshold == 0 || (oldTime != null && curTime - oldTime >= TimeUnit.SECONDS.toMillis(threshold))) {
+                        indicesToDelete.add(remoteIndexName);
+                        danglingRemoteIndicesMap.remove(remoteIndexName);
+                    }
+                } else {
+                    danglingRemoteIndicesMap.remove(remoteIndexName);
+                }
+            }
+            DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(indicesToDelete.toArray(new String[]{}));
+            if (deleteIndexRequest.indices() != null && deleteIndexRequest.indices().length > 0) {
+                String indexString = Arrays.toString(deleteIndexRequest.indices());
+                AcknowledgedResponse acknowledgedResponse = elasticConnection.getClient().indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
+                LOG.info("Deleting remote indices {}", indexString);
+                if (!acknowledgedResponse.isAcknowledged()) {
+                    LOG.error("Could not delete remote indices " + indexString);
+                }
+            }
+        } catch (IOException e) {
+            LOG.error("Could not delete remote indices", e);
+        }
+    }
+}

Modified: jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexNameHelper.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexNameHelper.java?rev=1880981&r1=1880980&r2=1880981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexNameHelper.java (original)
+++ jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexNameHelper.java Wed Aug 19 07:27:36 2020
@@ -16,8 +16,13 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.elastic;
 
-
+import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.jetbrains.annotations.Nullable;
 
 import java.util.UUID;
 import java.util.regex.Pattern;
@@ -40,6 +45,24 @@ public class ElasticIndexNameHelper {
         return getElasticSafeIndexName(indexPrefix + "." + indexPath);
     }
 
+    public static @Nullable String getRemoteIndexName(String indexPrefix, NodeState indexNode, String indexPath) {
+        PropertyState nodeTypeProp = indexNode.getProperty(JcrConstants.JCR_PRIMARYTYPE);
+        if (nodeTypeProp == null || !IndexConstants.INDEX_DEFINITIONS_NODE_TYPE.equals(nodeTypeProp.getValue(Type.STRING))) {
+            throw new IllegalArgumentException("Not an index definition node state");
+        }
+        PropertyState type = indexNode.getProperty(IndexConstants.TYPE_PROPERTY_NAME);
+        if (type == null || !ElasticIndexDefinition.TYPE_ELASTICSEARCH.equals(type.getValue(Type.STRING))) {
+            throw new IllegalArgumentException("Not an elastic index node");
+        }
+        PropertyState seedProp = indexNode.getProperty(ElasticIndexDefinition.PROP_INDEX_NAME_SEED);
+        if (seedProp == null) {
+            return null;
+        }
+        long seed = seedProp.getValue(Type.LONG);
+        String indexAlias = getIndexAlias(indexPrefix, indexPath);
+        return getRemoteIndexName(indexAlias, seed);
+    }
+
     /**
      * Create a name for remote elastic index from given index definition and seed.
      * @param indexDefinition elastic index definition to use
@@ -87,7 +110,11 @@ public class ElasticIndexNameHelper {
      * Convert {@code e} to Elasticsearch safe index name.
      * Ref: https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html
      */
-    private static String getElasticSafeName(String suggestedIndexName) {
+    static String getElasticSafeName(String suggestedIndexName) {
         return suggestedIndexName.replaceAll(INVALID_CHARS_REGEX, "").toLowerCase();
     }
+
+    private static String getRemoteIndexName(String indexAlias, long seed) {
+        return getElasticSafeIndexName(indexAlias + "-" + Long.toHexString(seed));
+    }
 }

Modified: jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexProviderService.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexProviderService.java?rev=1880981&r1=1880980&r2=1880981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexProviderService.java (original)
+++ jackrabbit/oak/trunk/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexProviderService.java Wed Aug 19 07:27:36 2020
@@ -17,18 +17,9 @@
 package org.apache.jackrabbit.oak.plugins.index.elastic;
 
 import org.apache.commons.io.FilenameUtils;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.ReferencePolicy;
-import org.apache.felix.scr.annotations.ReferencePolicyOption;
 import org.apache.jackrabbit.oak.api.jmx.CacheStatsMBean;
 import org.apache.jackrabbit.oak.cache.CacheStats;
 import org.apache.jackrabbit.oak.commons.IOUtils;
-import org.apache.jackrabbit.oak.commons.PropertiesUtil;
 import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard;
 import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider;
 import org.apache.jackrabbit.oak.plugins.index.elastic.index.ElasticIndexEditorProvider;
@@ -37,11 +28,22 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.index.search.ExtractedTextCache;
 import org.apache.jackrabbit.oak.spi.commit.Observer;
 import org.apache.jackrabbit.oak.spi.query.QueryIndexProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
 import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
 import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
 import org.apache.jackrabbit.oak.stats.StatisticsProvider;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -50,93 +52,82 @@ import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.List;
-import java.util.Map;
 
 import static org.apache.commons.io.FileUtils.ONE_MB;
 import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
+import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.scheduleWithFixedDelay;
 
-@Component(metatype = true, label = "Apache Jackrabbit Oak ElasticIndexProvider")
+@Component
+@Designate(ocd = ElasticIndexProviderService.Config.class)
 public class ElasticIndexProviderService {
 
-    private static final Logger LOG = LoggerFactory.getLogger(ElasticIndexProviderService.class);
-
-    private static final String REPOSITORY_HOME = "repository.home";
-
-    private static final int PROP_EXTRACTED_TEXT_CACHE_SIZE_DEFAULT = 20;
-    @Property(
-            intValue = PROP_EXTRACTED_TEXT_CACHE_SIZE_DEFAULT,
-            label = "Extracted text cache size (MB)",
-            description = "Cache size in MB for caching extracted text for some time. When set to 0 then " +
-                    "cache would be disabled"
-    )
-    private static final String PROP_EXTRACTED_TEXT_CACHE_SIZE = "extractedTextCacheSizeInMB";
-
-    private static final int PROP_EXTRACTED_TEXT_CACHE_EXPIRY_DEFAULT = 300;
-    @Property(
-            intValue = PROP_EXTRACTED_TEXT_CACHE_EXPIRY_DEFAULT,
-            label = "Extracted text cache expiry (secs)",
-            description = "Time in seconds for which the extracted text would be cached in memory"
-    )
-    private static final String PROP_EXTRACTED_TEXT_CACHE_EXPIRY = "extractedTextCacheExpiryInSecs";
-
-    private static final boolean PROP_PRE_EXTRACTED_TEXT_ALWAYS_USE_DEFAULT = false;
-    @Property(
-            boolValue = PROP_PRE_EXTRACTED_TEXT_ALWAYS_USE_DEFAULT,
-            label = "Always use pre-extracted text cache",
-            description = "By default pre extracted text cache would only be used for reindex case. If this setting " +
-                    "is enabled then it would also be used in normal incremental indexing"
-    )
-    private static final String PROP_PRE_EXTRACTED_TEXT_ALWAYS_USE = "alwaysUsePreExtractedCache";
-
-    private static final String PROP_INDEX_PREFIX_DEFAULT = "";
-    @Property(
-            value = PROP_INDEX_PREFIX_DEFAULT,
-            label = "Index prefix",
-            description = "Prefix to be added to name of each elastic search index"
-    )
     static final String PROP_INDEX_PREFIX = "indexPrefix";
-
-    @Property(
-            value = ElasticConnection.DEFAULT_SCHEME,
-            label = "Elasticsearch connection scheme"
-    )
     private static final String PROP_ELASTIC_SCHEME = ElasticConnection.SCHEME_PROP;
-
-    @Property(
-            value = ElasticConnection.DEFAULT_HOST,
-            label = "Elasticsearch connection host"
-    )
     private static final String PROP_ELASTIC_HOST = ElasticConnection.HOST_PROP;
-
-    @Property(
-            value = "" + ElasticConnection.DEFAULT_PORT,
-            label = "Elasticsearch connection port"
-    )
     private static final String PROP_ELASTIC_PORT = ElasticConnection.PORT_PROP;
-
-    @Property(
-            label = "Elasticsearch API key ID",
-            value = ElasticConnection.DEFAULT_API_KEY_ID
-    )
     private static final String PROP_ELASTIC_API_KEY_ID = ElasticConnection.API_KEY_ID_PROP;
-
-    @Property(
-            label = "Elasticsearch API key secret",
-            passwordValue = ElasticConnection.DEFAULT_API_KEY_SECRET
-    )
     private static final String PROP_ELASTIC_API_KEY_SECRET = ElasticConnection.API_KEY_SECRET_PROP;
-
-    @Property(
-            label = "Local text extraction cache path",
-            description = "Local file system path where text extraction cache stores/load entries to recover from timed out operation"
-    )
     private static final String PROP_LOCAL_TEXT_EXTRACTION_DIR = "localTextExtractionDir";
 
+    @ObjectClassDefinition(name = "ElasticIndexProviderService", description = "Apache Jackrabbit Oak ElasticIndexProvider")
+    public @interface Config {
+        @AttributeDefinition(name = "Extracted text cache size (MB)",
+                description = "Cache size in MB for caching extracted text for some time. When set to 0 then " +
+                        "cache would be disabled")
+        int extractedTextCacheSizeInMB() default 20 ;
+
+        @AttributeDefinition(name = "Extracted text cache expiry (secs)",
+                description = "Time in seconds for which the extracted text would be cached in memory")
+        int extractedTextCacheExpiryInSecs() default 300;
+
+        @AttributeDefinition(name = "Always use pre-extracted text cache",
+                description = "By default pre extracted text cache would only be used for reindex case. If this setting " +
+                        "is enabled then it would also be used in normal incremental indexing")
+        boolean alwaysUsePreExtractedCache() default false;
+
+        @AttributeDefinition(name = "Index prefix",
+                description = "Prefix to be added to name of each elastic search index")
+        String indexPrefix() default "oak-elastic";
+
+        @AttributeDefinition(name = "Elasticsearch connection scheme", description = "Elasticsearch connection scheme")
+        String elasticsearch_scheme() default ElasticConnection.DEFAULT_SCHEME;
+
+        @AttributeDefinition(name = "Elasticsearch connection host", description = "Elasticsearch connection host")
+        String elasticsearch_host() default ElasticConnection.DEFAULT_HOST;
+
+        @AttributeDefinition(name = "Elasticsearch connection port", description = "Elasticsearch connection port")
+        String elasticsearch_port() default ("" + ElasticConnection.DEFAULT_PORT);
+
+        @AttributeDefinition(name = "Elasticsearch API key ID", description = "Elasticsearch API key ID")
+        String elasticsearch_apiKeyId() default ElasticConnection.DEFAULT_API_KEY_ID;
+
+        @AttributeDefinition(name = "Elasticsearch API key secret", description = "Elasticsearch API key secret")
+        String elasticsearch_apiKeySecret() default ElasticConnection.DEFAULT_API_KEY_SECRET;
+
+        @AttributeDefinition(name = "Local text extraction cache path",
+                description = "Local file system path where text extraction cache stores/load entries to recover from timed out operation")
+        String localTextExtractionDir();
+
+        @AttributeDefinition(name = "Remote index cleanup frequency", description = "Frequency (in seconds) of running remote index deletion scheduled task")
+        int remoteIndexCleanupFrequency() default 60;
+
+        @AttributeDefinition(name = "Remote index deletion threshold", description = "Time in seconds after which a remote index whose local index is not found gets deleted")
+        int remoteIndexDeletionThreshold() default 300;
+    }
+
+
+    private static final Logger LOG = LoggerFactory.getLogger(ElasticIndexProviderService.class);
+
+    private static final String REPOSITORY_HOME = "repository.home";
+
     @Reference
     private StatisticsProvider statisticsProvider;
 
+    @Reference
+    private NodeStore nodeStore;
+
     @Reference(policy = ReferencePolicy.DYNAMIC,
-            cardinality = ReferenceCardinality.OPTIONAL_UNARY,
+            cardinality = ReferenceCardinality.OPTIONAL,
             policyOption = ReferencePolicyOption.GREEDY
     )
     private volatile PreExtractedTextProvider extractedTextProvider;
@@ -150,9 +141,11 @@ public class ElasticIndexProviderService
     private File textExtractionDir;
 
     private ElasticConnection elasticConnection;
+    private ElasticIndexProvider indexProvider;
+    private String indexPrefix;
 
     @Activate
-    private void activate(BundleContext bundleContext, Map<String, Object> config) {
+    private void activate(BundleContext bundleContext, Config config) {
         whiteboard = new OsgiWhiteboard(bundleContext);
 
         //initializeTextExtractionDir(bundleContext, config);
@@ -164,6 +157,7 @@ public class ElasticIndexProviderService
 
         registerIndexProvider(bundleContext);
         registerIndexEditor(bundleContext);
+        registerIndexCleaner(config);
     }
 
     @Deactivate
@@ -183,8 +177,13 @@ public class ElasticIndexProviderService
         }
     }
 
+    private void registerIndexCleaner(Config contextConfig) {
+        ElasticIndexCleaner task = new ElasticIndexCleaner(elasticConnection, nodeStore, contextConfig.remoteIndexDeletionThreshold());
+        oakRegs.add(scheduleWithFixedDelay(whiteboard, task, contextConfig.remoteIndexCleanupFrequency()));
+    }
+
     private void registerIndexProvider(BundleContext bundleContext) {
-        ElasticIndexProvider indexProvider = new ElasticIndexProvider(elasticConnection, new ElasticMetricHandler(statisticsProvider));
+        indexProvider = new ElasticIndexProvider(elasticConnection, new ElasticMetricHandler(statisticsProvider));
 
         // register observer needed for index tracking
         regs.add(bundleContext.registerService(Observer.class.getName(), indexProvider, null));
@@ -207,18 +206,12 @@ public class ElasticIndexProviderService
 //                "TextExtraction statistics"));
     }
 
-    private void initializeExtractedTextCache(Map<String, ?> config, StatisticsProvider statisticsProvider) {
-        int cacheSizeInMB = PropertiesUtil.toInteger(config.get(PROP_EXTRACTED_TEXT_CACHE_SIZE),
-                PROP_EXTRACTED_TEXT_CACHE_SIZE_DEFAULT);
-        int cacheExpiryInSecs = PropertiesUtil.toInteger(config.get(PROP_EXTRACTED_TEXT_CACHE_EXPIRY),
-                PROP_EXTRACTED_TEXT_CACHE_EXPIRY_DEFAULT);
-        boolean alwaysUsePreExtractedCache = PropertiesUtil.toBoolean(config.get(PROP_PRE_EXTRACTED_TEXT_ALWAYS_USE),
-                PROP_PRE_EXTRACTED_TEXT_ALWAYS_USE_DEFAULT);
+    private void initializeExtractedTextCache(final Config config, StatisticsProvider statisticsProvider) {
 
         extractedTextCache = new ExtractedTextCache(
-                cacheSizeInMB * ONE_MB,
-                cacheExpiryInSecs,
-                alwaysUsePreExtractedCache,
+                config.extractedTextCacheSizeInMB() * ONE_MB,
+                config.extractedTextCacheExpiryInSecs(),
+                config.alwaysUsePreExtractedCache(),
                 textExtractionDir,
                 statisticsProvider);
         if (extractedTextProvider != null) {
@@ -230,13 +223,13 @@ public class ElasticIndexProviderService
                     CacheStatsMBean.class, stats,
                     CacheStatsMBean.TYPE, stats.getName()));
             LOG.info("Extracted text caching enabled with maxSize {} MB, expiry time {} secs",
-                    cacheSizeInMB, cacheExpiryInSecs);
+                    config.extractedTextCacheSizeInMB(), config.extractedTextCacheExpiryInSecs());
         }
     }
 
-    private void initializeTextExtractionDir(BundleContext bundleContext, Map<String, ?> config) {
-        String textExtractionDir = PropertiesUtil.toString(config.get(PROP_LOCAL_TEXT_EXTRACTION_DIR), null);
-        if (textExtractionDir == null || textExtractionDir.trim().isEmpty()) {
+    private void initializeTextExtractionDir(BundleContext bundleContext, Config config) {
+        String textExtractionDir = config.localTextExtractionDir();
+        if (textExtractionDir.trim().isEmpty()) {
             String repoHome = bundleContext.getProperty(REPOSITORY_HOME);
             if (repoHome != null) {
                 textExtractionDir = FilenameUtils.concat(repoHome, "index");
@@ -265,23 +258,17 @@ public class ElasticIndexProviderService
         }
     }
 
-    private ElasticConnection getElasticConnection(Map<String, Object> contextConfig) {
+    private ElasticConnection getElasticConnection(Config contextConfig) {
         // system properties have priority, get mandatory params first
-        final String indexPrefix = System.getProperty(PROP_INDEX_PREFIX,
-                (String) contextConfig.getOrDefault(PROP_INDEX_PREFIX, "oak-elastic"));
-        final String scheme = System.getProperty(PROP_ELASTIC_SCHEME,
-                (String) contextConfig.getOrDefault(PROP_ELASTIC_SCHEME, ElasticConnection.DEFAULT_SCHEME));
-        final String host = System.getProperty(PROP_ELASTIC_HOST,
-                (String) contextConfig.getOrDefault(PROP_ELASTIC_HOST, ElasticConnection.DEFAULT_HOST));
-        final String portString = (String) contextConfig.getOrDefault(PROP_ELASTIC_PORT,
-                "" + ElasticConnection.DEFAULT_PORT);
+        indexPrefix = System.getProperty(PROP_INDEX_PREFIX, contextConfig.indexPrefix());
+        final String scheme = System.getProperty(PROP_ELASTIC_SCHEME, contextConfig.elasticsearch_scheme());
+        final String host = System.getProperty(PROP_ELASTIC_HOST, contextConfig.elasticsearch_host());
+        final String portString = System.getProperty(PROP_ELASTIC_PORT, contextConfig.elasticsearch_port());
         final int port = Integer.getInteger(PROP_ELASTIC_PORT, Integer.parseInt(portString));
 
         // optional params
-        final String apiKeyId = System.getProperty(PROP_ELASTIC_API_KEY_ID,
-                (String) contextConfig.get(PROP_ELASTIC_API_KEY_ID));
-        final String apiSecretId = System.getProperty(PROP_ELASTIC_API_KEY_SECRET,
-                (String) contextConfig.get(PROP_ELASTIC_API_KEY_SECRET));
+        final String apiKeyId = System.getProperty(PROP_ELASTIC_API_KEY_ID, contextConfig.elasticsearch_apiKeyId());
+        final String apiSecretId = System.getProperty(PROP_ELASTIC_API_KEY_SECRET, contextConfig.elasticsearch_apiKeySecret());
 
         return ElasticConnection.newBuilder()
                 .withIndexPrefix(indexPrefix)

Modified: jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticAbstractQueryTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticAbstractQueryTest.java?rev=1880981&r1=1880980&r2=1880981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticAbstractQueryTest.java (original)
+++ jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticAbstractQueryTest.java Wed Aug 19 07:27:36 2020
@@ -67,7 +67,7 @@ public abstract class ElasticAbstractQue
     // needs authentication
     // Do not set this if docker is running and you want to run the tests on docker instead.
     private static final String elasticConnectionString = System.getProperty("elasticConnectionString");
-    private ElasticConnection esConnection;
+    protected ElasticConnection esConnection;
 
     // 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)

Modified: jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticConnectionRule.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticConnectionRule.java?rev=1880981&r1=1880980&r2=1880981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticConnectionRule.java (original)
+++ jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticConnectionRule.java Wed Aug 19 07:27:36 2020
@@ -18,6 +18,8 @@ package org.apache.jackrabbit.oak.plugin
 
 import com.github.dockerjava.api.DockerClient;
 import org.elasticsearch.Version;
+import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
+import org.elasticsearch.client.RequestOptions;
 import org.junit.rules.ExternalResource;
 import org.junit.runner.Description;
 import org.junit.runners.model.Statement;
@@ -123,6 +125,7 @@ public class ElasticConnectionRule exten
 
     public void closeElasticConnection() throws IOException {
         if (elasticConnection != null) {
+            elasticConnection.getClient().indices().delete(new DeleteIndexRequest(elasticConnection.getIndexPrefix() + "*"), RequestOptions.DEFAULT);
             elasticConnection.close();
             // Make this object null otherwise tests after the first test would
             // receive an client that is closed.

Modified: jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticFunctionIndexCommonTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticFunctionIndexCommonTest.java?rev=1880981&r1=1880980&r2=1880981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticFunctionIndexCommonTest.java (original)
+++ jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticFunctionIndexCommonTest.java Wed Aug 19 07:27:36 2020
@@ -23,9 +23,11 @@ import org.apache.jackrabbit.oak.api.Typ
 import org.apache.jackrabbit.oak.plugins.index.FunctionIndexCommonTest;
 import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
 import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.junit.After;
 import org.junit.ClassRule;
 import org.junit.Ignore;
 
+import java.io.IOException;
 import java.util.Set;
 
 import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
@@ -44,6 +46,14 @@ public class ElasticFunctionIndexCommonT
     @ClassRule
     public static ElasticConnectionRule elasticRule = new ElasticConnectionRule(elasticConnectionString);
 
+    /*
+    Close the ES connection after every test method execution
+     */
+    @After
+    public void cleanup() throws IOException {
+        elasticRule.closeElasticConnection();
+    }
+
     public ElasticFunctionIndexCommonTest() {
         indexOptions = new ElasticIndexOptions();
     }

Added: jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexCleanerTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexCleanerTest.java?rev=1880981&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexCleanerTest.java (added)
+++ jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexCleanerTest.java Wed Aug 19 07:27:36 2020
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.oak.plugins.index.elastic;
+
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.plugins.index.search.util.IndexDefinitionBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.elasticsearch.client.RequestOptions;
+import org.elasticsearch.client.indices.GetIndexRequest;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.UUID;
+
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class ElasticIndexCleanerTest extends ElasticAbstractQueryTest {
+
+    @Override
+    protected boolean useAsyncIndexing() {
+        return true;
+    }
+
+    private String createIndexAndContentNode(String indexProperty, String contentNodeName) throws Exception {
+        IndexDefinitionBuilder builder = createIndex(indexProperty);
+        builder.async("async");
+        builder.indexRule("nt:base").property(indexProperty);
+
+        String indexId1 = UUID.randomUUID().toString();
+        setIndex(indexId1, builder);
+        root.commit();
+        addContent(indexProperty, contentNodeName);
+        String indexPath = "/" + INDEX_DEFINITIONS_NAME + "/" + indexId1;
+        assertEventually(() -> {
+            NodeState indexState = nodeStore.getRoot().getChildNode(INDEX_DEFINITIONS_NAME).getChildNode(indexId1);
+            String remoteIndexName = ElasticIndexNameHelper.getRemoteIndexName(esConnection.getIndexPrefix(), indexState,
+                    indexPath);
+            try {
+                assertTrue(esConnection.getClient().indices().exists(new GetIndexRequest(remoteIndexName), RequestOptions.DEFAULT));
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        });
+        return indexId1;
+    }
+
+    private void addContent(String indexProperty, String nodeName) throws Exception {
+        Tree test = root.getTree("/").addChild(nodeName);
+        test.addChild("a").setProperty(indexProperty, "Hello World!");
+        test.addChild("b").setProperty(indexProperty, "Simple test");
+        root.commit();
+    }
+
+    @Test
+    public void testIndexDeletion() throws Exception {
+        String indexId1 = createIndexAndContentNode("propa", "test1");
+        String indexId2 = createIndexAndContentNode("propb", "test2");
+        String indexId3 = createIndexAndContentNode("propc", "test3");
+        String indexPath1 = "/" + INDEX_DEFINITIONS_NAME + "/" + indexId1;
+        String indexPath2 = "/" + INDEX_DEFINITIONS_NAME + "/" + indexId2;
+        String indexPath3 = "/" + INDEX_DEFINITIONS_NAME + "/" + indexId3;
+        NodeState oakIndex = nodeStore.getRoot().getChildNode(INDEX_DEFINITIONS_NAME);
+        NodeState indexState1 = oakIndex.getChildNode(indexId1);
+        NodeState indexState2 = oakIndex.getChildNode(indexId2);
+        NodeState indexState3 = oakIndex.getChildNode(indexId3);
+
+        root.refresh();
+        root.getTree(indexPath1).remove();
+        root.getTree(indexPath2).remove();
+        root.commit();
+
+        oakIndex = nodeStore.getRoot().getChildNode(INDEX_DEFINITIONS_NAME);
+        assertFalse(oakIndex.getChildNode(indexId1).exists());
+        assertFalse(oakIndex.getChildNode(indexId2).exists());
+
+        ElasticIndexCleaner cleaner = new ElasticIndexCleaner(esConnection, nodeStore, 5);
+        cleaner.run();
+
+        String remoteIndexName1 = ElasticIndexNameHelper.getRemoteIndexName(esConnection.getIndexPrefix(), indexState1,
+                indexPath1);
+        String remoteIndexName2 = ElasticIndexNameHelper.getRemoteIndexName(esConnection.getIndexPrefix(), indexState2,
+                indexPath2);
+        String remoteIndexName3 = ElasticIndexNameHelper.getRemoteIndexName(esConnection.getIndexPrefix(), indexState3,
+                indexPath3);
+
+        assertTrue(esConnection.getClient().indices().exists(new GetIndexRequest(remoteIndexName1), RequestOptions.DEFAULT));
+        assertTrue(esConnection.getClient().indices().exists(new GetIndexRequest(remoteIndexName2), RequestOptions.DEFAULT));
+        assertTrue(esConnection.getClient().indices().exists(new GetIndexRequest(remoteIndexName3), RequestOptions.DEFAULT));
+
+        Thread.sleep(5000);
+        cleaner.run();
+
+        assertFalse(esConnection.getClient().indices().exists(new GetIndexRequest(remoteIndexName1), RequestOptions.DEFAULT));
+        assertFalse(esConnection.getClient().indices().exists(new GetIndexRequest(remoteIndexName2), RequestOptions.DEFAULT));
+        assertTrue(esConnection.getClient().indices().exists(new GetIndexRequest(remoteIndexName3), RequestOptions.DEFAULT));
+    }
+
+}

Modified: jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDescendantSpellcheckCommonTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDescendantSpellcheckCommonTest.java?rev=1880981&r1=1880980&r2=1880981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDescendantSpellcheckCommonTest.java (original)
+++ jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDescendantSpellcheckCommonTest.java Wed Aug 19 07:27:36 2020
@@ -19,9 +19,11 @@ package org.apache.jackrabbit.oak.plugin
 import org.apache.jackrabbit.oak.Oak;
 import org.apache.jackrabbit.oak.jcr.Jcr;
 import org.apache.jackrabbit.oak.plugins.index.IndexDescendantSpellcheckCommonTest;
+import org.junit.After;
 import org.junit.ClassRule;
 
 import javax.jcr.Repository;
+import java.io.IOException;
 
 public class ElasticIndexDescendantSpellcheckCommonTest extends IndexDescendantSpellcheckCommonTest {
 
@@ -42,4 +44,12 @@ public class ElasticIndexDescendantSpell
         Jcr jcr = new Jcr(oak);
         return jcr.createRepository();
     }
+
+    /**
+     * Close the ES connection after every test method execution
+     */
+    @After
+    public void cleanup() throws IOException {
+        elasticRule.closeElasticConnection();
+    }
 }

Modified: jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDescendantSuggestionCommonTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDescendantSuggestionCommonTest.java?rev=1880981&r1=1880980&r2=1880981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDescendantSuggestionCommonTest.java (original)
+++ jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDescendantSuggestionCommonTest.java Wed Aug 19 07:27:36 2020
@@ -19,9 +19,11 @@ package org.apache.jackrabbit.oak.plugin
 import org.apache.jackrabbit.oak.Oak;
 import org.apache.jackrabbit.oak.jcr.Jcr;
 import org.apache.jackrabbit.oak.plugins.index.IndexDescendantSuggestionCommonTest;
+import org.junit.After;
 import org.junit.ClassRule;
 
 import javax.jcr.Repository;
+import java.io.IOException;
 
 public class ElasticIndexDescendantSuggestionCommonTest extends IndexDescendantSuggestionCommonTest {
 
@@ -42,4 +44,12 @@ public class ElasticIndexDescendantSugge
         Jcr jcr = new Jcr(oak);
         return jcr.createRepository();
     }
+
+    /**
+     * Close the ES connection after every test method execution
+     */
+    @After
+    public void cleanup() throws IOException {
+        elasticRule.closeElasticConnection();
+    }
 }

Modified: jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexQueryCommonTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexQueryCommonTest.java?rev=1880981&r1=1880980&r2=1880981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexQueryCommonTest.java (original)
+++ jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexQueryCommonTest.java Wed Aug 19 07:27:36 2020
@@ -20,8 +20,11 @@ import org.apache.jackrabbit.oak.Initial
 import org.apache.jackrabbit.oak.api.ContentRepository;
 import org.apache.jackrabbit.oak.plugins.index.IndexQueryCommonTest;
 import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.junit.After;
 import org.junit.ClassRule;
 
+import java.io.IOException;
+
 public class ElasticIndexQueryCommonTest extends IndexQueryCommonTest {
 
     // Set this connection string as
@@ -44,4 +47,12 @@ public class ElasticIndexQueryCommonTest
         repositoryOptionsUtil = elasticTestRepositoryBuilder.build();
         return repositoryOptionsUtil.getOak().createContentRepository();
     }
+
+    /**
+     * Close the ES connection after every test method execution
+     */
+    @After
+    public void cleanup() throws IOException {
+        elasticRule.closeElasticConnection();
+    }
 }

Modified: jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexCommonTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexCommonTest.java?rev=1880981&r1=1880980&r2=1880981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexCommonTest.java (original)
+++ jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexCommonTest.java Wed Aug 19 07:27:36 2020
@@ -18,8 +18,11 @@ package org.apache.jackrabbit.oak.plugin
 
 import org.apache.jackrabbit.oak.api.ContentRepository;
 import org.apache.jackrabbit.oak.plugins.index.PropertyIndexCommonTest;
+import org.junit.After;
 import org.junit.ClassRule;
 
+import java.io.IOException;
+
 public class ElasticPropertyIndexCommonTest extends PropertyIndexCommonTest {
 
     // Set this connection string as
@@ -45,4 +48,12 @@ public class ElasticPropertyIndexCommonT
     protected void createTestIndexNode() {
         setTraversalEnabled(false);
     }
+
+    /*
+    Close the ES connection after every test method execution
+     */
+    @After
+    public void cleanup() throws IOException {
+        elasticRule.closeElasticConnection();
+    }
 }

Modified: jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticStrictPathRestrictionEnableCommonTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticStrictPathRestrictionEnableCommonTest.java?rev=1880981&r1=1880980&r2=1880981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticStrictPathRestrictionEnableCommonTest.java (original)
+++ jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticStrictPathRestrictionEnableCommonTest.java Wed Aug 19 07:27:36 2020
@@ -20,8 +20,11 @@ import org.apache.jackrabbit.oak.api.Con
 import org.apache.jackrabbit.oak.api.StrictPathRestriction;
 import org.apache.jackrabbit.oak.plugins.index.StrictPathRestrictionEnableCommonTest;
 import org.apache.jackrabbit.oak.query.QueryEngineSettings;
+import org.junit.After;
 import org.junit.ClassRule;
 
+import java.io.IOException;
+
 public class ElasticStrictPathRestrictionEnableCommonTest extends StrictPathRestrictionEnableCommonTest {
 
     // Set this connection string as
@@ -43,4 +46,12 @@ public class ElasticStrictPathRestrictio
         repositoryOptionsUtil = elasticTestRepositoryBuilder.build();
         return repositoryOptionsUtil.getOak().createContentRepository();
     }
+
+    /**
+     * Close the ES connection after every test method execution
+     */
+    @After
+    public void cleanup() throws IOException {
+        elasticRule.closeElasticConnection();
+    }
 }

Modified: jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticStrictPathRestrictionWarnCommonTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticStrictPathRestrictionWarnCommonTest.java?rev=1880981&r1=1880980&r2=1880981&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticStrictPathRestrictionWarnCommonTest.java (original)
+++ jackrabbit/oak/trunk/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticStrictPathRestrictionWarnCommonTest.java Wed Aug 19 07:27:36 2020
@@ -20,8 +20,11 @@ import org.apache.jackrabbit.oak.api.Con
 import org.apache.jackrabbit.oak.api.StrictPathRestriction;
 import org.apache.jackrabbit.oak.plugins.index.StrictPathRestrictionWarnCommonTest;
 import org.apache.jackrabbit.oak.query.QueryEngineSettings;
+import org.junit.After;
 import org.junit.ClassRule;
 
+import java.io.IOException;
+
 public class ElasticStrictPathRestrictionWarnCommonTest extends StrictPathRestrictionWarnCommonTest {
 
     // Set this connection string as
@@ -43,4 +46,12 @@ public class ElasticStrictPathRestrictio
         repositoryOptionsUtil = elasticTestRepositoryBuilder.build();
         return repositoryOptionsUtil.getOak().createContentRepository();
     }
+
+    /**
+     * Close the ES connection after every test method execution
+     */
+    @After
+    public void cleanup() throws IOException {
+        elasticRule.closeElasticConnection();
+    }
 }