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.
This commit is contained in:
parent
564b80303d
commit
1249e6ba5d
|
@ -1514,9 +1514,14 @@ public class InternalEngine extends Engine {
|
||||||
: "Noop tombstone document but _tombstone field is not set [" + doc + " ]";
|
: "Noop tombstone document but _tombstone field is not set [" + doc + " ]";
|
||||||
doc.add(softDeletesField);
|
doc.add(softDeletesField);
|
||||||
indexWriter.addDocument(doc);
|
indexWriter.addDocument(doc);
|
||||||
} catch (Exception ex) {
|
} catch (final Exception ex) {
|
||||||
|
/*
|
||||||
|
* Document level failures when adding a no-op are unexpected, we likely hit something fatal such as the Lucene
|
||||||
|
* index being corrupt, or the Lucene document limit. We have already issued a sequence number here so this is
|
||||||
|
* fatal, fail the engine.
|
||||||
|
*/
|
||||||
if (ex instanceof AlreadyClosedException == false && indexWriter.getTragicException() == null) {
|
if (ex instanceof AlreadyClosedException == false && indexWriter.getTragicException() == null) {
|
||||||
throw new AssertionError("noop operation should never fail at document level", ex);
|
failEngine("no-op origin[" + noOp.origin() + "] seq#[" + noOp.seqNo() + "] failed at document level", ex);
|
||||||
}
|
}
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
@ -2852,4 +2857,5 @@ public class InternalEngine extends Engine {
|
||||||
// remove live entries in the version map
|
// remove live entries in the version map
|
||||||
refresh("restore_version_map_and_checkpoint_tracker", SearcherScope.INTERNAL, true);
|
refresh("restore_version_map_and_checkpoint_tracker", SearcherScope.INTERNAL, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5972,4 +5972,29 @@ public class InternalEngineTests extends EngineTestCase {
|
||||||
equalTo(seqNos));
|
equalTo(seqNos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testNoOpFailure() throws IOException {
|
||||||
|
engine.close();
|
||||||
|
final Settings settings = Settings.builder()
|
||||||
|
.put(defaultSettings.getSettings())
|
||||||
|
.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true).build();
|
||||||
|
final IndexSettings indexSettings = IndexSettingsModule.newIndexSettings(
|
||||||
|
IndexMetaData.builder(defaultSettings.getIndexMetaData()).settings(settings).build());
|
||||||
|
try (Store store = createStore();
|
||||||
|
Engine engine = createEngine((dir, iwc) -> new IndexWriter(dir, iwc) {
|
||||||
|
@Override
|
||||||
|
public long addDocument(Iterable<? extends IndexableField> doc) throws IOException {
|
||||||
|
throw new IllegalArgumentException("fatal");
|
||||||
|
}
|
||||||
|
}, null, null, config(indexSettings, store, createTempDir(), NoMergePolicy.INSTANCE, null))) {
|
||||||
|
final Engine.NoOp op = new Engine.NoOp(0, 0, PRIMARY, System.currentTimeMillis(), "test");
|
||||||
|
final IllegalArgumentException e = expectThrows(IllegalArgumentException. class, () -> engine.noOp(op));
|
||||||
|
assertThat(e.getMessage(), equalTo("fatal"));
|
||||||
|
assertTrue(engine.isClosed.get());
|
||||||
|
assertThat(engine.failedEngine.get(), not(nullValue()));
|
||||||
|
assertThat(engine.failedEngine.get(), instanceOf(IllegalArgumentException.class));
|
||||||
|
assertThat(engine.failedEngine.get().getMessage(), equalTo("fatal"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue