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 2023/04/04 15:20:58 UTC

[jackrabbit-oak] branch trunk updated: OAK-10167: Elastic bulk processor should fail when intermediate bulks fail (#881)

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 7b428de7bd OAK-10167: Elastic bulk processor should fail when intermediate bulks fail (#881)
7b428de7bd is described below

commit 7b428de7bdab8f7776d7a968bf6b1270d09fd81d
Author: Fabrizio Fortino <fa...@gmail.com>
AuthorDate: Tue Apr 4 17:20:50 2023 +0200

    OAK-10167: Elastic bulk processor should fail when intermediate bulks fail (#881)
    
    * OAK-10167: bulk processor fails when at least an intermediate bulk fails
    
    * OAK-10167: consolidate usage of java 9/10 immutable data structures
    
    * Update oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDefinition.java
    
    Co-authored-by: Nuno Santos <ns...@adobe.com>
    
    * OAK-10167: improved exception handling in bulk processor
    
    ---------
    
    Co-authored-by: Nuno Santos <ns...@adobe.com>
---
 .../index/elastic/ElasticIndexDefinition.java      |  14 +++
 .../elastic/index/ElasticBulkProcessorHandler.java |  29 +++--
 .../index/elastic/index/ElasticIndexHelper.java    |   7 ++
 .../index/elastic/index/ElasticIndexWriter.java    |   4 +-
 .../index/elastic/ElasticPropertyIndexTest.java    | 127 +++++++++++++++++----
 .../elastic/index/ElasticIndexWriterTest.java      |   8 +-
 6 files changed, 153 insertions(+), 36 deletions(-)

diff --git a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDefinition.java b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDefinition.java
index 9444757f36..114a655026 100644
--- a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDefinition.java
+++ b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticIndexDefinition.java
@@ -68,6 +68,14 @@ public class ElasticIndexDefinition extends IndexDefinition {
     public static final String TRACK_TOTAL_HITS = "trackTotalHits";
     public static final Integer TRACK_TOTAL_HITS_DEFAULT = 10000;
 
+    public static final String DYNAMIC_MAPPING = "dynamicMapping";
+    // possible values are: true, false, runtime, strict. See https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic.html
+    public static final String DYNAMIC_MAPPING_DEFAULT = "true";
+
+    // when true, fails indexing in case of bulk failures
+    public static final String FAIL_ON_ERROR = "failOnError";
+    public static final boolean FAIL_ON_ERROR_DEFAULT = true;
+
     /**
      * Hidden property for storing a seed value to be used as suffix in remote index name.
      */
@@ -117,6 +125,8 @@ public class ElasticIndexDefinition extends IndexDefinition {
     public final int numberOfReplicas;
     public final int[] queryFetchSizes;
     public final Integer trackTotalHits;
+    public final String dynamicMapping;
+    public final boolean failOnError;
 
     private final Map<String, List<PropertyDefinition>> propertiesByName;
     private final List<PropertyDefinition> dynamicBoostProperties;
@@ -138,6 +148,10 @@ public class ElasticIndexDefinition extends IndexDefinition {
         this.queryFetchSizes = Arrays.stream(getOptionalValues(defn, QUERY_FETCH_SIZES, Type.LONGS, Long.class, QUERY_FETCH_SIZES_DEFAULT))
                 .mapToInt(Long::intValue).toArray();
         this.trackTotalHits = getOptionalValue(defn, TRACK_TOTAL_HITS, TRACK_TOTAL_HITS_DEFAULT);
+        this.dynamicMapping = getOptionalValue(defn, DYNAMIC_MAPPING, DYNAMIC_MAPPING_DEFAULT);
+        this.failOnError = getOptionalValue(defn, FAIL_ON_ERROR,
+                Boolean.parseBoolean(System.getProperty(TYPE_ELASTICSEARCH + "." + FAIL_ON_ERROR, Boolean.toString(FAIL_ON_ERROR_DEFAULT)))
+        );
 
         this.propertiesByName = getDefinedRules()
                 .stream()
diff --git a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticBulkProcessorHandler.java b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticBulkProcessorHandler.java
index fdf23df902..98d8249b4b 100644
--- a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticBulkProcessorHandler.java
+++ b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticBulkProcessorHandler.java
@@ -18,10 +18,8 @@ package org.apache.jackrabbit.oak.plugins.index.elastic.index;
 
 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.plugins.index.elastic.ElasticConnection;
 import org.apache.jackrabbit.oak.plugins.index.elastic.ElasticIndexDefinition;
-import org.apache.jackrabbit.oak.plugins.index.importer.AsyncLaneSwitcher;
 import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition;
 import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -45,6 +43,7 @@ import java.io.IOException;
 import java.util.LinkedHashSet;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Phaser;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
@@ -81,9 +80,9 @@ class ElasticBulkProcessorHandler {
     private final Phaser phaser = new Phaser(1); // register main controller
 
     /**
-     * IOException object wrapping any error/exception which occurred while trying to update index in elasticsearch.
+     * Exceptions occurred while trying to update index in elasticsearch
      */
-    private volatile IOException ioException;
+    private final ConcurrentLinkedQueue<Throwable> suppressedExceptions = new ConcurrentLinkedQueue<>();
 
     /**
      * Key-value structure to keep the history of bulk requests. Keys are the bulk execution ids, the boolean
@@ -156,12 +155,23 @@ class ElasticBulkProcessorHandler {
                 .build();
     }
 
+    private void checkFailures() throws IOException {
+        if (suppressedExceptions.size() > 0) {
+            IOException ioe = new IOException("Exception while indexing. See suppressed for details");
+            suppressedExceptions.forEach(ioe::addSuppressed);
+            throw ioe;
+        }
+    }
+
     protected BiConsumer<BulkRequest, ActionListener<BulkResponse>> requestConsumer() {
         // TODO: migrate to ES Java client https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/indexing-bulk.html
         return (request, bulkListener) -> elasticConnection.getOldClient().bulkAsync(request, RequestOptions.DEFAULT, bulkListener);
     }
 
-    public void add(DocWriteRequest<?> request) {
+    public void add(DocWriteRequest<?> request) throws IOException {
+        // fail fast: we don't want to wait until the processor gets closed to fail
+        checkFailures();
+
         bulkProcessor.add(request);
         totalOperations++;
     }
@@ -187,9 +197,7 @@ class ElasticBulkProcessorHandler {
             }
         }
 
-        if (ioException != null) {
-            throw ioException;
-        }
+        checkFailures();
 
         if (LOG.isTraceEnabled()) {
             LOG.trace("Bulk identifier -> update status = {}", updatesMap);
@@ -246,6 +254,9 @@ class ElasticBulkProcessorHandler {
                 for (BulkItemResponse bulkItemResponse : bulkResponse) {
                     if (bulkItemResponse.isFailed()) {
                         BulkItemResponse.Failure failure = bulkItemResponse.getFailure();
+                        if (indexDefinition.failOnError && failure.getCause() != null) {
+                            suppressedExceptions.add(failure.getCause());
+                        }
                         if (!isFailedDocSetFull && failedDocSet.size() < FAILED_DOC_COUNT_FOR_STATUS_NODE) {
                             failedDocSet.add(bulkItemResponse.getId());
                         } else {
@@ -276,7 +287,7 @@ class ElasticBulkProcessorHandler {
         @Override
         public void afterBulk(long executionId, BulkRequest bulkRequest, Throwable throwable) {
             LOG.error("ElasticIndex Update Bulk Failure : Bulk with id {} threw an error", executionId, throwable);
-            ElasticBulkProcessorHandler.this.ioException = new IOException(throwable);
+            suppressedExceptions.add(throwable);
             phaser.arriveAndDeregister();
         }
     }
diff --git a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexHelper.java b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexHelper.java
index 735ffb8507..e8beb54aaf 100644
--- a/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexHelper.java
+++ b/oak-search-elastic/src/main/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexHelper.java
@@ -17,6 +17,7 @@
 package org.apache.jackrabbit.oak.plugins.index.elastic.index;
 
 import co.elastic.clients.elasticsearch._types.Time;
+import co.elastic.clients.elasticsearch._types.mapping.DynamicMapping;
 import co.elastic.clients.elasticsearch._types.mapping.Property;
 import co.elastic.clients.elasticsearch._types.mapping.TypeMapping;
 import co.elastic.clients.elasticsearch.indices.CreateIndexRequest;
@@ -35,6 +36,7 @@ import org.jetbrains.annotations.NotNull;
 
 import java.io.Reader;
 import java.io.StringReader;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -71,6 +73,11 @@ class ElasticIndexHelper {
 
     private static ObjectBuilder<TypeMapping> loadMappings(@NotNull TypeMapping.Builder builder,
                                                            @NotNull ElasticIndexDefinition indexDefinition) {
+        builder.dynamic(Arrays
+                .stream(DynamicMapping.values())
+                .filter(dm -> dm.jsonValue().equals(indexDefinition.dynamicMapping))
+                .findFirst().orElse(DynamicMapping.True)
+        );
         mapInternalProperties(builder);
         mapIndexRules(builder, indexDefinition);
         return builder;
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 110eae1d66..9cdf8d2b59 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
@@ -131,7 +131,7 @@ class ElasticIndexWriter implements FulltextIndexWriter<ElasticDocument> {
     }
 
     @Override
-    public void updateDocument(String path, ElasticDocument doc) {
+    public void updateDocument(String path, ElasticDocument doc) throws IOException {
         IndexRequest request = new IndexRequest(indexName)
                 .id(ElasticIndexUtils.idFromPath(path))
                 .source(doc.build(), XContentType.JSON);
@@ -139,7 +139,7 @@ class ElasticIndexWriter implements FulltextIndexWriter<ElasticDocument> {
     }
 
     @Override
-    public void deleteDocuments(String path) {
+    public void deleteDocuments(String path) throws IOException {
         DeleteRequest request = new DeleteRequest(indexName).id(ElasticIndexUtils.idFromPath(path));
         bulkProcessorHandler.add(request);
     }
diff --git a/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexTest.java b/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexTest.java
index db066e1909..4f5120b92c 100644
--- a/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexTest.java
+++ b/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/ElasticPropertyIndexTest.java
@@ -16,16 +16,15 @@
  */
 package org.apache.jackrabbit.oak.plugins.index.elastic;
 
-import com.google.common.collect.ImmutableList;
 import org.apache.jackrabbit.oak.api.CommitFailedException;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.plugins.index.elastic.util.ElasticIndexDefinitionBuilder;
 import org.apache.jackrabbit.oak.plugins.index.search.util.IndexDefinitionBuilder;
 import org.junit.Test;
 
-import java.util.Arrays;
+import java.io.IOException;
+import java.util.List;
 
-import static java.util.Collections.singletonList;
 import static org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants.PROPDEF_PROP_NODE_NAME;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -48,8 +47,7 @@ public class ElasticPropertyIndexTest extends ElasticAbstractQueryTest {
         String propaQuery = "select [jcr:path] from [nt:base] where [propa] = 'foo248'";
         assertEventually(() -> {
             assertThat(explain(propaQuery), containsString("elasticsearch:test1"));
-
-            assertQuery(propaQuery, singletonList("/test/a248"));
+            assertQuery(propaQuery, List.of("/test/a248"));
         });
 
         // Now we test for 250 < nodes < 500
@@ -61,8 +59,7 @@ public class ElasticPropertyIndexTest extends ElasticAbstractQueryTest {
         String propaQuery2 = "select [jcr:path] from [nt:base] where [propa] = 'foo299'";
         assertEventually(() -> {
             assertThat(explain(propaQuery2), containsString("elasticsearch:test1"));
-
-            assertQuery(propaQuery2, singletonList("/test/a299"));
+            assertQuery(propaQuery2, List.of("/test/a299"));
         });
     }
 
@@ -85,9 +82,9 @@ public class ElasticPropertyIndexTest extends ElasticAbstractQueryTest {
             assertThat(explain(propaQuery), containsString("elasticsearch:test1"));
             assertThat(explain("select [jcr:path] from [nt:base] where [propc] = 'foo'"), containsString("elasticsearch:test2"));
 
-            assertQuery(propaQuery, Arrays.asList("/test/a", "/test/b"));
-            assertQuery("select [jcr:path] from [nt:base] where [propa] = 'foo2'", singletonList("/test/c"));
-            assertQuery("select [jcr:path] from [nt:base] where [propc] = 'foo'", singletonList("/test/d"));
+            assertQuery(propaQuery, List.of("/test/a", "/test/b"));
+            assertQuery("select [jcr:path] from [nt:base] where [propa] = 'foo2'", List.of("/test/c"));
+            assertQuery("select [jcr:path] from [nt:base] where [propc] = 'foo'", List.of("/test/d"));
         });
     }
 
@@ -117,15 +114,15 @@ public class ElasticPropertyIndexTest extends ElasticAbstractQueryTest {
             String explanation = explain(propabQuery);
             assertThat(explanation, containsString("elasticsearch:test1(/oak:index/test1) "));
             assertThat(explanation, containsString("{\"term\":{\":nodeName\":{\"value\":\"foo\""));
-            assertQuery(propabQuery, singletonList("/test/foo"));
+            assertQuery(propabQuery, List.of("/test/foo"));
 
-            assertQuery(queryPrefix + "LOCALNAME() = 'bar'", singletonList("/test/sc/bar"));
-            assertQuery(queryPrefix + "LOCALNAME() LIKE 'foo'", singletonList("/test/foo"));
-            assertQuery(queryPrefix + "LOCALNAME() LIKE 'camel%'", singletonList("/test/camelCase"));
+            assertQuery(queryPrefix + "LOCALNAME() = 'bar'", List.of("/test/sc/bar"));
+            assertQuery(queryPrefix + "LOCALNAME() LIKE 'foo'", List.of("/test/foo"));
+            assertQuery(queryPrefix + "LOCALNAME() LIKE 'camel%'", List.of("/test/camelCase"));
 
-            assertQuery(queryPrefix + "NAME() = 'bar'", singletonList("/test/sc/bar"));
-            assertQuery(queryPrefix + "NAME() LIKE 'foo'", singletonList("/test/foo"));
-            assertQuery(queryPrefix + "NAME() LIKE 'camel%'", singletonList("/test/camelCase"));
+            assertQuery(queryPrefix + "NAME() = 'bar'", List.of("/test/sc/bar"));
+            assertQuery(queryPrefix + "NAME() LIKE 'foo'", List.of("/test/foo"));
+            assertQuery(queryPrefix + "NAME() LIKE 'camel%'", List.of("/test/camelCase"));
         });
     }
 
@@ -143,7 +140,6 @@ public class ElasticPropertyIndexTest extends ElasticAbstractQueryTest {
                 containsString("elasticsearch:test1")));
     }
 
-
     @Test
     public void inOperandStringValues() throws Exception {
         String query = "select [jcr:path] from [nt:base] where [propa] in(\"a\", \"e\", \"i\")";
@@ -156,7 +152,7 @@ public class ElasticPropertyIndexTest extends ElasticAbstractQueryTest {
         root.commit();
         assertEventually(() -> {
             assertThat(explain(query), containsString("{\"terms\":{\"propa\":[\"a\",\"e\",\"i\"]}}"));
-            assertQuery(query, SQL2, ImmutableList.of("/test/node-a", "/test/node-e", "/test/node-i"));
+            assertQuery(query, SQL2, List.of("/test/node-a", "/test/node-e", "/test/node-i"));
         });
     }
 
@@ -173,7 +169,7 @@ public class ElasticPropertyIndexTest extends ElasticAbstractQueryTest {
 
         assertEventually(() -> {
             assertThat(explain(query), containsString("{\"terms\":{\"propa\":[2,3,5,7]}}"));
-            assertQuery(query, SQL2, ImmutableList.of("/test/node-2", "/test/node-3", "/test/node-5", "/test/node-7"));
+            assertQuery(query, SQL2, List.of("/test/node-2", "/test/node-3", "/test/node-5", "/test/node-7"));
         });
     }
 
@@ -191,10 +187,99 @@ public class ElasticPropertyIndexTest extends ElasticAbstractQueryTest {
 
         assertEventually(() -> {
             assertThat(explain(query), containsString("{\"terms\":{\"propa\":[2.0,3.0,5.0,7.0]}}"));
-            assertQuery(query, SQL2, ImmutableList.of("/test/node-2", "/test/node-3", "/test/node-5", "/test/node-7"));
+            assertQuery(query, SQL2, List.of("/test/node-2", "/test/node-3", "/test/node-5", "/test/node-7"));
         });
     }
 
+    @Test
+    public void indexFailuresWithFailOnErrorOn() throws Exception {
+        IndexDefinitionBuilder builder = createIndex("a");
+        builder.includedPaths("/test")
+                .indexRule("nt:base")
+                .property("nodeName", PROPDEF_PROP_NODE_NAME);
+
+        // configuring the index with a regex property and strict mapping to simulate failures
+        builder.indexRule("nt:base").property("b", true).propertyIndex();
+        builder.getBuilderTree().setProperty(ElasticIndexDefinition.DYNAMIC_MAPPING, "strict");
+
+        setIndex("test1", builder);
+        root.commit();
+
+        Tree test = root.getTree("/").addChild("test");
+        for (int i = 1; i < 3; i++) {
+            test.addChild("a" + i).setProperty("a", "foo");
+        }
+        root.commit();
+
+        // now we add 5 correct docs and 5 docs cannot be mapped
+        test.addChild("a100").setProperty("a", "foo");
+        test.addChild("a200").setProperty("b", "foo");
+        test.addChild("a101").setProperty("a", "foo");
+        test.addChild("a201").setProperty("b", "foo");
+        test.addChild("a102").setProperty("a", "foo");
+        test.addChild("a202").setProperty("b", "foo");
+        test.addChild("a103").setProperty("a", "foo");
+        test.addChild("a203").setProperty("b", "foo");
+        test.addChild("a104").setProperty("a", "foo");
+        test.addChild("a204").setProperty("b", "foo");
+
+        CommitFailedException cfe = null;
+        try {
+            root.commit();
+        } catch (CommitFailedException e) {
+            cfe = e;
+        }
+
+        assertThat("no exception thrown", cfe != null);
+        assertThat("the exception cause has to be an IOException", cfe.getCause() instanceof IOException);
+        assertThat("there should be 5 suppressed exception", cfe.getCause().getSuppressed().length == 5);
+
+        String query = "select [jcr:path] from [nt:base] where [a] = 'foo'";
+        assertEventually(() -> assertQuery(query, SQL2,
+                List.of("/test/a1", "/test/a2", "/test/a100", "/test/a101", "/test/a102", "/test/a103", "/test/a104")
+        ));
+    }
+
+    @Test
+    public void indexFailuresWithFailOnErrorOff() throws Exception {
+        IndexDefinitionBuilder builder = createIndex("a");
+        builder.includedPaths("/test")
+                .indexRule("nt:base")
+                .property("nodeName", PROPDEF_PROP_NODE_NAME);
+
+        // configuring the index with a regex property and strict mapping to simulate failures
+        builder.indexRule("nt:base").property("b", true).propertyIndex();
+        builder.getBuilderTree().setProperty(ElasticIndexDefinition.DYNAMIC_MAPPING, "strict");
+        builder.getBuilderTree().setProperty(ElasticIndexDefinition.FAIL_ON_ERROR, false);
+
+        setIndex("test1", builder);
+        root.commit();
+
+        Tree test = root.getTree("/").addChild("test");
+        for (int i = 1; i < 3; i++) {
+            test.addChild("a" + i).setProperty("a", "foo");
+        }
+        root.commit();
+
+        // now we add 5 correct docs and 5 docs cannot be mapped
+        test.addChild("a100").setProperty("a", "foo");
+        test.addChild("a200").setProperty("b", "foo");
+        test.addChild("a101").setProperty("a", "foo");
+        test.addChild("a201").setProperty("b", "foo");
+        test.addChild("a102").setProperty("a", "foo");
+        test.addChild("a202").setProperty("b", "foo");
+        test.addChild("a103").setProperty("a", "foo");
+        test.addChild("a203").setProperty("b", "foo");
+        test.addChild("a104").setProperty("a", "foo");
+        test.addChild("a204").setProperty("b", "foo");
+        root.commit();
+
+        String query = "select [jcr:path] from [nt:base] where [a] = 'foo'";
+        assertEventually(() -> assertQuery(query, SQL2,
+                List.of("/test/a1", "/test/a2", "/test/a100", "/test/a101", "/test/a102", "/test/a103", "/test/a104")
+        ));
+    }
+
     private void createIndexOfType(String type) throws CommitFailedException {
         IndexDefinitionBuilder builder = new ElasticIndexDefinitionBuilder();
         builder.noAsync();
diff --git a/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexWriterTest.java b/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexWriterTest.java
index d4be2e892c..a51eda1994 100644
--- a/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexWriterTest.java
+++ b/oak-search-elastic/src/test/java/org/apache/jackrabbit/oak/plugins/index/elastic/index/ElasticIndexWriterTest.java
@@ -63,7 +63,7 @@ public class ElasticIndexWriterTest {
     }
 
     @Test
-    public void singleUpdateDocument() {
+    public void singleUpdateDocument() throws IOException {
         indexWriter.updateDocument("/foo", new ElasticDocument("/foo"));
 
         ArgumentCaptor<IndexRequest> acIndexRequest = ArgumentCaptor.forClass(IndexRequest.class);
@@ -75,7 +75,7 @@ public class ElasticIndexWriterTest {
     }
 
     @Test
-    public void singleDeleteDocument() {
+    public void singleDeleteDocument() throws IOException {
         indexWriter.deleteDocuments("/bar");
 
         ArgumentCaptor<DeleteRequest> acDeleteRequest = ArgumentCaptor.forClass(DeleteRequest.class);
@@ -87,7 +87,7 @@ public class ElasticIndexWriterTest {
     }
 
     @Test
-    public void multiRequests() {
+    public void multiRequests() throws IOException {
         indexWriter.updateDocument("/foo", new ElasticDocument("/foo"));
         indexWriter.updateDocument("/bar", new ElasticDocument("/bar"));
         indexWriter.deleteDocuments("/foo");
@@ -98,7 +98,7 @@ public class ElasticIndexWriterTest {
     }
 
     @Test
-    public void longDocumentPath() {
+    public void longDocumentPath() throws IOException {
         String generatedPath = randomString(1024);
 
         indexWriter.updateDocument(generatedPath, new ElasticDocument(generatedPath));