Skip to content

Commit 857d169

Browse files
committed
Account soft-deletes in FrozenEngine (#51192) (#51229)
Currently, we do not exclude soft-deleted documents when opening index reader in the FrozenEngine. Backport of #51192
1 parent 6c04815 commit 857d169

File tree

7 files changed

+103
-27
lines changed

7 files changed

+103
-27
lines changed

qa/rolling-upgrade/src/test/java/org/elasticsearch/upgrades/RecoveryIT.java

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -540,28 +540,6 @@ private static Version indexVersionCreated(final String indexName) throws IOExce
540540
return Version.fromId(Integer.parseInt(ObjectPath.createFromResponse(response).evaluate(versionCreatedSetting)));
541541
}
542542

543-
/**
544-
* Returns the minimum node version among all nodes of the cluster
545-
*/
546-
private static Version minimumNodeVersion() throws IOException {
547-
final Request request = new Request("GET", "_nodes");
548-
request.addParameter("filter_path", "nodes.*.version");
549-
550-
final Response response = client().performRequest(request);
551-
final Map<String, Object> nodes = ObjectPath.createFromResponse(response).evaluate("nodes");
552-
553-
Version minVersion = null;
554-
for (Map.Entry<String, Object> node : nodes.entrySet()) {
555-
@SuppressWarnings("unchecked")
556-
Version nodeVersion = Version.fromString((String) ((Map<String, Object>) node.getValue()).get("version"));
557-
if (minVersion == null || minVersion.after(nodeVersion)) {
558-
minVersion = nodeVersion;
559-
}
560-
}
561-
assertNotNull(minVersion);
562-
return minVersion;
563-
}
564-
565543
/**
566544
* Asserts that an index is closed in the cluster state. If `checkRoutingTable` is true, it also asserts
567545
* that the index has started shards.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public NoOpEngine(EngineConfig config) {
5454
super(config, null, null, true, Function.identity());
5555
this.stats = new SegmentsStats();
5656
Directory directory = store.directory();
57-
try (DirectoryReader reader = DirectoryReader.open(directory, OFF_HEAP_READER_ATTRIBUTES)) {
57+
try (DirectoryReader reader = openDirectory(directory, config.getIndexSettings())) {
5858
for (LeafReaderContext ctx : reader.getContext().leaves()) {
5959
SegmentReader segmentReader = Lucene.segmentReader(ctx.reader());
6060
fillSegmentStats(segmentReader, true, stats);

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.elasticsearch.common.lucene.index.ElasticsearchDirectoryReader;
3434
import org.elasticsearch.common.util.concurrent.ReleasableLock;
3535
import org.elasticsearch.core.internal.io.IOUtils;
36+
import org.elasticsearch.index.IndexSettings;
3637
import org.elasticsearch.index.mapper.MapperService;
3738
import org.elasticsearch.index.seqno.SeqNoStats;
3839
import org.elasticsearch.index.seqno.SequenceNumbers;
@@ -68,7 +69,7 @@ public class ReadOnlyEngine extends Engine {
6869
* Reader attributes used for read only engines. These attributes prevent loading term dictionaries on-heap even if the field is an
6970
* ID field if we are reading form memory maps.
7071
*/
71-
public static final Map<String, String> OFF_HEAP_READER_ATTRIBUTES = Collections.singletonMap(BlockTreeTermsReader.FST_MODE_KEY,
72+
private static final Map<String, String> OFF_HEAP_READER_ATTRIBUTES = Collections.singletonMap(BlockTreeTermsReader.FST_MODE_KEY,
7273
BlockTreeTermsReader.FSTLoadMode.AUTO.name());
7374
private final SegmentInfos lastCommittedSegmentInfos;
7475
private final SeqNoStats seqNoStats;
@@ -534,4 +535,13 @@ public void advanceMaxSeqNoOfUpdatesOrDeletes(long maxSeqNoOfUpdatesOnPrimary) {
534535
assert maxSeqNoOfUpdatesOnPrimary <= getMaxSeqNoOfUpdatesOrDeletes() :
535536
maxSeqNoOfUpdatesOnPrimary + ">" + getMaxSeqNoOfUpdatesOrDeletes();
536537
}
538+
539+
protected DirectoryReader openDirectory(Directory dir, IndexSettings indexSettings) throws IOException {
540+
final DirectoryReader reader = DirectoryReader.open(dir, OFF_HEAP_READER_ATTRIBUTES);
541+
if (indexSettings.isSoftDeleteEnabled()) {
542+
return new SoftDeletesDirectoryReaderWrapper(reader, Lucene.SOFT_DELETES_FIELD);
543+
} else {
544+
return reader;
545+
}
546+
}
537547
}

