Skip to content

Commit 46f28c9

Browse files
committed
Allow searchable snapshots indices in version N-2 to join
1 parent d23319b commit 46f28c9

File tree

10 files changed

+285
-36
lines changed

10 files changed

+285
-36
lines changed

server/src/main/java/org/elasticsearch/cluster/coordination/NodeJoinExecutor.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import java.util.function.Function;
4848
import java.util.stream.Collectors;
4949

50+
import static org.elasticsearch.cluster.metadata.IndexMetadataVerifier.isReadOnlySupportedVersion;
5051
import static org.elasticsearch.gateway.GatewayService.STATE_NOT_RECOVERED_BLOCK;
5152

5253
public class NodeJoinExecutor implements ClusterStateTaskExecutor<JoinTask> {
@@ -177,7 +178,12 @@ public ClusterState execute(BatchExecutionContext<JoinTask> batchExecutionContex
177178
enforceNodeFeatureBarrier(node.getId(), allNodesFeatures, features);
178179
// we do this validation quite late to prevent race conditions between nodes joining and importing dangling indices
179180
// we have to reject nodes that don't support all indices we have in this cluster
180-
ensureIndexCompatibility(node.getMinIndexVersion(), node.getMaxIndexVersion(), initialState.getMetadata());
181+
ensureIndexCompatibility(
182+
node.getMinIndexVersion(),
183+
node.getMinReadOnlyIndexVersion(),
184+
node.getMaxIndexVersion(),
185+
initialState.getMetadata()
186+
);
181187
nodesBuilder.add(node);
182188
compatibilityVersionsMap.put(node.getId(), compatibilityVersions);
183189
nodeFeatures.put(node.getId(), features);
@@ -362,7 +368,12 @@ private static void blockForbiddenVersions(TransportVersion joiningTransportVers
362368
* @see IndexVersions#MINIMUM_COMPATIBLE
363369
* @throws IllegalStateException if any index is incompatible with the given version
364370
*/
365-
public static void ensureIndexCompatibility(IndexVersion minSupportedVersion, IndexVersion maxSupportedVersion, Metadata metadata) {
371+
public static void ensureIndexCompatibility(
372+
IndexVersion minSupportedVersion,
373+
IndexVersion minReadOnlySupportedVersion,
374+
IndexVersion maxSupportedVersion,
375+
Metadata metadata
376+
) {
366377
// we ensure that all indices in the cluster we join are compatible with us no matter if they are
367378
// closed or not we can't read mappings of these indices so we need to reject the join...
368379
for (IndexMetadata idxMetadata : metadata) {
@@ -377,6 +388,9 @@ public static void ensureIndexCompatibility(IndexVersion minSupportedVersion, In
377388
);
378389
}
379390
if (idxMetadata.getCompatibilityVersion().before(minSupportedVersion)) {
391+
if (isReadOnlySupportedVersion(idxMetadata, minSupportedVersion, minReadOnlySupportedVersion)) {
392+
continue;
393+
}
380394
throw new IllegalStateException(
381395
"index "
382396
+ idxMetadata.getIndex()
@@ -477,7 +491,12 @@ public static Collection<BiConsumer<DiscoveryNode, ClusterState>> addBuiltInJoin
477491
final Collection<BiConsumer<DiscoveryNode, ClusterState>> validators = new ArrayList<>();
478492
validators.add((node, state) -> {
479493
ensureNodesCompatibility(node.getVersion(), state.getNodes());
480-
ensureIndexCompatibility(node.getMinIndexVersion(), node.getMaxIndexVersion(), state.getMetadata());
494+
ensureIndexCompatibility(
495+
node.getMinIndexVersion(),
496+
node.getMinReadOnlyIndexVersion(),
497+
node.getMaxIndexVersion(),
498+
state.getMetadata()
499+
);
481500
});
482501
validators.addAll(onJoinValidators);
483502
return Collections.unmodifiableCollection(validators);

server/src/main/java/org/elasticsearch/cluster/metadata/IndexMetadataVerifier.java

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,12 @@ public IndexMetadataVerifier(
8888
* If the index does not need upgrade it returns the index metadata unchanged, otherwise it returns a modified index metadata. If index
8989
* cannot be updated the method throws an exception.
9090
*/
91-
public IndexMetadata verifyIndexMetadata(IndexMetadata indexMetadata, IndexVersion minimumIndexCompatibilityVersion) {
92-
checkSupportedVersion(indexMetadata, minimumIndexCompatibilityVersion);
91+
public IndexMetadata verifyIndexMetadata(
92+
IndexMetadata indexMetadata,
93+
IndexVersion minimumIndexCompatibilityVersion,
94+
IndexVersion minimumReadOnlyIndexCompatibilityVersion
95+
) {
96+
checkSupportedVersion(indexMetadata, minimumIndexCompatibilityVersion, minimumReadOnlyIndexCompatibilityVersion);
9397

9498
// First convert any shared_cache searchable snapshot indices to only use _tier_preference: data_frozen
9599
IndexMetadata newMetadata = convertSharedCacheTierPreference(indexMetadata);
@@ -105,26 +109,66 @@ public IndexMetadata verifyIndexMetadata(IndexMetadata indexMetadata, IndexVersi
105109
}
106110

107111
/**
108-
* Check that the index version is compatible. Elasticsearch does not support indices created before the
109-
* previous major version.
112+
* Check that the index version is compatible. Elasticsearch supports reading and writing indices created in the current version ("N")
113+
+ as well as the previous major version ("N-1"). Elasticsearch only supports reading indices created down to the penultimate version
114+
+ ("N-2") and does not support reading nor writing any version below that.
110115
*/
111-
private static void checkSupportedVersion(IndexMetadata indexMetadata, IndexVersion minimumIndexCompatibilityVersion) {
112-
boolean isSupportedVersion = indexMetadata.getCompatibilityVersion().onOrAfter(minimumIndexCompatibilityVersion);
113-
if (isSupportedVersion == false) {
114-
throw new IllegalStateException(
115-
"The index "
116-
+ indexMetadata.getIndex()
117-
+ " has current compatibility version ["
118-
+ indexMetadata.getCompatibilityVersion().toReleaseVersion()
119-
+ "] but the minimum compatible version is ["
120-
+ minimumIndexCompatibilityVersion.toReleaseVersion()
121-
+ "]. It should be re-indexed in Elasticsearch "
122-
+ (Version.CURRENT.major - 1)
123-
+ ".x before upgrading to "
124-
+ Build.current().version()
125-
+ "."
126-
);
116+
private static void checkSupportedVersion(
117+
IndexMetadata indexMetadata,
118+
IndexVersion minimumIndexCompatibilityVersion,
119+
IndexVersion minimumReadOnlyIndexCompatibilityVersion
120+
) {
121+
if (isFullySupportedVersion(indexMetadata, minimumIndexCompatibilityVersion)) {
122+
return;
123+
}
124+
if (isReadOnlySupportedVersion(indexMetadata, minimumIndexCompatibilityVersion, minimumReadOnlyIndexCompatibilityVersion)) {
125+
return;
126+
}
127+
throw new IllegalStateException(
128+
"The index "
129+
+ indexMetadata.getIndex()
130+
+ " has current compatibility version ["
131+
+ indexMetadata.getCompatibilityVersion().toReleaseVersion()
132+
+ "] but the minimum compatible version is ["
133+
+ minimumIndexCompatibilityVersion.toReleaseVersion()
134+
+ "]. It should be re-indexed in Elasticsearch "
135+
+ (Version.CURRENT.major - 1)
136+
+ ".x before upgrading to "
137+
+ Build.current().version()
138+
+ "."
139+
);
140+
}
141+
142+
private static boolean isFullySupportedVersion(IndexMetadata indexMetadata, IndexVersion minimumIndexCompatibilityVersion) {
143+
return indexMetadata.getCompatibilityVersion().onOrAfter(minimumIndexCompatibilityVersion);
144+
}
145+
146+
public static boolean isReadOnlySupportedVersion(
147+
IndexMetadata indexMetadata,
148+
IndexVersion minimumIndexCompatibilityVersion,
149+
IndexVersion minReadOnlyIndexCompatibilityVersion
150+
) {
151+
boolean isReadOnlySupportedVersion = indexMetadata.getCompatibilityVersion().onOrAfter(minReadOnlyIndexCompatibilityVersion);
152+
assert isFullySupportedVersion(indexMetadata, minimumIndexCompatibilityVersion) == false;
153+
154+
if (isReadOnlySupportedVersion && indexMetadata.isSearchableSnapshot()) {
155+
boolean isReadOnly = IndexMetadata.INDEX_BLOCKS_WRITE_SETTING.get(indexMetadata.getSettings());
156+
if (isReadOnly == false) {
157+
throw new IllegalStateException(
158+
"The index "
159+
+ indexMetadata.getIndex()
160+
+ " with current compatibility version ["
161+
+ indexMetadata.getCompatibilityVersion().toReleaseVersion()
162+
+ "] must be marked as read-only using the setting ["
163+
+ IndexMetadata.SETTING_BLOCKS_WRITE
164+
+ "] set to [true] before upgrading to "
165+
+ Build.current().version()
166+
+ "."
167+
);
168+
}
169+
return true;
127170
}
171+
return false;
128172
}
129173

130174
/**

server/src/main/java/org/elasticsearch/cluster/metadata/MetadataIndexStateService.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,7 @@ private ClusterState openIndices(final Index[] indices, final ClusterState curre
11201120
final Metadata.Builder metadata = Metadata.builder(currentState.metadata());
11211121
final ClusterBlocks.Builder blocks = ClusterBlocks.builder(currentState.blocks());
11221122
final IndexVersion minIndexCompatibilityVersion = currentState.getNodes().getMinSupportedIndexVersion();
1123+
final IndexVersion minReadOnlyIndexCompatibilityVersion = currentState.getNodes().getMinReadOnlySupportedIndexVersion();
11231124

11241125
for (IndexMetadata indexMetadata : indicesToOpen) {
11251126
final Index index = indexMetadata.getIndex();
@@ -1137,7 +1138,11 @@ private ClusterState openIndices(final Index[] indices, final ClusterState curre
11371138

11381139
// The index might be closed because we couldn't import it due to an old incompatible
11391140
// version, so we need to verify its compatibility.
1140-
newIndexMetadata = indexMetadataVerifier.verifyIndexMetadata(newIndexMetadata, minIndexCompatibilityVersion);
1141+
newIndexMetadata = indexMetadataVerifier.verifyIndexMetadata(
1142+
newIndexMetadata,
1143+
minIndexCompatibilityVersion,
1144+
minReadOnlyIndexCompatibilityVersion
1145+
);
11411146
try {
11421147
indicesService.verifyIndexMetadata(newIndexMetadata, newIndexMetadata);
11431148
} catch (Exception e) {

server/src/main/java/org/elasticsearch/gateway/GatewayMetaState.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,11 @@ static Metadata upgradeMetadata(Metadata metadata, IndexMetadataVerifier indexMe
295295
boolean changed = false;
296296
final Metadata.Builder upgradedMetadata = Metadata.builder(metadata);
297297
for (IndexMetadata indexMetadata : metadata) {
298-
IndexMetadata newMetadata = indexMetadataVerifier.verifyIndexMetadata(indexMetadata, IndexVersions.MINIMUM_COMPATIBLE);
298+
IndexMetadata newMetadata = indexMetadataVerifier.verifyIndexMetadata(
299+
indexMetadata,
300+
IndexVersions.MINIMUM_COMPATIBLE,
301+
IndexVersions.MINIMUM_READONLY_COMPATIBLE
302+
);
299303
changed |= indexMetadata != newMetadata;
300304
upgradedMetadata.put(newMetadata, false);
301305
}

server/src/main/java/org/elasticsearch/gateway/LocalAllocateDangledIndices.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ public ClusterState execute(ClusterState currentState) {
125125
currentState.routingTable()
126126
);
127127
IndexVersion minIndexCompatibilityVersion = currentState.nodes().getMinSupportedIndexVersion();
128+
IndexVersion minReadOnlyIndexCompatibilityVersion = currentState.nodes().getMinReadOnlySupportedIndexVersion();
128129
IndexVersion maxIndexCompatibilityVersion = currentState.nodes().getMaxDataNodeCompatibleIndexVersion();
129130
boolean importNeeded = false;
130131
StringBuilder sb = new StringBuilder();
@@ -176,7 +177,11 @@ public ClusterState execute(ClusterState currentState) {
176177
try {
177178
// The dangled index might be from an older version, we need to make sure it's compatible
178179
// with the current version.
179-
newIndexMetadata = indexMetadataVerifier.verifyIndexMetadata(indexMetadata, minIndexCompatibilityVersion);
180+
newIndexMetadata = indexMetadataVerifier.verifyIndexMetadata(
181+
indexMetadata,
182+
minIndexCompatibilityVersion,
183+
minReadOnlyIndexCompatibilityVersion
184+
);
180185
newIndexMetadata = IndexMetadata.builder(newIndexMetadata)
181186
.settings(
182187
Settings.builder()

server/src/main/java/org/elasticsearch/snapshots/RestoreService.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,7 @@ public ClusterState execute(ClusterState currentState) {
13481348
final Map<ShardId, ShardRestoreStatus> shards = new HashMap<>();
13491349

13501350
final IndexVersion minIndexCompatibilityVersion = currentState.getNodes().getMinSupportedIndexVersion();
1351+
final IndexVersion minReadOnlyIndexCompatibilityVersion = currentState.getNodes().getMinReadOnlySupportedIndexVersion();
13511352
final String localNodeId = clusterService.state().nodes().getLocalNodeId();
13521353
for (Map.Entry<String, IndexId> indexEntry : indicesToRestore.entrySet()) {
13531354
final IndexId index = indexEntry.getValue();
@@ -1360,12 +1361,16 @@ public ClusterState execute(ClusterState currentState) {
13601361
request.indexSettings(),
13611362
request.ignoreIndexSettings()
13621363
);
1363-
if (snapshotIndexMetadata.getCompatibilityVersion().before(minIndexCompatibilityVersion)) {
1364+
if (snapshotIndexMetadata.getCompatibilityVersion().isLegacyIndexVersion()) {
13641365
// adapt index metadata so that it can be understood by current version
13651366
snapshotIndexMetadata = convertLegacyIndex(snapshotIndexMetadata, currentState, indicesService);
13661367
}
13671368
try {
1368-
snapshotIndexMetadata = indexMetadataVerifier.verifyIndexMetadata(snapshotIndexMetadata, minIndexCompatibilityVersion);
1369+
snapshotIndexMetadata = indexMetadataVerifier.verifyIndexMetadata(
1370+
snapshotIndexMetadata,
1371+
minIndexCompatibilityVersion,
1372+
minReadOnlyIndexCompatibilityVersion
1373+
);
13691374
} catch (Exception ex) {
13701375
throw new SnapshotRestoreException(snapshot, "cannot restore index [" + index + "] because it cannot be upgraded", ex);
13711376
}

0 commit comments

Comments
 (0)