Skip to content

Commit 8ecac81

Browse files
committed
Allow read-only compatible indices
Relates ES-10274
1 parent 15b6b1d commit 8ecac81

File tree

11 files changed

+290
-45
lines changed

11 files changed

+290
-45
lines changed

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

Lines changed: 31 additions & 11 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);
@@ -360,9 +366,15 @@ private static void blockForbiddenVersions(TransportVersion joiningTransportVers
360366
* will not be created with a newer version of elasticsearch as well as that all indices are newer or equal to the minimum index
361367
* compatibility version.
362368
* @see IndexVersions#MINIMUM_COMPATIBLE
369+
* @see IndexVersions#MINIMUM_READONLY_COMPATIBLE
363370
* @throws IllegalStateException if any index is incompatible with the given version
364371
*/
365-
public static void ensureIndexCompatibility(IndexVersion minSupportedVersion, IndexVersion maxSupportedVersion, Metadata metadata) {
372+
public static void ensureIndexCompatibility(
373+
IndexVersion minSupportedVersion,
374+
IndexVersion minReadOnlySupportedVersion,
375+
IndexVersion maxSupportedVersion,
376+
Metadata metadata
377+
) {
366378
// we ensure that all indices in the cluster we join are compatible with us no matter if they are
367379
// closed or not we can't read mappings of these indices so we need to reject the join...
368380
for (IndexMetadata idxMetadata : metadata) {
@@ -377,14 +389,17 @@ public static void ensureIndexCompatibility(IndexVersion minSupportedVersion, In
377389
);
378390
}
379391
if (idxMetadata.getCompatibilityVersion().before(minSupportedVersion)) {
380-
throw new IllegalStateException(
381-
"index "
382-
+ idxMetadata.getIndex()
383-
+ " version not supported: "
384-
+ idxMetadata.getCompatibilityVersion().toReleaseVersion()
385-
+ " minimum compatible index version is: "
386-
+ minSupportedVersion.toReleaseVersion()
387-
);
392+
boolean isReadOnlySupported = isReadOnlySupportedVersion(idxMetadata, minSupportedVersion, minReadOnlySupportedVersion);
393+
if (isReadOnlySupported == false) {
394+
throw new IllegalStateException(
395+
"index "
396+
+ idxMetadata.getIndex()
397+
+ " version not supported: "
398+
+ idxMetadata.getCompatibilityVersion().toReleaseVersion()
399+
+ " minimum compatible index version is: "
400+
+ minSupportedVersion.toReleaseVersion()
401+
);
402+
}
388403
}
389404
}
390405
}
@@ -477,7 +492,12 @@ public static Collection<BiConsumer<DiscoveryNode, ClusterState>> addBuiltInJoin
477492
final Collection<BiConsumer<DiscoveryNode, ClusterState>> validators = new ArrayList<>();
478493
validators.add((node, state) -> {
479494
ensureNodesCompatibility(node.getVersion(), state.getNodes());
480-
ensureIndexCompatibility(node.getMinIndexVersion(), node.getMaxIndexVersion(), state.getMetadata());
495+
ensureIndexCompatibility(
496+
node.getMinIndexVersion(),
497+
node.getMinReadOnlyIndexVersion(),
498+
node.getMaxIndexVersion(),
499+
state.getMetadata()
500+
);
481501
});
482502
validators.addAll(onJoinValidators);
483503
return Collections.unmodifiableCollection(validators);

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

Lines changed: 79 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,81 @@ 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+
/**
147+
* Returns {@code true} if the index version is compatible in read-only mode. As of today, only searchable snapshots and archive indices
148+
* in version N-2 with a write block are read-only compatible. This method throws an {@link IllegalStateException} if the index is
149+
* either a searchable snapshot or an archive index with a read-only compatible version but is missing the write block.
150+
*
151+
* @param indexMetadata the index metadata
152+
* @param minimumIndexCompatibilityVersion the min. index compatible version for reading and writing indices (used in assertion)
153+
* @param minReadOnlyIndexCompatibilityVersion the min. index compatible version for only reading indices
154+
*
155+
* @return {@code true} if the index version is compatible in read-only mode, {@code false} otherwise.
156+
* @throws IllegalStateException if the index is read-only compatible but has no write block in place.
157+
*/
158+
public static boolean isReadOnlySupportedVersion(
159+
IndexMetadata indexMetadata,
160+
IndexVersion minimumIndexCompatibilityVersion,
161+
IndexVersion minReadOnlyIndexCompatibilityVersion
162+
) {
163+
boolean isReadOnlySupportedVersion = indexMetadata.getCompatibilityVersion().onOrAfter(minReadOnlyIndexCompatibilityVersion);
164+
assert isFullySupportedVersion(indexMetadata, minimumIndexCompatibilityVersion) == false;
165+
166+
if (isReadOnlySupportedVersion
167+
&& (indexMetadata.isSearchableSnapshot() || indexMetadata.getCreationVersion().isLegacyIndexVersion())) {
168+
boolean isReadOnly = IndexMetadata.INDEX_BLOCKS_WRITE_SETTING.get(indexMetadata.getSettings());
169+
if (isReadOnly == false) {
170+
throw new IllegalStateException(
171+
"The index "
172+
+ indexMetadata.getIndex()
173+
+ " created in version ["
174+
+ indexMetadata.getCreationVersion()
175+
+ "] with current compatibility version ["
176+
+ indexMetadata.getCompatibilityVersion().toReleaseVersion()
177+
+ "] must be marked as read-only using the setting ["
178+
+ IndexMetadata.SETTING_BLOCKS_WRITE
179+
+ "] set to [true] before upgrading to "
180+
+ Build.current().version()
181+
+ '.'
182+
);
183+
}
184+
return true;
127185
}
186+
return false;
128187
}
129188

130189
/**

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
@@ -308,7 +308,11 @@ static Metadata upgradeMetadata(Metadata metadata, IndexMetadataVerifier indexMe
308308
boolean changed = false;
309309
final Metadata.Builder upgradedMetadata = Metadata.builder(metadata);
310310
for (IndexMetadata indexMetadata : metadata) {
311-
IndexMetadata newMetadata = indexMetadataVerifier.verifyIndexMetadata(indexMetadata, IndexVersions.MINIMUM_COMPATIBLE);
311+
IndexMetadata newMetadata = indexMetadataVerifier.verifyIndexMetadata(
312+
indexMetadata,
313+
IndexVersions.MINIMUM_COMPATIBLE,
314+
IndexVersions.MINIMUM_READONLY_COMPATIBLE
315+
);
312316
changed |= indexMetadata != newMetadata;
313317
upgradedMetadata.put(newMetadata, false);
314318
}

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/index/IndexVersion.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ public static IndexVersion current() {
124124
}
125125

126126
public boolean isLegacyIndexVersion() {
127-
return before(IndexVersions.MINIMUM_COMPATIBLE);
127+
return before(IndexVersions.MINIMUM_READONLY_COMPATIBLE);
128128
}
129129

130130
public static IndexVersion getMinimumCompatibleIndexVersion(int versionId) {

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

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

13171317
final IndexVersion minIndexCompatibilityVersion = currentState.getNodes().getMinSupportedIndexVersion();
1318+
final IndexVersion minReadOnlyIndexCompatibilityVersion = currentState.getNodes().getMinReadOnlySupportedIndexVersion();
13181319
final String localNodeId = clusterService.state().nodes().getLocalNodeId();
13191320
for (Map.Entry<String, IndexId> indexEntry : indicesToRestore.entrySet()) {
13201321
final IndexId index = indexEntry.getValue();
@@ -1327,12 +1328,16 @@ public ClusterState execute(ClusterState currentState) {
13271328
request.indexSettings(),
13281329
request.ignoreIndexSettings()
13291330
);
1330-
if (snapshotIndexMetadata.getCompatibilityVersion().before(minIndexCompatibilityVersion)) {
1331+
if (snapshotIndexMetadata.getCompatibilityVersion().isLegacyIndexVersion()) {
13311332
// adapt index metadata so that it can be understood by current version
13321333
snapshotIndexMetadata = convertLegacyIndex(snapshotIndexMetadata, currentState, indicesService);
13331334
}
13341335
try {
1335-
snapshotIndexMetadata = indexMetadataVerifier.verifyIndexMetadata(snapshotIndexMetadata, minIndexCompatibilityVersion);
1336+
snapshotIndexMetadata = indexMetadataVerifier.verifyIndexMetadata(
1337+
snapshotIndexMetadata,
1338+
minIndexCompatibilityVersion,
1339+
minReadOnlyIndexCompatibilityVersion
1340+
);
13361341
} catch (Exception ex) {
13371342
throw new SnapshotRestoreException(snapshot, "cannot restore index [" + index + "] because it cannot be upgraded", ex);
13381343
}

0 commit comments

Comments
 (0)