test/framework/src/main/java/org/elasticsearch/test/rest/ESRestTestCase.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import org.elasticsearch.rest.RestStatus;
5656
import org.elasticsearch.snapshots.SnapshotState;
5757
import org.elasticsearch.test.ESTestCase;
58+
import org.elasticsearch.test.rest.yaml.ObjectPath;
5859
import org.hamcrest.Matchers;
5960
import org.junit.After;
6061
import org.junit.AfterClass;
@@ -1144,4 +1145,26 @@ public void ensurePeerRecoveryRetentionLeasesRenewedAndSynced(String index) thro
11441145
}
11451146
}, 60, TimeUnit.SECONDS);
11461147
}
1148+
1149+
/**
1150+
* Returns the minimum node version among all nodes of the cluster
1151+
*/
1152+
protected static Version minimumNodeVersion() throws IOException {
1153+
final Request request = new Request("GET", "_nodes");
1154+
request.addParameter("filter_path", "nodes.*.version");
1155+
1156+
final Response response = client().performRequest(request);
1157+
final Map<String, Object> nodes = ObjectPath.createFromResponse(response).evaluate("nodes");
1158+
1159+
Version minVersion = null;
1160+
for (Map.Entry<String, Object> node : nodes.entrySet()) {
1161+
@SuppressWarnings("unchecked")
1162+
Version nodeVersion = Version.fromString((String) ((Map<String, Object>) node.getValue()).get("version"));
1163+
if (minVersion == null || minVersion.after(nodeVersion)) {
1164+
minVersion = nodeVersion;
1165+
}
1166+
}
1167+
assertNotNull(minVersion);
1168+
return minVersion;
1169+
}
11471170
}

