[7.x] Allow transactional metadata update with index creation (#55308)
This commit is contained in:
parent
643ecf68b5
commit
e89c5d6850
|
@ -91,6 +91,7 @@ import java.util.Map;
|
|||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
@ -301,8 +302,8 @@ public class MetadataCreateIndexService {
|
|||
* Handles the cluster state transition to a version that reflects the {@link CreateIndexClusterStateUpdateRequest}.
|
||||
* All the requested changes are firstly validated before mutating the {@link ClusterState}.
|
||||
*/
|
||||
public ClusterState applyCreateIndexRequest(ClusterState currentState, CreateIndexClusterStateUpdateRequest request,
|
||||
boolean silent) throws Exception {
|
||||
public ClusterState applyCreateIndexRequest(ClusterState currentState, CreateIndexClusterStateUpdateRequest request, boolean silent,
|
||||
BiConsumer<Metadata.Builder, IndexMetadata> metadataTransformer) throws Exception {
|
||||
logger.trace("executing IndexCreationTask for [{}] against cluster state version [{}]", request, currentState.version());
|
||||
|
||||
validate(request, currentState);
|
||||
|
@ -313,7 +314,7 @@ public class MetadataCreateIndexService {
|
|||
if (sourceMetadata != null) {
|
||||
// If source metadata was provided, it means we're recovering from an existing index,
|
||||
// in which case templates don't apply, so create the index from the source metadata
|
||||
return applyCreateIndexRequestWithExistingMetadata(currentState, request, silent, sourceMetadata);
|
||||
return applyCreateIndexRequestWithExistingMetadata(currentState, request, silent, sourceMetadata, metadataTransformer);
|
||||
} else {
|
||||
// Hidden indices apply templates slightly differently (ignoring wildcard '*'
|
||||
// templates), so we need to check to see if the request is creating a hidden index
|
||||
|
@ -328,18 +329,23 @@ public class MetadataCreateIndexService {
|
|||
if (v2Template != null) {
|
||||
// If a v2 template was found, it takes precedence over all v1 templates, so create
|
||||
// the index using that template and the request's specified settings
|
||||
return applyCreateIndexRequestWithV2Template(currentState, request, silent, v2Template);
|
||||
return applyCreateIndexRequestWithV2Template(currentState, request, silent, v2Template, metadataTransformer);
|
||||
} else {
|
||||
// A v2 template wasn't found, check the v1 templates, in the event no templates are
|
||||
// found creation still works using the request's specified index settings
|
||||
final List<IndexTemplateMetadata> v1Templates = MetadataIndexTemplateService.findV1Templates(currentState.metadata(),
|
||||
request.index(), isHiddenFromRequest);
|
||||
|
||||
return applyCreateIndexRequestWithV1Templates(currentState, request, silent, v1Templates);
|
||||
return applyCreateIndexRequestWithV1Templates(currentState, request, silent, v1Templates, metadataTransformer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ClusterState applyCreateIndexRequest(ClusterState currentState, CreateIndexClusterStateUpdateRequest request,
|
||||
boolean silent) throws Exception {
|
||||
return applyCreateIndexRequest(currentState, request, silent, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the state and a request as well as the metadata necessary to build a new index,
|
||||
* validate the configuration with an actual index service as return a new cluster state with
|
||||
|
@ -352,6 +358,8 @@ public class MetadataCreateIndexService {
|
|||
* @param mappings a map of mappings for the new index
|
||||
* @param aliasSupplier a function that takes the real {@link IndexService} and returns a list of {@link AliasMetadata} aliases
|
||||
* @param templatesApplied a list of the names of the templates applied, for logging
|
||||
* @param metadataTransformer if provided, a function that may alter cluster metadata in the same cluster state update that
|
||||
* creates the index
|
||||
* @return a new cluster state with the index added
|
||||
*/
|
||||
private ClusterState applyCreateIndexWithTemporaryService(final ClusterState currentState,
|
||||
|
@ -361,7 +369,9 @@ public class MetadataCreateIndexService {
|
|||
final IndexMetadata temporaryIndexMeta,
|
||||
final Map<String, Map<String, Object>> mappings,
|
||||
final Function<IndexService, List<AliasMetadata>> aliasSupplier,
|
||||
final List<String> templatesApplied) throws Exception {
|
||||
final List<String> templatesApplied,
|
||||
final BiConsumer<Metadata.Builder, IndexMetadata> metadataTransformer)
|
||||
throws Exception {
|
||||
// create the index here (on the master) to validate it can be created, as well as adding the mapping
|
||||
return indicesService.<ClusterState, Exception>withTempIndexService(temporaryIndexMeta, indexService -> {
|
||||
try {
|
||||
|
@ -389,7 +399,7 @@ public class MetadataCreateIndexService {
|
|||
|
||||
indexService.getIndexEventListener().beforeIndexAddedToCluster(indexMetadata.getIndex(),
|
||||
indexMetadata.getSettings());
|
||||
return clusterStateCreateIndex(currentState, request.blocks(), indexMetadata, allocationService::reroute);
|
||||
return clusterStateCreateIndex(currentState, request.blocks(), indexMetadata, allocationService::reroute, metadataTransformer);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -424,7 +434,9 @@ public class MetadataCreateIndexService {
|
|||
private ClusterState applyCreateIndexRequestWithV1Templates(final ClusterState currentState,
|
||||
final CreateIndexClusterStateUpdateRequest request,
|
||||
final boolean silent,
|
||||
final List<IndexTemplateMetadata> templates) throws Exception {
|
||||
final List<IndexTemplateMetadata> templates,
|
||||
final BiConsumer<Metadata.Builder, IndexMetadata> metadataTransformer)
|
||||
throws Exception {
|
||||
logger.info("applying create index request using v1 templates {}", templates);
|
||||
|
||||
final Map<String, Map<String, Object>> mappings = Collections.unmodifiableMap(parseMappings(request.mappings(),
|
||||
|
@ -452,13 +464,15 @@ public class MetadataCreateIndexService {
|
|||
// the context is only used for validation so it's fine to pass fake values for the
|
||||
// shard id and the current timestamp
|
||||
xContentRegistry, indexService.newQueryShardContext(0, null, () -> 0L, null)),
|
||||
templates.stream().map(IndexTemplateMetadata::getName).collect(toList()));
|
||||
templates.stream().map(IndexTemplateMetadata::getName).collect(toList()), metadataTransformer);
|
||||
}
|
||||
|
||||
private ClusterState applyCreateIndexRequestWithV2Template(final ClusterState currentState,
|
||||
final CreateIndexClusterStateUpdateRequest request,
|
||||
final boolean silent,
|
||||
final String templateName) throws Exception {
|
||||
final String templateName,
|
||||
final BiConsumer<Metadata.Builder, IndexMetadata> metadataTransformer)
|
||||
throws Exception {
|
||||
logger.info("applying create index request using v2 template [{}]", templateName);
|
||||
|
||||
final Map<String, Map<String, Object>> mappings = Collections.unmodifiableMap(parseMappings(request.mappings(),
|
||||
|
@ -480,13 +494,15 @@ public class MetadataCreateIndexService {
|
|||
// the context is only used for validation so it's fine to pass fake values for the
|
||||
// shard id and the current timestamp
|
||||
xContentRegistry, indexService.newQueryShardContext(0, null, () -> 0L, null)),
|
||||
Collections.singletonList(templateName));
|
||||
Collections.singletonList(templateName), metadataTransformer);
|
||||
}
|
||||
|
||||
private ClusterState applyCreateIndexRequestWithExistingMetadata(final ClusterState currentState,
|
||||
final CreateIndexClusterStateUpdateRequest request,
|
||||
final boolean silent,
|
||||
final IndexMetadata sourceMetadata) throws Exception {
|
||||
final IndexMetadata sourceMetadata,
|
||||
final BiConsumer<Metadata.Builder, IndexMetadata> metadataTransformer)
|
||||
throws Exception {
|
||||
logger.info("applying create index request using existing index [{}] metadata", sourceMetadata.getIndex().getName());
|
||||
|
||||
final Map<String, Map<String, Object>> mappings;
|
||||
|
@ -510,7 +526,7 @@ public class MetadataCreateIndexService {
|
|||
// the context is only used for validation so it's fine to pass fake values for the
|
||||
// shard id and the current timestamp
|
||||
indexService.newQueryShardContext(0, null, () -> 0L, null)),
|
||||
Collections.emptyList());
|
||||
org.elasticsearch.common.collect.List.of(), metadataTransformer);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -746,10 +762,14 @@ public class MetadataCreateIndexService {
|
|||
* table based on the live nodes.
|
||||
*/
|
||||
static ClusterState clusterStateCreateIndex(ClusterState currentState, Set<ClusterBlock> clusterBlocks, IndexMetadata indexMetadata,
|
||||
BiFunction<ClusterState, String, ClusterState> rerouteRoutingTable) {
|
||||
Metadata newMetadata = Metadata.builder(currentState.metadata())
|
||||
.put(indexMetadata, false)
|
||||
.build();
|
||||
BiFunction<ClusterState, String, ClusterState> rerouteRoutingTable,
|
||||
BiConsumer<Metadata.Builder, IndexMetadata> metadataTransformer) {
|
||||
Metadata.Builder builder = Metadata.builder(currentState.metadata())
|
||||
.put(indexMetadata, false);
|
||||
if (metadataTransformer != null) {
|
||||
metadataTransformer.accept(builder, indexMetadata);
|
||||
}
|
||||
Metadata newMetadata = builder.build();
|
||||
|
||||
String indexName = indexMetadata.getIndex().getName();
|
||||
ClusterBlocks.Builder blocks = createClusterBlocksBuilder(currentState, indexName, clusterBlocks);
|
||||
|
|
|
@ -82,6 +82,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -89,7 +90,6 @@ import java.util.stream.Stream;
|
|||
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.emptySet;
|
||||
import static java.util.Collections.singleton;
|
||||
import static java.util.Collections.singletonList;
|
||||
import static java.util.Collections.singletonMap;
|
||||
|
@ -807,7 +807,8 @@ public class MetadataCreateIndexServiceTests extends ESTestCase {
|
|||
|
||||
assertThat(
|
||||
expectThrows(IllegalStateException.class,
|
||||
() -> clusterStateCreateIndex(currentClusterState, emptySet(), newIndex, (state, reason) -> state)).getMessage(),
|
||||
() -> clusterStateCreateIndex(currentClusterState, org.elasticsearch.common.collect.Set.of(), newIndex,
|
||||
(state, reason) -> state, null)).getMessage(),
|
||||
startsWith("alias [alias1] has more than one write index [")
|
||||
);
|
||||
}
|
||||
|
@ -830,13 +831,44 @@ public class MetadataCreateIndexServiceTests extends ESTestCase {
|
|||
return clusterState;
|
||||
};
|
||||
|
||||
ClusterState updatedClusterState = clusterStateCreateIndex(currentClusterState, singleton(INDEX_READ_ONLY_BLOCK), newIndexMetadata,
|
||||
rerouteRoutingTable);
|
||||
ClusterState updatedClusterState = clusterStateCreateIndex(currentClusterState,
|
||||
org.elasticsearch.common.collect.Set.of(INDEX_READ_ONLY_BLOCK), newIndexMetadata, rerouteRoutingTable, null);
|
||||
assertThat(updatedClusterState.blocks().getIndexBlockWithId("test", INDEX_READ_ONLY_BLOCK.id()), is(INDEX_READ_ONLY_BLOCK));
|
||||
assertThat(updatedClusterState.routingTable().index("test"), is(notNullValue()));
|
||||
assertThat(allocationRerouted.get(), is(true));
|
||||
}
|
||||
|
||||
public void testClusterStateCreateIndexWithMetadataTransaction() {
|
||||
ClusterState currentClusterState = ClusterState.builder(ClusterState.EMPTY_STATE)
|
||||
.metadata(Metadata.builder()
|
||||
.put(IndexMetadata.builder("my-index")
|
||||
.settings(settings(Version.CURRENT).put(SETTING_READ_ONLY, true))
|
||||
.numberOfShards(1)
|
||||
.numberOfReplicas(0)))
|
||||
.build();
|
||||
|
||||
IndexMetadata newIndexMetadata = IndexMetadata.builder("test")
|
||||
.settings(settings(Version.CURRENT).put(SETTING_READ_ONLY, true))
|
||||
.numberOfShards(1)
|
||||
.numberOfReplicas(0)
|
||||
.putAlias(AliasMetadata.builder("alias1").writeIndex(true).build())
|
||||
.build();
|
||||
|
||||
// adds alias from new index to existing index
|
||||
BiConsumer<Metadata.Builder, IndexMetadata> metadataTransformer = (builder, indexMetadata) -> {
|
||||
AliasMetadata newAlias = indexMetadata.getAliases().iterator().next().value;
|
||||
IndexMetadata myIndex = builder.get("my-index");
|
||||
builder.put(IndexMetadata.builder(myIndex).putAlias(AliasMetadata.builder(newAlias.getAlias()).build()));
|
||||
};
|
||||
|
||||
ClusterState updatedClusterState = clusterStateCreateIndex(currentClusterState, org.elasticsearch.common.collect.Set.of(
|
||||
INDEX_READ_ONLY_BLOCK), newIndexMetadata, (clusterState, y) -> clusterState, metadataTransformer);
|
||||
assertTrue(updatedClusterState.metadata().findAllAliases(new String[]{"my-index"}).containsKey("my-index"));
|
||||
assertNotNull(updatedClusterState.metadata().findAllAliases(new String[]{"my-index"}).get("my-index"));
|
||||
assertNotNull(updatedClusterState.metadata().findAllAliases(new String[]{"my-index"}).get("my-index").get(0).alias(),
|
||||
equalTo("alias1"));
|
||||
}
|
||||
|
||||
public void testParseMappingsWithTypedTemplateAndTypelessIndexMapping() throws Exception {
|
||||
IndexTemplateMetadata templateMetadata = addMatchingTemplate(builder -> {
|
||||
try {
|
||||
|
|
Loading…
Reference in New Issue