Description
Describe the bug
In the index templates documentation for Composable index templates, it states:
Settings and mappings that you specify directly in the create index request override any settings or mappings specified in an index template and its component templates.
This documentation is not accurate.
Specifically, overriding is not possible if the conflicting type can't be merged (e.g., between object and nested, field and object, etc.). Overriding only occurs on "leaf" components. (See ab65a57)
Worse, this mapping conflict prevents index creation, including system indices.
A user could accidentally add a wrong mapping and potentially prevent a node from even starting up: opensearch-project/observability#1872
Related component
Indexing
To Reproduce
- Create a component template with an object or nested mapping for a field ("error" in this case):
PUT _component_template/error_mapping_template
{
"template": {
"mappings": {
"properties": {
"error" : {
"type": "nested",
"properties": {
"message": {
"type": "text"
},
"status": {
"type": "integer"
}
}
}
}
}
}
}
- Create an index template using that composable template mapping:
PUT _index_template/match_all_template
{
"index_patterns": [
"*"
],
"template": {
"settings": {}
},
"priority": 10,
"composed_of": [
"error_mapping_template"
],
"version": 1
}
- Attempt to create an index with a conflicting field mapping (including a system index):
PUT /.plugins-foo-system-index
{
"mappings": {
"properties": {
"error": {
"type": "text"
}
}
}
}
- Receive the below error, and observe that the index did not successfully create.
{
"error": {
"root_cause": [
{
"type": "illegal_argument_exception",
"reason": "can't merge a non object mapping [error] with an object mapping"
}
],
"type": "illegal_argument_exception",
"reason": "can't merge a non object mapping [error] with an object mapping"
},
"status": 400
}
Expected behavior
The mapping specified in the create index API call overrides the template mapping, and the index is actually created.
Additional Details
Plugins
N/A. Reproduced on a local cluster using HEAD of 2.x branch, using ./gradlew clean run
.
However, this is a significant issue for any plugin that uses a system index. It's a critical issue for plugins that prevent a node from starting up if the system index is not created.
Host/Environment (please complete the following information):
Reproducable locally on macOS; occurred on a production cluster on AOS, OpenSearch version 2.15
Additional context
This is significant in that:
- Many plugins use system indices
- These index templates are in control of the user and yet conflict with system indices
- There's no easy way for a plugin, when creating a system index, to avoid this conflict or specify "don't use templates at all", essentially allowing a user to break several plugins without really even knowing it.
In this case, to work around the bug I had to create a (temporary) higher priority matching template with an empty object in the mapping
field to prevent the incompatible mapping.
Stack trace leading to the error:
java.lang.IllegalArgumentException: can't merge a non object mapping [error] with an object mapping
at org.opensearch.index.mapper.ObjectMapper.merge(ObjectMapper.java:767) ~[opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.index.mapper.ObjectMapper.doMerge(ObjectMapper.java:798) ~[opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.index.mapper.RootObjectMapper.doMerge(RootObjectMapper.java:364) ~[opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.index.mapper.ObjectMapper.merge(ObjectMapper.java:771) ~[opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.index.mapper.RootObjectMapper.merge(RootObjectMapper.java:359) ~[opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.index.mapper.Mapping.merge(Mapping.java:130) ~[opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.index.mapper.DocumentMapper.merge(DocumentMapper.java:310) ~[opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.index.mapper.MapperService.internalMerge(MapperService.java:521) ~[opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.index.mapper.MapperService.internalMerge(MapperService.java:483) ~[opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.index.mapper.MapperService.merge(MapperService.java:447) ~[opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.cluster.metadata.MetadataCreateIndexService.updateIndexMappingsAndBuildSortOrder(MetadataCreateIndexService.java:1441) ~[opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.cluster.metadata.MetadataCreateIndexService.lambda$applyCreateIndexWithTemporaryService$3(MetadataCreateIndexService.java:523) [opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.indices.IndicesService.withTempIndexService(IndicesService.java:982) [opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.cluster.metadata.MetadataCreateIndexService.applyCreateIndexWithTemporaryService(MetadataCreateIndexService.java:514) [opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.cluster.metadata.MetadataCreateIndexService.applyCreateIndexRequestWithV2Template(MetadataCreateIndexService.java:783) [opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.cluster.metadata.MetadataCreateIndexService.applyCreateIndexRequest(MetadataCreateIndexService.java:457) [opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.cluster.metadata.MetadataCreateIndexService.applyCreateIndexRequest(MetadataCreateIndexService.java:483) [opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.cluster.metadata.MetadataCreateIndexService$1.execute(MetadataCreateIndexService.java:389) [opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]
at org.opensearch.cluster.ClusterStateUpdateTask.execute(ClusterStateUpdateTask.java:67) [opensearch-2.18.0-SNAPSHOT.jar:2.18.0-SNAPSHOT]