Skip to content

Commit 87cfe45

Browse files
authored
When a primary shard uses the read-only engine, it always returns a RefreshResult.NO_REFRESH for refreshes. Since #93600 we added an extra roundtrip to hook unpromotable shard refresh logic. This hook is always executed, even if there are no unpromotable shards, but the UnpromotableShardRefreshRequest would fail if the primary shard returns a RefreshResult.NO_REFRESH result. (#129343)
Fix to be backported to several versions as it's annoying. Closes #129036 Backport of #129176 for 8.17.8
1 parent 841feb6 commit 87cfe45

File tree

8 files changed

+83
-8
lines changed

8 files changed

+83
-8
lines changed

build-tools-internal/src/main/resources/changelog-schema.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
"Rollup",
8787
"SQL",
8888
"Search",
89+
"Searchable Snapshots",
8990
"Security",
9091
"Snapshot/Restore",
9192
"Stats",

docs/changelog/129176.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 129176
2+
summary: Adjust unpromotable shard refresh request validation to allow `RefreshResult.NO_REFRESH`
3+
area: Searchable Snapshots
4+
type: bug
5+
issues:
6+
- 129036

server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportShardRefreshAction.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ public void onPrimaryOperationComplete(
118118
IndexShardRoutingTable indexShardRoutingTable,
119119
ActionListener<Void> listener
120120
) {
121-
assert replicaRequest.primaryRefreshResult.refreshed() : "primary has not refreshed";
122121
UnpromotableShardRefreshRequest unpromotableReplicaRequest = new UnpromotableShardRefreshRequest(
123122
indexShardRoutingTable,
124123
replicaRequest.primaryRefreshResult.primaryTerm(),

server/src/main/java/org/elasticsearch/action/admin/indices/refresh/TransportUnpromotableShardRefreshAction.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.elasticsearch.cluster.action.shard.ShardStateAction;
1717
import org.elasticsearch.cluster.service.ClusterService;
1818
import org.elasticsearch.common.io.stream.StreamInput;
19+
import org.elasticsearch.index.engine.Engine;
1920
import org.elasticsearch.indices.IndicesService;
2021
import org.elasticsearch.injection.guice.Inject;
2122
import org.elasticsearch.tasks.Task;
@@ -88,12 +89,13 @@ protected void unpromotableShardOperation(
8889
+ transportService.getLocalNodeConnection().getTransportVersion()
8990
+ " (before FAST_REFRESH_RCO_2)";
9091

92+
var primaryTerm = request.getPrimaryTerm();
93+
assert Engine.UNKNOWN_PRIMARY_TERM < primaryTerm : primaryTerm;
94+
var segmentGeneration = request.getSegmentGeneration();
95+
assert Engine.RefreshResult.UNKNOWN_GENERATION < segmentGeneration : segmentGeneration;
96+
9197
ActionListener.run(responseListener, listener -> {
92-
shard.waitForPrimaryTermAndGeneration(
93-
request.getPrimaryTerm(),
94-
request.getSegmentGeneration(),
95-
listener.map(l -> ActionResponse.Empty.INSTANCE)
96-
);
98+
shard.waitForPrimaryTermAndGeneration(primaryTerm, segmentGeneration, listener.map(l -> ActionResponse.Empty.INSTANCE));
9799
});
98100
}
99101

server/src/main/java/org/elasticsearch/action/admin/indices/refresh/UnpromotableShardRefreshRequest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,9 @@ public UnpromotableShardRefreshRequest(StreamInput in) throws IOException {
4747
@Override
4848
public ActionRequestValidationException validate() {
4949
ActionRequestValidationException validationException = super.validate();
50-
if (segmentGeneration == Engine.RefreshResult.UNKNOWN_GENERATION) {
50+
if (segmentGeneration == Engine.RefreshResult.UNKNOWN_GENERATION && primaryTerm == Engine.UNKNOWN_PRIMARY_TERM) {
51+
// read-only primary shards (like searchable snapshot shard) return Engine.RefreshResult.NO_REFRESH during refresh
52+
} else if (segmentGeneration == Engine.RefreshResult.UNKNOWN_GENERATION) {
5153
validationException = addValidationError("segment generation is unknown", validationException);
5254
}
5355
return validationException;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ public RefreshResult refresh(String source) {
443443

444444
@Override
445445
public void maybeRefresh(String source, ActionListener<RefreshResult> listener) throws EngineException {
446-
listener.onResponse(RefreshResult.NO_REFRESH);
446+
ActionListener.completeWith(listener, () -> refresh(source));
447447
}
448448

449449
@Override

x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/FrozenSearchableSnapshotsIntegTests.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
import static org.elasticsearch.search.aggregations.AggregationBuilders.dateHistogram;
7272
import static org.elasticsearch.snapshots.SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_STORE_TYPE;
7373
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
74+
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
7475
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailuresAndResponse;
7576
import static org.elasticsearch.xpack.searchablesnapshots.SearchableSnapshots.SNAPSHOT_RECOVERY_STATE_FACTORY_KEY;
7677
import static org.hamcrest.Matchers.arrayWithSize;
@@ -542,6 +543,37 @@ public void testRequestCacheOnFrozen() throws Exception {
542543
}
543544
}
544545

546+
public void testRefreshPartiallyMountedIndex() throws Exception {
547+
internalCluster().ensureAtLeastNumDataNodes(2);
548+
549+
final var index = "index";
550+
createIndex(index, 1, 0);
551+
populateIndex(index, 1_000);
552+
553+
final var repository = "repository";
554+
createRepository(repository, FsRepository.TYPE, Settings.builder().put("location", randomRepoPath()));
555+
556+
final var snapshot = "repository";
557+
createFullSnapshot(repository, snapshot);
558+
559+
assertAcked(indicesAdmin().prepareDelete(index));
560+
561+
final var partialIndex = "partial-" + index;
562+
mountSnapshot(
563+
repository,
564+
snapshot,
565+
index,
566+
partialIndex,
567+
Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, randomInt(1)).build(),
568+
MountSearchableSnapshotRequest.Storage.SHARED_CACHE
569+
);
570+
ensureGreen(partialIndex);
571+
572+
// before the fix this would have failed
573+
var refreshResult = indicesAdmin().prepareRefresh(partialIndex).execute().actionGet();
574+
assertNoFailures(refreshResult);
575+
}
576+
545577
public void testTierPreferenceCannotBeRemovedForFrozenIndex() throws Exception {
546578
final String fsRepoName = randomAlphaOfLength(10);
547579
final String indexName = randomAlphaOfLength(10).toLowerCase(Locale.ROOT);

x-pack/plugin/searchable-snapshots/src/internalClusterTest/java/org/elasticsearch/xpack/searchablesnapshots/SearchableSnapshotsIntegTests.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.elasticsearch.index.shard.IndexLongFieldRange;
5252
import org.elasticsearch.indices.IndicesService;
5353
import org.elasticsearch.repositories.RepositoryData;
54+
import org.elasticsearch.repositories.fs.FsRepository;
5455
import org.elasticsearch.rest.RestStatus;
5556
import org.elasticsearch.search.SearchResponseUtils;
5657
import org.elasticsearch.snapshots.SnapshotId;
@@ -89,6 +90,7 @@
8990
import static org.elasticsearch.snapshots.SearchableSnapshotsSettings.SEARCHABLE_SNAPSHOT_STORE_TYPE;
9091
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
9192
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
93+
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoFailures;
9294
import static org.elasticsearch.xpack.searchablesnapshots.SearchableSnapshots.SNAPSHOT_RECOVERY_STATE_FACTORY_KEY;
9395
import static org.hamcrest.Matchers.allOf;
9496
import static org.hamcrest.Matchers.containsString;
@@ -1274,6 +1276,37 @@ public void onFailure(Exception e) {
12741276
}
12751277
}
12761278

1279+
public void testRefreshFullyMountedIndex() throws Exception {
1280+
internalCluster().ensureAtLeastNumDataNodes(2);
1281+
1282+
final var index = "index";
1283+
createIndex(index, 1, 0);
1284+
populateIndex(index, 1_000);
1285+
1286+
final var repository = "repository";
1287+
createRepository(repository, FsRepository.TYPE, Settings.builder().put("location", randomRepoPath()));
1288+
1289+
final var snapshot = "repository";
1290+
createFullSnapshot(repository, snapshot);
1291+
1292+
assertAcked(indicesAdmin().prepareDelete(index));
1293+
1294+
final var fullIndex = "full-" + index;
1295+
mountSnapshot(
1296+
repository,
1297+
snapshot,
1298+
index,
1299+
fullIndex,
1300+
Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, randomInt(1)).build(),
1301+
MountSearchableSnapshotRequest.Storage.FULL_COPY
1302+
);
1303+
ensureGreen(fullIndex);
1304+
1305+
// before the fix this would have failed
1306+
var refreshResult = indicesAdmin().prepareRefresh(fullIndex).execute().actionGet();
1307+
assertNoFailures(refreshResult);
1308+
}
1309+
12771310
private TaskInfo getTaskForActionFromMaster(String action) {
12781311
ListTasksResponse response = client().execute(
12791312
TransportListTasksAction.TYPE,

0 commit comments

Comments
 (0)