Skip to content

Commit 09b8773

Browse files
authored
Introduce index.version.compatibility setting (#83264)
Introduces an index.version.compatibility setting (that defaults to index.version.created) that's used to determine whether the index can be handled by the current cluster. Allows older (archive) indices (which have older index.version.created) to be imported with a higher index.version.compatibility version, and thereby be accepted by the nodes in the cluster. In the future, I can envision the capability of indices to even have the ability to "upgrade" to a newer index.version.compatibility version (after they undergo a series of checks which makes them eligible for "upgrade"). Relates #81210
1 parent 33b3cca commit 09b8773

File tree

19 files changed

+398
-211
lines changed

19 files changed

+398
-211
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,22 +272,22 @@ public static void ensureIndexCompatibility(final Version nodeVersion, Metadata
272272
// we ensure that all indices in the cluster we join are compatible with us no matter if they are
273273
// closed or not we can't read mappings of these indices so we need to reject the join...
274274
for (IndexMetadata idxMetadata : metadata) {
275-
if (idxMetadata.getCreationVersion().after(nodeVersion)) {
275+
if (idxMetadata.getCompatibilityVersion().after(nodeVersion)) {
276276
throw new IllegalStateException(
277277
"index "
278278
+ idxMetadata.getIndex()
279279
+ " version not supported: "
280-
+ idxMetadata.getCreationVersion()
280+
+ idxMetadata.getCompatibilityVersion()
281281
+ " the node version is: "
282282
+ nodeVersion
283283
);
284284
}
285-
if (idxMetadata.getCreationVersion().before(supportedIndexVersion)) {
285+
if (idxMetadata.getCompatibilityVersion().before(supportedIndexVersion)) {
286286
throw new IllegalStateException(
287287
"index "
288288
+ idxMetadata.getIndex()
289289
+ " version not supported: "
290-
+ idxMetadata.getCreationVersion()
290+
+ idxMetadata.getCompatibilityVersion()
291291
+ " minimum compatible index version is: "
292292
+ supportedIndexVersion
293293
);

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

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,49 @@ public static APIBlock readFrom(StreamInput input) throws IOException {
336336
@Deprecated
337337
public static final String SETTING_VERSION_UPGRADED_STRING = "index.version.upgraded_string";
338338

339+
public static final String SETTING_VERSION_COMPATIBILITY = "index.version.compatibility";
340+
341+
/**
342+
* See {@link #getCompatibilityVersion()}
343+
*/
344+
public static final Setting<Version> SETTING_INDEX_VERSION_COMPATIBILITY = Setting.versionSetting(
345+
SETTING_VERSION_COMPATIBILITY,
346+
SETTING_INDEX_VERSION_CREATED, // fall back to index.version.created
347+
new Setting.Validator<>() {
348+
349+
@Override
350+
public void validate(final Version compatibilityVersion) {
351+
352+
}
353+
354+
@Override
355+
public void validate(final Version compatibilityVersion, final Map<Setting<?>, Object> settings) {
356+
Version createdVersion = (Version) settings.get(SETTING_INDEX_VERSION_CREATED);
357+
if (compatibilityVersion.before(createdVersion)) {
358+
throw new IllegalArgumentException(
359+
SETTING_VERSION_COMPATIBILITY
360+
+ " ["
361+
+ compatibilityVersion
362+
+ "] must be >= "
363+
+ SETTING_VERSION_CREATED
364+
+ " ["
365+
+ createdVersion
366+
+ "]"
367+
);
368+
}
369+
}
370+
371+
@Override
372+
public Iterator<Setting<?>> settings() {
373+
final List<Setting<?>> settings = List.of(SETTING_INDEX_VERSION_CREATED);
374+
return settings.iterator();
375+
}
376+
377+
},
378+
Property.IndexScope,
379+
Property.PrivateIndex
380+
);
381+
339382
/**
340383
* The user provided name for an index. This is the plain string provided by the user when the index was created.
341384
* It might still contain date math expressions etc. (added in 5.0)
@@ -484,6 +527,7 @@ public static APIBlock readFrom(StreamInput input) throws IOException {
484527
private final DiscoveryNodeFilters initialRecoveryFilters;
485528

486529
private final Version indexCreatedVersion;
530+
private final Version indexCompatibilityVersion;
487531

488532
private final ActiveShardCount waitForActiveShards;
489533
private final ImmutableOpenMap<String, RolloverInfo> rolloverInfos;
@@ -592,6 +636,7 @@ private IndexMetadata(
592636
this.autoExpandReplicas = autoExpandReplicas;
593637
this.isSearchableSnapshot = isSearchableSnapshot;
594638
this.isPartialSearchableSnapshot = isPartialSearchableSnapshot;
639+
this.indexCompatibilityVersion = SETTING_INDEX_VERSION_COMPATIBILITY.get(settings);
595640
assert numberOfShards * routingFactor == routingNumShards : routingNumShards + " must be a multiple of " + numberOfShards;
596641
}
597642

@@ -689,11 +734,23 @@ public long primaryTerm(int shardId) {
689734
/**
690735
* Return the {@link Version} on which this index has been created. This
691736
* information is typically useful for backward compatibility.
737+
* To check index compatibility (e.g. N-1 checks), use {@link #getCompatibilityVersion()} instead.
692738
*/
693739
public Version getCreationVersion() {
694740
return indexCreatedVersion;
695741
}
696742

743+
/**
744+
* Return the {@link Version} that this index provides compatibility for.
745+
* This is typically compared to the {@link Version#minimumIndexCompatibilityVersion()} to figure out whether the index can be handled
746+
* by the cluster.
747+
* By default, this is equal to the {@link #getCreationVersion()}, but can also be a newer version if the index has been imported as
748+
* a legacy index from an older snapshot, and its metadata has been converted to be handled by newer version nodes.
749+
*/
750+
public Version getCompatibilityVersion() {
751+
return indexCompatibilityVersion;
752+
}
753+
697754
public long getCreationDate() {
698755
return creationDate;
699756
}
@@ -1923,11 +1980,12 @@ public static IndexMetadata legacyFromXContent(XContentParser parser) throws IOE
19231980
} else if (token == XContentParser.Token.START_OBJECT) {
19241981
if ("settings".equals(currentFieldName)) {
19251982
Settings settings = Settings.fromXContent(parser);
1926-
if (SETTING_INDEX_VERSION_CREATED.get(settings).onOrAfter(Version.CURRENT.minimumIndexCompatibilityVersion())) {
1983+
if (SETTING_INDEX_VERSION_COMPATIBILITY.get(settings)
1984+
.onOrAfter(Version.CURRENT.minimumIndexCompatibilityVersion())) {
19271985
throw new IllegalStateException(
1928-
"this method should only be used to parse older index metadata versions "
1986+
"this method should only be used to parse older incompatible index metadata versions "
19291987
+ "but got "
1930-
+ SETTING_INDEX_VERSION_CREATED.get(settings)
1988+
+ SETTING_INDEX_VERSION_COMPATIBILITY.get(settings)
19311989
);
19321990
}
19331991
builder.settings(settings);
@@ -2008,7 +2066,7 @@ public static IndexMetadata legacyFromXContent(XContentParser parser) throws IOE
20082066
}
20092067

20102068
IndexMetadata indexMetadata = builder.build();
2011-
assert indexMetadata.getCreationVersion().before(Version.CURRENT.minimumIndexCompatibilityVersion());
2069+
assert indexMetadata.getCompatibilityVersion().before(Version.CURRENT.minimumIndexCompatibilityVersion());
20122070
return indexMetadata;
20132071
}
20142072

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,13 @@ public IndexMetadata verifyIndexMetadata(IndexMetadata indexMetadata, Version mi
9999
* previous major version.
100100
*/
101101
private void checkSupportedVersion(IndexMetadata indexMetadata, Version minimumIndexCompatibilityVersion) {
102-
boolean isSupportedVersion = indexMetadata.getCreationVersion().onOrAfter(minimumIndexCompatibilityVersion);
102+
boolean isSupportedVersion = indexMetadata.getCompatibilityVersion().onOrAfter(minimumIndexCompatibilityVersion);
103103
if (isSupportedVersion == false) {
104104
throw new IllegalStateException(
105105
"The index "
106106
+ indexMetadata.getIndex()
107-
+ " was created with version ["
108-
+ indexMetadata.getCreationVersion()
107+
+ " has current compatibility version ["
108+
+ indexMetadata.getCompatibilityVersion()
109109
+ "] but the minimum compatible version is ["
110110
+ minimumIndexCompatibilityVersion
111111
+ "]. It should be re-indexed in Elasticsearch "

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1663,7 +1663,7 @@ public Metadata build(boolean builtIndicesLookupEagerly) {
16631663
}
16641664
}
16651665
indexMetadata.getAliases().keysIt().forEachRemaining(indicesAliases::add);
1666-
oldestIndexVersionId = Math.min(oldestIndexVersionId, indexMetadata.getCreationVersion().id);
1666+
oldestIndexVersionId = Math.min(oldestIndexVersionId, indexMetadata.getCompatibilityVersion().id);
16671667
}
16681668

16691669
final DataStreamMetadata dataStreamMetadata = (DataStreamMetadata) this.customs.get(DataStreamMetadata.TYPE);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1463,6 +1463,7 @@ static void prepareResizeIndexSettings(
14631463
}
14641464

14651465
indexSettingsBuilder.put(IndexMetadata.SETTING_VERSION_CREATED, sourceMetadata.getCreationVersion())
1466+
.put(IndexMetadata.SETTING_VERSION_COMPATIBILITY, sourceMetadata.getCompatibilityVersion())
14661467
.put(builder.build())
14671468
.put(IndexMetadata.SETTING_ROUTING_PARTITION_SIZE, sourceMetadata.getRoutingPartitionSize())
14681469
.put(IndexMetadata.INDEX_RESIZE_SOURCE_NAME.getKey(), resizeSourceIndex.getName())

server/src/main/java/org/elasticsearch/common/settings/IndexScopedSettings.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public final class IndexScopedSettings extends AbstractScopedSettings {
5353
MergeSchedulerConfig.MAX_MERGE_COUNT_SETTING,
5454
MergeSchedulerConfig.MAX_THREAD_COUNT_SETTING,
5555
IndexMetadata.SETTING_INDEX_VERSION_CREATED,
56+
IndexMetadata.SETTING_INDEX_VERSION_COMPATIBILITY,
5657
IndexMetadata.INDEX_ROUTING_EXCLUDE_GROUP_SETTING,
5758
IndexMetadata.INDEX_ROUTING_INCLUDE_GROUP_SETTING,
5859
IndexMetadata.INDEX_ROUTING_REQUIRE_GROUP_SETTING,

server/src/main/java/org/elasticsearch/common/settings/Setting.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,6 +1233,15 @@ public static Setting<Version> versionSetting(final String key, final Version de
12331233
return new Setting<>(key, s -> Integer.toString(defaultValue.id), s -> Version.fromId(Integer.parseInt(s)), properties);
12341234
}
12351235

1236+
public static Setting<Version> versionSetting(
1237+
final String key,
1238+
Setting<Version> fallbackSetting,
1239+
Validator<Version> validator,
1240+
Property... properties
1241+
) {
1242+
return new Setting<>(key, fallbackSetting, s -> Version.fromId(Integer.parseInt(s)), properties);
1243+
}
1244+
12361245
public static Setting<Float> floatSetting(String key, float defaultValue, Property... properties) {
12371246
return new Setting<>(key, (s) -> Float.toString(defaultValue), Float::parseFloat, properties);
12381247
}

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,24 +120,24 @@ public ClusterState execute(ClusterState currentState) {
120120
boolean importNeeded = false;
121121
StringBuilder sb = new StringBuilder();
122122
for (IndexMetadata indexMetadata : request.indices) {
123-
if (indexMetadata.getCreationVersion().before(minIndexCompatibilityVersion)) {
123+
if (indexMetadata.getCompatibilityVersion().before(minIndexCompatibilityVersion)) {
124124
logger.warn(
125-
"ignoring dangled index [{}] on node [{}] since it's created version [{}] is not supported by at "
126-
+ "least one node in the cluster minVersion [{}]",
125+
"ignoring dangled index [{}] on node [{}] since it's current compatibility version [{}] "
126+
+ "is not supported by at least one node in the cluster minVersion [{}]",
127127
indexMetadata.getIndex(),
128128
request.fromNode,
129-
indexMetadata.getCreationVersion(),
129+
indexMetadata.getCompatibilityVersion(),
130130
minIndexCompatibilityVersion
131131
);
132132
continue;
133133
}
134-
if (currentState.nodes().getMinNodeVersion().before(indexMetadata.getCreationVersion())) {
134+
if (currentState.nodes().getMinNodeVersion().before(indexMetadata.getCompatibilityVersion())) {
135135
logger.warn(
136-
"ignoring dangled index [{}] on node [{}]"
137-
+ " since its created version [{}] is later than the oldest versioned node in the cluster [{}]",
136+
"ignoring dangled index [{}] on node [{}] since its current compatibility version [{}] "
137+
+ "is later than the oldest versioned node in the cluster [{}]",
138138
indexMetadata.getIndex(),
139139
request.fromNode,
140-
indexMetadata.getCreationVersion(),
140+
indexMetadata.getCompatibilityVersion(),
141141
currentState.getNodes().getMasterNode().getVersion()
142142
);
143143
continue;

server/src/main/java/org/elasticsearch/index/IndexSettings.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -938,6 +938,16 @@ public synchronized boolean updateIndexMetadata(IndexMetadata indexMetadata) {
938938
if (version.equals(newIndexVersion) == false) {
939939
throw new IllegalArgumentException("version mismatch on settings update expected: " + version + " but was: " + newIndexVersion);
940940
}
941+
Version newCompatibilityVersion = IndexMetadata.SETTING_INDEX_VERSION_COMPATIBILITY.get(newSettings);
942+
Version compatibilityVersion = IndexMetadata.SETTING_INDEX_VERSION_COMPATIBILITY.get(settings);
943+
if (compatibilityVersion.equals(newCompatibilityVersion) == false) {
944+
throw new IllegalArgumentException(
945+
"compatibility version mismatch on settings update expected: "
946+
+ compatibilityVersion
947+
+ " but was: "
948+
+ newCompatibilityVersion
949+
);
950+
}
941951
final String newUUID = newSettings.get(IndexMetadata.SETTING_INDEX_UUID, IndexMetadata.INDEX_UUID_NA_VALUE);
942952
if (newUUID.equals(getUUID()) == false) {
943953
throw new IllegalArgumentException("uuid mismatch on settings update expected: " + getUUID() + " but was: " + newUUID);

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

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1285,9 +1285,7 @@ public ClusterState execute(ClusterState currentState) {
12851285

12861286
final ImmutableOpenMap.Builder<ShardId, ShardRestoreStatus> shardsBuilder = ImmutableOpenMap.builder();
12871287

1288-
final Version minIndexCompatibilityVersion = skipVersionChecks(repositoryMetadata)
1289-
? Version.fromString("1.0.0")
1290-
: currentState.getNodes().getMaxNodeVersion().minimumIndexCompatibilityVersion();
1288+
final Version minIndexCompatibilityVersion = currentState.getNodes().getMaxNodeVersion().minimumIndexCompatibilityVersion();
12911289
final String localNodeId = clusterService.state().nodes().getLocalNodeId();
12921290
for (Map.Entry<String, IndexId> indexEntry : indicesToRestore.entrySet()) {
12931291
final IndexId index = indexEntry.getValue();
@@ -1297,10 +1295,9 @@ public ClusterState execute(ClusterState currentState) {
12971295
request.indexSettings(),
12981296
request.ignoreIndexSettings()
12991297
);
1300-
if (snapshotIndexMetadata.getCreationVersion()
1301-
.before(currentState.getNodes().getMaxNodeVersion().minimumIndexCompatibilityVersion())) {
1298+
if (snapshotIndexMetadata.getCompatibilityVersion().before(minIndexCompatibilityVersion)) {
13021299
// adapt index metadata so that it can be understood by current version
1303-
snapshotIndexMetadata = convertLegacyIndex(snapshotIndexMetadata);
1300+
snapshotIndexMetadata = convertLegacyIndex(snapshotIndexMetadata, currentState);
13041301
}
13051302
try {
13061303
snapshotIndexMetadata = indexMetadataVerifier.verifyIndexMetadata(snapshotIndexMetadata, minIndexCompatibilityVersion);
@@ -1590,7 +1587,10 @@ public void clusterStateProcessed(ClusterState oldState, ClusterState newState)
15901587
}
15911588
}
15921589

1593-
private IndexMetadata convertLegacyIndex(IndexMetadata snapshotIndexMetadata) {
1590+
private IndexMetadata convertLegacyIndex(IndexMetadata snapshotIndexMetadata, ClusterState clusterState) {
1591+
if (snapshotIndexMetadata.getCreationVersion().before(Version.fromString("5.0.0"))) {
1592+
throw new IllegalArgumentException("can't restore an index created before version 5.0.0");
1593+
}
15941594
MappingMetadata mappingMetadata = snapshotIndexMetadata.mapping();
15951595
Map<String, Object> loadedMappingSource = mappingMetadata.rawSourceAsMap();
15961596

@@ -1613,7 +1613,17 @@ private IndexMetadata convertLegacyIndex(IndexMetadata snapshotIndexMetadata) {
16131613
Map<String, Object> newMapping = new LinkedHashMap<>();
16141614
newMapping.put(mappingMetadata.type(), newMappingSource);
16151615
// TODO: _routing? Perhaps we don't need to obey any routing here as stuff is read-only anyway and get API will be disabled
1616-
return IndexMetadata.builder(snapshotIndexMetadata).putMapping(new MappingMetadata(mappingMetadata.type(), newMapping)).build();
1616+
return IndexMetadata.builder(snapshotIndexMetadata)
1617+
.putMapping(new MappingMetadata(mappingMetadata.type(), newMapping))
1618+
.settings(
1619+
Settings.builder()
1620+
.put(snapshotIndexMetadata.getSettings())
1621+
.put(
1622+
IndexMetadata.SETTING_INDEX_VERSION_COMPATIBILITY.getKey(),
1623+
clusterState.getNodes().getSmallestNonClientNodeVersion()
1624+
)
1625+
)
1626+
.build();
16171627
}
16181628

16191629
private static IndexMetadata.Builder restoreToCreateNewIndex(IndexMetadata snapshotIndexMetadata, String renamedIndexName) {

0 commit comments

Comments
 (0)