x-pack/plugin/frozen-indices/src/main/java/org/elasticsearch/index/engine/FrozenEngine.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public FrozenEngine(EngineConfig config) {
7777

7878
boolean success = false;
7979
Directory directory = store.directory();
80-
try (DirectoryReader reader = DirectoryReader.open(directory, OFF_HEAP_READER_ATTRIBUTES)) {
80+
try (DirectoryReader reader = openDirectory(directory, config.getIndexSettings())) {
8181
canMatchReader = ElasticsearchDirectoryReader.wrap(new RewriteCachingDirectoryReader(directory, reader.leaves()),
8282
config.getShardId());
8383
// we record the segment stats here - that's what the reader needs when it's open and it give the user
@@ -167,7 +167,7 @@ private synchronized ElasticsearchDirectoryReader getOrOpenReader() throws IOExc
167167
for (ReferenceManager.RefreshListener listeners : config ().getInternalRefreshListener()) {
168168
listeners.beforeRefresh();
169169
}
170-
final DirectoryReader dirReader = DirectoryReader.open(engineConfig.getStore().directory(), OFF_HEAP_READER_ATTRIBUTES);
170+
final DirectoryReader dirReader = openDirectory(engineConfig.getStore().directory(), engineConfig.getIndexSettings());
171171
reader = lastOpenedReader = wrapReader(dirReader, Function.identity());
172172
processReader(reader);
173173
reader.getReaderCacheHelper().addClosedListener(this::onReaderClosed);

x-pack/plugin/frozen-indices/src/test/java/org/elasticsearch/index/engine/FrozenEngineTests.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
import java.util.concurrent.atomic.AtomicInteger;
3434
import java.util.concurrent.atomic.AtomicLong;
3535

36+
import static org.hamcrest.Matchers.equalTo;
37+
3638
public class FrozenEngineTests extends EngineTestCase {
3739

3840
public void testAcquireReleaseReset() throws IOException {
@@ -328,4 +330,30 @@ public void testCanMatch() throws IOException {
328330
}
329331
}
330332
}
333+
334+
public void testSearchers() throws Exception {
335+
IOUtils.close(engine, store);
336+
final AtomicLong globalCheckpoint = new AtomicLong(SequenceNumbers.NO_OPS_PERFORMED);
337+
try (Store store = createStore()) {
338+
EngineConfig config = config(defaultSettings, store, createTempDir(), newMergePolicy(), null, null, null,
339+
globalCheckpoint::get, new NoneCircuitBreakerService());
340+
final int totalDocs;
341+
try (InternalEngine engine = createEngine(config)) {
342+
applyOperations(engine, generateHistoryOnReplica(between(10, 1000), false, randomBoolean(), randomBoolean()));
343+
globalCheckpoint.set(engine.getProcessedLocalCheckpoint());
344+
engine.syncTranslog();
345+
engine.flush();
346+
engine.refresh("test");
347+
try (Engine.Searcher searcher = engine.acquireSearcher("test")) {
348+
totalDocs = searcher.search(new MatchAllDocsQuery(), Integer.MAX_VALUE).scoreDocs.length;
349+
}
350+
}
351+
try (FrozenEngine frozenEngine = new FrozenEngine(config)) {
352+
try (Engine.Searcher searcher = frozenEngine.acquireSearcher("test")) {
353+
TopDocs topDocs = searcher.search(new MatchAllDocsQuery(), Integer.MAX_VALUE);
354+
assertThat(topDocs.scoreDocs.length, equalTo(totalDocs));
355+
}
356+
}
357+
}
358+
}
331359
}

x-pack/qa/full-cluster-restart/src/test/java/org/elasticsearch/xpack/restart/FullClusterRestartIT.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.elasticsearch.common.xcontent.XContentType;
2222
import org.elasticsearch.common.xcontent.json.JsonXContent;
2323
import org.elasticsearch.common.xcontent.support.XContentMapValues;
24+
import org.elasticsearch.index.IndexSettings;
2425
import org.elasticsearch.rest.RestStatus;
2526
import org.elasticsearch.rest.action.document.RestGetAction;
2627
import org.elasticsearch.rest.action.document.RestIndexAction;
@@ -304,7 +305,7 @@ public void testRollupAfterRestart() throws Exception {
304305
assertRollUpJob("rollup-job-test");
305306
}
306307
}
307-
308+
308309
public void testSlmStats() throws IOException {
309310
SnapshotLifecyclePolicy slmPolicy = new SnapshotLifecyclePolicy("test-policy", "test-policy", "* * * 31 FEB ? *", "test-repo",
310311
Collections.singletonMap("indices", Collections.singletonList("*")), null);
@@ -823,4 +824,40 @@ private Map<String, Object> getJob(Map<String, Object> jobsMap, String targetJob
823824
}
824825
return null;
825826
}
827+
828+
public void testFrozenIndexAfterRestarted() throws Exception {
829+
final String index = "test_frozen_index";
830+
if (isRunningAgainstOldCluster()) {
831+
Settings.Builder settings = Settings.builder();
832+
if (minimumNodeVersion().onOrAfter(Version.V_6_5_0) && randomBoolean()) {
833+
settings.put(IndexSettings.INDEX_SOFT_DELETES_SETTING.getKey(), randomBoolean());
834+
}
835+
createIndex(index, settings.build());
836+
ensureGreen(index);
837+
int numDocs = randomIntBetween(10, 500);
838+
for (int i = 0; i < numDocs; i++) {
839+
int id = randomIntBetween(0, 100);
840+
final Request indexRequest = new Request("POST", "/" + index + "/" + "_doc/" + id);
841+
indexRequest.setJsonEntity(Strings.toString(JsonXContent.contentBuilder().startObject().field("f", "v").endObject()));
842+
assertOK(client().performRequest(indexRequest));
843+
if (rarely()) {
844+
flush(index, randomBoolean());
845+
}
846+
}
847+
} else {
848+
ensureGreen(index);
849+
final int totalHits = (int) XContentMapValues.extractValue("hits.total.value",
850+
entityAsMap(client().performRequest(new Request("GET", "/" + index + "/_search"))));
851+
assertOK(client().performRequest(new Request("POST", index + "/_freeze")));
852+
ensureGreen(index);
853+
assertNoFileBasedRecovery(index, n -> true);
854+
final Request request = new Request("GET", "/" + index + "/_search");
855+
request.addParameter("ignore_throttled", "false");
856+
assertThat(XContentMapValues.extractValue("hits.total.value", entityAsMap(client().performRequest(request))),
857+
equalTo(totalHits));
858+
assertOK(client().performRequest(new Request("POST", index + "/_unfreeze")));
859+
ensureGreen(index);
860+
assertNoFileBasedRecovery(index, n -> true);
861+
}
862+
}
826863
}

0 commit comments

Comments
 (0)