MetaDataIndexUpgradeService.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. /*
  2. * Licensed to Elasticsearch under one or more contributor
  3. * license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright
  5. * ownership. Elasticsearch licenses this file to you under
  6. * the Apache License, Version 2.0 (the "License"); you may
  7. * not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. package org.elasticsearch.cluster.metadata;
  20. import org.apache.logging.log4j.LogManager;
  21. import org.apache.logging.log4j.Logger;
  22. import org.apache.logging.log4j.message.ParameterizedMessage;
  23. import org.apache.lucene.analysis.Analyzer;
  24. import org.apache.lucene.search.similarities.Similarity;
  25. import org.elasticsearch.Version;
  26. import org.elasticsearch.common.TriFunction;
  27. import org.elasticsearch.common.settings.IndexScopedSettings;
  28. import org.elasticsearch.common.settings.Settings;
  29. import org.elasticsearch.common.xcontent.NamedXContentRegistry;
  30. import org.elasticsearch.index.IndexSettings;
  31. import org.elasticsearch.index.analysis.AnalyzerScope;
  32. import org.elasticsearch.index.analysis.IndexAnalyzers;
  33. import org.elasticsearch.index.analysis.NamedAnalyzer;
  34. import org.elasticsearch.index.mapper.MapperService;
  35. import org.elasticsearch.index.similarity.SimilarityService;
  36. import org.elasticsearch.indices.mapper.MapperRegistry;
  37. import org.elasticsearch.script.ScriptService;
  38. import java.util.AbstractMap;
  39. import java.util.Collections;
  40. import java.util.Map;
  41. import java.util.Set;
  42. /**
  43. * This service is responsible for upgrading legacy index metadata to the current version
  44. * <p>
  45. * Every time an existing index is introduced into cluster this service should be used
  46. * to upgrade the existing index metadata to the latest version of the cluster. It typically
  47. * occurs during cluster upgrade, when dangling indices are imported into the cluster or indices
  48. * are restored from a repository.
  49. */
  50. public class MetaDataIndexUpgradeService {
  51. private static final Logger logger = LogManager.getLogger(MetaDataIndexUpgradeService.class);
  52. private final Settings settings;
  53. private final NamedXContentRegistry xContentRegistry;
  54. private final MapperRegistry mapperRegistry;
  55. private final IndexScopedSettings indexScopedSettings;
  56. public MetaDataIndexUpgradeService(Settings settings, NamedXContentRegistry xContentRegistry, MapperRegistry mapperRegistry,
  57. IndexScopedSettings indexScopedSettings) {
  58. this.settings = settings;
  59. this.xContentRegistry = xContentRegistry;
  60. this.mapperRegistry = mapperRegistry;
  61. this.indexScopedSettings = indexScopedSettings;
  62. }
  63. /**
  64. * Checks that the index can be upgraded to the current version of the master node.
  65. *
  66. * <p>
  67. * If the index does not need upgrade it returns the index metadata unchanged, otherwise it returns a modified index metadata. If index
  68. * cannot be updated the method throws an exception.
  69. */
  70. public IndexMetaData upgradeIndexMetaData(IndexMetaData indexMetaData, Version minimumIndexCompatibilityVersion) {
  71. // Throws an exception if there are too-old segments:
  72. if (isUpgraded(indexMetaData)) {
  73. /*
  74. * We still need to check for broken index settings since it might be that a user removed a plugin that registers a setting
  75. * needed by this index.
  76. */
  77. return archiveBrokenIndexSettings(indexMetaData);
  78. }
  79. checkSupportedVersion(indexMetaData, minimumIndexCompatibilityVersion);
  80. // we have to run this first otherwise in we try to create IndexSettings
  81. // with broken settings and fail in checkMappingsCompatibility
  82. final IndexMetaData newMetaData = archiveBrokenIndexSettings(indexMetaData);
  83. // only run the check with the upgraded settings!!
  84. checkMappingsCompatibility(newMetaData);
  85. return markAsUpgraded(newMetaData);
  86. }
  87. /**
  88. * Checks if the index was already opened by this version of Elasticsearch and doesn't require any additional checks.
  89. */
  90. boolean isUpgraded(IndexMetaData indexMetaData) {
  91. return indexMetaData.getUpgradedVersion().onOrAfter(Version.CURRENT);
  92. }
  93. /**
  94. * Elasticsearch does not support indices created before the previous major version. They must be reindexed using an earlier version
  95. * before they can be opened here.
  96. */
  97. private void checkSupportedVersion(IndexMetaData indexMetaData, Version minimumIndexCompatibilityVersion) {
  98. if (isSupportedVersion(indexMetaData, minimumIndexCompatibilityVersion) == false) {
  99. throw new IllegalStateException("The index " + indexMetaData.getIndex() + " was created with version ["
  100. + indexMetaData.getCreationVersion() + "] but the minimum compatible version is ["
  101. + minimumIndexCompatibilityVersion + "]. It should be re-indexed in Elasticsearch "
  102. + minimumIndexCompatibilityVersion.major + ".x before upgrading to " + Version.CURRENT + ".");
  103. }
  104. }
  105. /*
  106. * Returns true if this index can be supported by the current version of elasticsearch
  107. */
  108. private static boolean isSupportedVersion(IndexMetaData indexMetaData, Version minimumIndexCompatibilityVersion) {
  109. return indexMetaData.getCreationVersion().onOrAfter(minimumIndexCompatibilityVersion);
  110. }
  111. /**
  112. * Checks the mappings for compatibility with the current version
  113. */
  114. private void checkMappingsCompatibility(IndexMetaData indexMetaData) {
  115. try {
  116. // We cannot instantiate real analysis server or similarity service at this point because the node
  117. // might not have been started yet. However, we don't really need real analyzers or similarities at
  118. // this stage - so we can fake it using constant maps accepting every key.
  119. // This is ok because all used similarities and analyzers for this index were known before the upgrade.
  120. // Missing analyzers and similarities plugin will still trigger the appropriate error during the
  121. // actual upgrade.
  122. IndexSettings indexSettings = new IndexSettings(indexMetaData, this.settings);
  123. final Map<String, TriFunction<Settings, Version, ScriptService, Similarity>> similarityMap
  124. = new AbstractMap<String, TriFunction<Settings, Version, ScriptService, Similarity>>() {
  125. @Override
  126. public boolean containsKey(Object key) {
  127. return true;
  128. }
  129. @Override
  130. public TriFunction<Settings, Version, ScriptService, Similarity> get(Object key) {
  131. assert key instanceof String : "key must be a string but was: " + key.getClass();
  132. return SimilarityService.BUILT_IN.get(SimilarityService.DEFAULT_SIMILARITY);
  133. }
  134. // this entrySet impl isn't fully correct but necessary as SimilarityService will iterate
  135. // over all similarities
  136. @Override
  137. public Set<Entry<String, TriFunction<Settings, Version, ScriptService, Similarity>>> entrySet() {
  138. return Collections.emptySet();
  139. }
  140. };
  141. SimilarityService similarityService = new SimilarityService(indexSettings, null, similarityMap);
  142. final NamedAnalyzer fakeDefault = new NamedAnalyzer("default", AnalyzerScope.INDEX, new Analyzer() {
  143. @Override
  144. protected TokenStreamComponents createComponents(String fieldName) {
  145. throw new UnsupportedOperationException("shouldn't be here");
  146. }
  147. });
  148. final Map<String, NamedAnalyzer> analyzerMap = new AbstractMap<String, NamedAnalyzer>() {
  149. @Override
  150. public NamedAnalyzer get(Object key) {
  151. assert key instanceof String : "key must be a string but was: " + key.getClass();
  152. return new NamedAnalyzer((String)key, AnalyzerScope.INDEX, fakeDefault.analyzer());
  153. }
  154. // this entrySet impl isn't fully correct but necessary as IndexAnalyzers will iterate
  155. // over all analyzers to close them
  156. @Override
  157. public Set<Entry<String, NamedAnalyzer>> entrySet() {
  158. return Collections.emptySet();
  159. }
  160. };
  161. try (IndexAnalyzers fakeIndexAnalzyers =
  162. new IndexAnalyzers(analyzerMap, analyzerMap, analyzerMap)) {
  163. MapperService mapperService = new MapperService(indexSettings, fakeIndexAnalzyers, xContentRegistry, similarityService,
  164. mapperRegistry, () -> null);
  165. mapperService.merge(indexMetaData, MapperService.MergeReason.MAPPING_RECOVERY);
  166. }
  167. } catch (Exception ex) {
  168. // Wrap the inner exception so we have the index name in the exception message
  169. throw new IllegalStateException("unable to upgrade the mappings for the index [" + indexMetaData.getIndex() + "]", ex);
  170. }
  171. }
  172. /**
  173. * Marks index as upgraded so we don't have to test it again
  174. */
  175. private IndexMetaData markAsUpgraded(IndexMetaData indexMetaData) {
  176. Settings settings = Settings.builder().put(indexMetaData.getSettings())
  177. .put(IndexMetaData.SETTING_VERSION_UPGRADED, Version.CURRENT).build();
  178. return IndexMetaData.builder(indexMetaData).settings(settings).build();
  179. }
  180. IndexMetaData archiveBrokenIndexSettings(IndexMetaData indexMetaData) {
  181. final Settings settings = indexMetaData.getSettings();
  182. final Settings upgrade = indexScopedSettings.archiveUnknownOrInvalidSettings(
  183. settings,
  184. e -> logger.warn("{} ignoring unknown index setting: [{}] with value [{}]; archiving",
  185. indexMetaData.getIndex(), e.getKey(), e.getValue()),
  186. (e, ex) -> logger.warn(() -> new ParameterizedMessage("{} ignoring invalid index setting: [{}] with value [{}]; archiving",
  187. indexMetaData.getIndex(), e.getKey(), e.getValue()), ex));
  188. if (upgrade != settings) {
  189. return IndexMetaData.builder(indexMetaData).settings(upgrade).build();
  190. } else {
  191. return indexMetaData;
  192. }
  193. }
  194. }