Require soft-deletes when access changes snapshot (#36446)

Today we do not enforce soft-deletes when accessing the Lucene changes
snapshot. This might lead to internal errors because we assume
soft-deletes are enabled in that code path.
This commit is contained in:
Nhat Nguyen 2018-12-11 11:18:10 -05:00 committed by GitHub
parent 8b821706cc
commit 084e06e481
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 29 additions and 5 deletions

View File

@ -722,7 +722,8 @@ public abstract class Engine implements Closeable {
public abstract Closeable acquireRetentionLockForPeerRecovery(); public abstract Closeable acquireRetentionLockForPeerRecovery();
/** /**
* Creates a new history snapshot from Lucene for reading operations whose seqno in the requesting seqno range (both inclusive) * Creates a new history snapshot from Lucene for reading operations whose seqno in the requesting seqno range (both inclusive).
* This feature requires soft-deletes enabled. If soft-deletes are disabled, this method will throw an {@link IllegalStateException}.
*/ */
public abstract Translog.Snapshot newChangesSnapshot(String source, MapperService mapperService, public abstract Translog.Snapshot newChangesSnapshot(String source, MapperService mapperService,
long fromSeqNo, long toSeqNo, boolean requiredFullRange) throws IOException; long fromSeqNo, long toSeqNo, boolean requiredFullRange) throws IOException;

View File

@ -2481,7 +2481,9 @@ public class InternalEngine extends Engine {
@Override @Override
public Translog.Snapshot newChangesSnapshot(String source, MapperService mapperService, public Translog.Snapshot newChangesSnapshot(String source, MapperService mapperService,
long fromSeqNo, long toSeqNo, boolean requiredFullRange) throws IOException { long fromSeqNo, long toSeqNo, boolean requiredFullRange) throws IOException {
// TODO: Should we defer the refresh until we really need it? if (softDeleteEnabled == false) {
throw new IllegalStateException("accessing changes snapshot requires soft-deletes enabled");
}
ensureOpen(); ensureOpen();
refreshIfNeeded(source, toSeqNo); refreshIfNeeded(source, toSeqNo);
Searcher searcher = acquireSearcher(source, SearcherScope.INTERNAL); Searcher searcher = acquireSearcher(source, SearcherScope.INTERNAL);

View File

@ -243,6 +243,9 @@ public class ReadOnlyEngine extends Engine {
@Override @Override
public Translog.Snapshot newChangesSnapshot(String source, MapperService mapperService, long fromSeqNo, long toSeqNo, public Translog.Snapshot newChangesSnapshot(String source, MapperService mapperService, long fromSeqNo, long toSeqNo,
boolean requiredFullRange) throws IOException { boolean requiredFullRange) throws IOException {
if (engineConfig.getIndexSettings().isSoftDeleteEnabled() == false) {
throw new IllegalStateException("accessing changes snapshot requires soft-deletes enabled");
}
return readHistoryOperations(source, mapperService, fromSeqNo); return readHistoryOperations(source, mapperService, fromSeqNo);
} }

View File

@ -3411,8 +3411,10 @@ public class InternalEngineTests extends EngineTestCase {
TopDocs topDocs = searcher.searcher().search(new MatchAllDocsQuery(), 10); TopDocs topDocs = searcher.searcher().search(new MatchAllDocsQuery(), 10);
assertEquals(1, topDocs.totalHits.value); assertEquals(1, topDocs.totalHits.value);
} }
List<Translog.Operation> ops = readAllOperationsInLucene(engine, createMapperService("test")); if (engine.engineConfig.getIndexSettings().isSoftDeleteEnabled()) {
assertThat(ops.stream().map(o -> o.seqNo()).collect(Collectors.toList()), hasItem(20L)); List<Translog.Operation> ops = readAllOperationsInLucene(engine, createMapperService("test"));
assertThat(ops.stream().map(o -> o.seqNo()).collect(Collectors.toList()), hasItem(20L));
}
} }
public void testRetryWithAutogeneratedIdWorksAndNoDuplicateDocs() throws IOException { public void testRetryWithAutogeneratedIdWorksAndNoDuplicateDocs() throws IOException {
@ -5292,8 +5294,11 @@ public class InternalEngineTests extends EngineTestCase {
final MapperService mapperService = createMapperService("test"); final MapperService mapperService = createMapperService("test");
final long maxSeqNo = randomLongBetween(10, 50); final long maxSeqNo = randomLongBetween(10, 50);
final AtomicLong refreshCounter = new AtomicLong(); final AtomicLong refreshCounter = new AtomicLong();
final IndexSettings indexSettings = IndexSettingsModule.newIndexSettings(
IndexMetaData.builder(defaultSettings.getIndexMetaData()).settings(Settings.builder().
put(defaultSettings.getSettings()).put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), true)).build());
try (Store store = createStore(); try (Store store = createStore();
InternalEngine engine = createEngine(config(defaultSettings, store, createTempDir(), newMergePolicy(), InternalEngine engine = createEngine(config(indexSettings, store, createTempDir(), newMergePolicy(),
null, null,
new ReferenceManager.RefreshListener() { new ReferenceManager.RefreshListener() {
@Override @Override
@ -5481,6 +5486,19 @@ public class InternalEngineTests extends EngineTestCase {
} }
} }
public void testRequireSoftDeletesWhenAccessingChangesSnapshot() throws Exception {
try (Store store = createStore()) {
final IndexSettings indexSettings = IndexSettingsModule.newIndexSettings(
IndexMetaData.builder(defaultSettings.getIndexMetaData()).settings(Settings.builder().
put(defaultSettings.getSettings()).put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), false)).build());
try (InternalEngine engine = createEngine(config(indexSettings, store, createTempDir(), newMergePolicy(), null))) {
IllegalStateException error = expectThrows(IllegalStateException.class,
() -> engine.newChangesSnapshot("test", createMapperService("test"), 0, randomNonNegativeLong(), randomBoolean()));
assertThat(error.getMessage(), equalTo("accessing changes snapshot requires soft-deletes enabled"));
}
}
}
static void trimUnsafeCommits(EngineConfig config) throws IOException { static void trimUnsafeCommits(EngineConfig config) throws IOException {
final Store store = config.getStore(); final Store store = config.getStore();
final TranslogConfig translogConfig = config.getTranslogConfig(); final TranslogConfig translogConfig = config.getTranslogConfig();