From ddb20f1d00e2fc6b9cebe624b6252f06ddaf6391 Mon Sep 17 00:00:00 2001 From: Nhat Nguyen Date: Mon, 25 Feb 2019 15:31:48 -0500 Subject: [PATCH] Bubble up exception when processing NoOp (#39338) Today we do not bubble up exceptions when processing NoOps but always treat them as document-level failures. This incorrect treatment causes the assert_no_failure being tripped in peer-recovery if IndexWriter was closed exceptionally before. Closes #38898 --- .../elasticsearch/index/engine/Engine.java | 2 +- .../index/engine/InternalEngine.java | 12 +++++++++--- .../elasticsearch/index/shard/IndexShard.java | 2 +- .../index/engine/InternalEngineTests.java | 19 ++++++++----------- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/server/src/main/java/org/elasticsearch/index/engine/Engine.java b/server/src/main/java/org/elasticsearch/index/engine/Engine.java index 7c41a436ca25..2c8ac5b2f2c2 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/Engine.java +++ b/server/src/main/java/org/elasticsearch/index/engine/Engine.java @@ -400,7 +400,7 @@ public Condition newCondition() { */ public abstract DeleteResult delete(Delete delete) throws IOException; - public abstract NoOpResult noOp(NoOp noOp); + public abstract NoOpResult noOp(NoOp noOp) throws IOException; /** * Base class for index and delete operation results diff --git a/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java b/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java index 4bda8baf7f83..c5d42835a701 100644 --- a/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java +++ b/server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java @@ -1538,13 +1538,19 @@ public void maybePruneDeletes() { } @Override - public NoOpResult noOp(final NoOp noOp) { - NoOpResult noOpResult; + public NoOpResult noOp(final NoOp noOp) throws IOException { + final NoOpResult noOpResult; try (ReleasableLock ignored = readLock.acquire()) { + ensureOpen(); markSeqNoAsSeen(noOp.seqNo()); noOpResult = innerNoOp(noOp); } catch (final Exception e) { - noOpResult = new NoOpResult(getPrimaryTerm(), noOp.seqNo(), e); + try { + maybeFailEngine("noop", e); + } catch (Exception inner) { + e.addSuppressed(inner); + } + throw e; } return noOpResult; } diff --git a/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java b/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java index 2ddea363b2e5..ef861e9ac9c6 100644 --- a/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java +++ b/server/src/main/java/org/elasticsearch/index/shard/IndexShard.java @@ -832,7 +832,7 @@ private Engine.NoOpResult markSeqNoAsNoop(Engine engine, long seqNo, long opPrim return noOp(engine, noOp); } - private Engine.NoOpResult noOp(Engine engine, Engine.NoOp noOp) { + private Engine.NoOpResult noOp(Engine engine, Engine.NoOp noOp) throws IOException { active.set(true); if (logger.isTraceEnabled()) { logger.trace("noop (seq# [{}])", noOp.seqNo()); diff --git a/server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java b/server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java index ecede189ce67..7ac8e32b1667 100644 --- a/server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java +++ b/server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java @@ -3252,7 +3252,7 @@ public void testHandleDocumentFailure() throws Exception { final ParsedDocument doc3 = testParsedDocument("3", null, testDocumentWithTextField(), B_1, null); AtomicReference throwingIndexWriter = new AtomicReference<>(); - try (Engine engine = createEngine(defaultSettings, store, createTempDir(), NoMergePolicy.INSTANCE, + try (InternalEngine engine = createEngine(defaultSettings, store, createTempDir(), NoMergePolicy.INSTANCE, (directory, iwc) -> { throwingIndexWriter.set(new ThrowingIndexWriter(directory, iwc)); return throwingIndexWriter.get(); @@ -3317,16 +3317,13 @@ public BytesRef binaryValue() { engine.close(); } // now the engine is closed check we respond correctly - try { - if (randomBoolean()) { - engine.index(indexForDoc(doc1)); - } else { - engine.delete(new Engine.Delete("test", "", newUid(doc1), primaryTerm.get())); - } - fail("engine should be closed"); - } catch (Exception e) { - assertThat(e, instanceOf(AlreadyClosedException.class)); - } + expectThrows(AlreadyClosedException.class, () -> engine.index(indexForDoc(doc1))); + expectThrows(AlreadyClosedException.class, + () -> engine.delete(new Engine.Delete("test", "", newUid(doc1), primaryTerm.get()))); + expectThrows(AlreadyClosedException.class, () -> engine.noOp( + new Engine.NoOp(engine.getLocalCheckpointTracker().generateSeqNo(), + engine.config().getPrimaryTermSupplier().getAsLong(), + randomFrom(Engine.Operation.Origin.values()), randomNonNegativeLong(), "test"))); } } }