Skip to content

Commit 43a22da

Browse files
committed
Handle no-op document level failures (#46083)
Today we assume that document failures can not occur for no-ops. This assumption is bogus, as they can fail for a variety of reasons such as the Lucene index having reached the document limit. Because of this assumption, we were asserting that such a document-level failure would never happen. When this bogus assertion is violated, we fail the node, a catastrophe. Instead, we need to treat this as a fatal engine exception.
1 parent d92935c commit 43a22da

File tree

3 files changed

+42
-5
lines changed

3 files changed

+42
-5
lines changed

server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,11 +1580,16 @@ private NoOpResult innerNoOp(final NoOp noOp) throws IOException {
15801580
: "Noop tombstone document but _tombstone field is not set [" + doc + " ]";
15811581
doc.add(softDeletesField);
15821582
indexWriter.addDocument(doc);
1583-
} catch (Exception ex) {
1584-
if (maybeFailEngine("noop", ex)) {
1585-
throw ex;
1583+
} catch (final Exception ex) {
1584+
/*
1585+
* Document level failures when adding a no-op are unexpected, we likely hit something fatal such as the Lucene
1586+
* index being corrupt, or the Lucene document limit. We have already issued a sequence number here so this is
1587+
* fatal, fail the engine.
1588+
*/
1589+
if (ex instanceof AlreadyClosedException == false && indexWriter.getTragicException() == null) {
1590+
failEngine("no-op origin[" + noOp.origin() + "] seq#[" + noOp.seqNo() + "] failed at document level", ex);
15861591
}
1587-
failure = ex;
1592+
throw ex;
15881593
}
15891594
}
15901595
if (failure == null) {
@@ -2816,4 +2821,5 @@ public void reinitializeMaxSeqNoOfUpdatesOrDeletes() {
28162821
final long maxSeqNo = SequenceNumbers.max(localCheckpointTracker.getMaxSeqNo(), translog.getMaxSeqNo());
28172822
advanceMaxSeqNoOfUpdatesOrDeletes(maxSeqNo);
28182823
}
2824+
28192825
}

server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5784,4 +5784,35 @@ public void testRefreshAndFailEngineConcurrently() throws Exception {
57845784
}
57855785
assertThat(engine.config().getCircuitBreakerService().getBreaker(CircuitBreaker.ACCOUNTING).getUsed(), equalTo(0L));
57865786
}
5787+
5788+
public void testNoOpFailure() throws IOException {
5789+
engine.close();
5790+
final Settings settings = Settings.builder()
5791+
.put(defaultSettings.getSettings())
5792+
.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true).build();
5793+
final IndexSettings indexSettings = IndexSettingsModule.newIndexSettings(
5794+
IndexMetaData.builder(defaultSettings.getIndexMetaData()).settings(settings).build());
5795+
try (Store store = createStore();
5796+
Engine engine = createEngine(
5797+
(dir, iwc) -> new IndexWriter(dir, iwc) {
5798+
5799+
@Override
5800+
public long addDocument(Iterable<? extends IndexableField> doc) throws IOException {
5801+
throw new IllegalArgumentException("fatal");
5802+
}
5803+
5804+
},
5805+
null,
5806+
null,
5807+
config(indexSettings, store, createTempDir(), NoMergePolicy.INSTANCE, null))) {
5808+
final Engine.NoOp op = new Engine.NoOp(0, 0, PRIMARY, System.currentTimeMillis(), "test");
5809+
final IllegalArgumentException e = expectThrows(IllegalArgumentException. class, () -> engine.noOp(op));
5810+
assertThat(e.getMessage(), equalTo("fatal"));
5811+
assertTrue(engine.isClosed.get());
5812+
assertThat(engine.failedEngine.get(), not(nullValue()));
5813+
assertThat(engine.failedEngine.get(), instanceOf(IllegalArgumentException.class));
5814+
assertThat(engine.failedEngine.get().getMessage(), equalTo("fatal"));
5815+
}
5816+
}
5817+
57875818
}

test/framework/src/main/java/org/elasticsearch/index/engine/EngineTestCase.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ protected InternalEngine createEngine(EngineConfig config) throws IOException {
503503
return createEngine(null, null, null, config);
504504
}
505505

506-
private InternalEngine createEngine(@Nullable IndexWriterFactory indexWriterFactory,
506+
protected InternalEngine createEngine(@Nullable IndexWriterFactory indexWriterFactory,
507507
@Nullable BiFunction<Long, Long, LocalCheckpointTracker> localCheckpointTrackerSupplier,
508508
@Nullable ToLongBiFunction<Engine, Engine.Operation> seqNoForOperation,
509509
EngineConfig config) throws IOException {

0 commit comments

Comments
 (0)