Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions docs/changelog/125650.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pr: 125650
summary: Load `FieldInfos` from store if not yet initialised through a refresh on
`IndexShard`
area: Search
type: bug
issues:
- 125483
40 changes: 30 additions & 10 deletions server/src/main/java/org/elasticsearch/index/shard/IndexShard.java
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@
import java.io.Closeable;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.nio.channels.ClosedByInterruptException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
Expand Down Expand Up @@ -413,7 +415,6 @@ public IndexShard(
this.refreshFieldHasValueListener = new RefreshFieldHasValueListener();
this.relativeTimeInNanosSupplier = relativeTimeInNanosSupplier;
this.indexCommitListener = indexCommitListener;
this.fieldInfos = FieldInfos.EMPTY;
}

public ThreadPool getThreadPool() {
Expand Down Expand Up @@ -1011,12 +1012,26 @@ private Engine.IndexResult applyIndexOperation(
return index(engine, operation);
}

public void setFieldInfos(FieldInfos fieldInfos) {
this.fieldInfos = fieldInfos;
private static final VarHandle FIELD_INFOS;

static {
try {
FIELD_INFOS = MethodHandles.lookup().findVarHandle(IndexShard.class, "fieldInfos", FieldInfos.class);
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}

public FieldInfos getFieldInfos() {
return fieldInfos;
var res = fieldInfos;
if (res == null) {
// don't replace field infos loaded via the refresh listener to avoid overwriting the field with an older version of the
// field infos when racing with a refresh
var read = loadFieldInfos();
var existing = (FieldInfos) FIELD_INFOS.compareAndExchange(this, null, read);
return existing == null ? read : existing;
}
return res;
}

public static Engine.Index prepareIndex(
Expand Down Expand Up @@ -4067,16 +4082,21 @@ public void beforeRefresh() {}

@Override
public void afterRefresh(boolean didRefresh) {
if (enableFieldHasValue && (didRefresh || fieldInfos == FieldInfos.EMPTY)) {
try (Engine.Searcher hasValueSearcher = getEngine().acquireSearcher("field_has_value")) {
setFieldInfos(FieldInfos.getMergedFieldInfos(hasValueSearcher.getIndexReader()));
} catch (AlreadyClosedException ignore) {
// engine is closed - no updated FieldInfos is fine
}
if (enableFieldHasValue && (didRefresh || fieldInfos == null)) {
FIELD_INFOS.setRelease(IndexShard.this, loadFieldInfos());
}
}
}

private FieldInfos loadFieldInfos() {
try (Engine.Searcher hasValueSearcher = getEngine().acquireSearcher("field_has_value")) {
return FieldInfos.getMergedFieldInfos(hasValueSearcher.getIndexReader());
} catch (AlreadyClosedException ignore) {
// engine is closed - no update to FieldInfos is fine
}
return FieldInfos.EMPTY;
}

/**
* Returns the shard-level field stats, which includes the number of segments in the latest NRT reader of this shard
* and the total number of fields across those segments.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

package org.elasticsearch.xpack.searchablesnapshots;

import org.elasticsearch.action.fieldcaps.FieldCapabilitiesResponse;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchType;
Expand Down Expand Up @@ -125,5 +126,9 @@ public void testKeywordSortedQueryOnFrozen() throws Exception {
assertThat(searchResponse.getTotalShards(), equalTo(20));
assertThat(searchResponse.getHits().getTotalHits().value, equalTo(4L));
});

// check that field_caps empty field filtering works as well
FieldCapabilitiesResponse response = client().prepareFieldCaps(mountedIndices).setFields("*").setincludeEmptyFields(false).get();
assertNotNull(response.getField("keyword"));
}
}