Disallow "enabled" attribute change for types in mapping update (#33566)#33933
Disallow "enabled" attribute change for types in mapping update (#33566)#33933cbuescher merged 13 commits intoelastic:masterfrom cbismuth:33566_disallow_type_enabled_mapping_update
Conversation
This commit adds a check for "enabled" attribute change for types when a RestPutMappingAction is received. A MappingException is thrown when such a change is detected. Change are prevented in both ways: "false -> true" and "true -> false". Tests are not part of this commit, they will be developed after design validation.
|
Pinging @elastic/es-search-aggs |
|
|
||
| public MapperException(String msg, Object... args) { | ||
| super(msg, args); | ||
| } |
There was a problem hiding this comment.
nit: not sure we need another constructor here, can you just insert the args into the message String in the call?
There was a problem hiding this comment.
Sure.
Is LoggerMessageFormat#format preferred over String#format or maybe just string concatenation?
|
|
||
| for (Mapper mergeWithMapper : mergeWith) { | ||
| Mapper mergeIntoMapper = mappers.get(mergeWithMapper.simpleName()); | ||
| throwExceptionIfEnabledAttributeIsUpdatedForType(mergeWith, mergeWithMapper, mergeIntoMapper); |
There was a problem hiding this comment.
nit: ImNotABigFanOfOverlyLongMethodNamesButWhatTheHeckIfYouReallyLikeIt ;-)
There was a problem hiding this comment.
I'll shorten it, I'm not a big fan neither.
As I wasn't sure of the suggested design I've chosen (very) (very) explicit name to make myself understood.
| final ObjectMapper mergeWithObjectMapper = (ObjectMapper) mergeWithMapper; | ||
|
|
||
| if (mergeIntoObjectMapper.isEnabled() != mergeWithObjectMapper.isEnabled()) { | ||
| if (mergeIntoObjectMapper.nestedTypeFilter() instanceof TermQuery) { |
There was a problem hiding this comment.
I'm not quite sure I understand what the enable setting in mappings that are merged have to do with nested queries, especially with TermQuery. Maybe I'm missing sth., could you explain?
There was a problem hiding this comment.
I dug into this with a debugger and the _type attribute happened to be wrapped into a TermQuery.
I've been a (tiny) bit paranoiac as I wanted to be sure to not make other things break.
I'll have another debugging session to see how things work when updating mapping with "_source": { "enabled": false }. I'll let you know.
There was a problem hiding this comment.
Well, it looks like updating the _source attribute in a mapping is not allowed.
command
curl -XPUT "${ES_CLUSTER}/index1/_mapping/type1" -H "Content-Type: application/json" --data '{
"properties": {
"foo": {
"source": {
"enabled": false
},
"properties": {
"bar": {
"type": "text"
}
}
}
}
}'
output
{
"error": {
"root_cause": [
{
"type": "mapper_parsing_exception",
"reason": "Mapping definition for [foo] has unsupported parameters: [source : {enabled=false}]"
}
],
"type": "mapper_parsing_exception",
"reason": "Mapping definition for [foo] has unsupported parameters: [source : {enabled=false}]"
},
"status": 400
}
I'll now have a look if removing this conditional block doesn't conflict with other enabled-like metadata attributes.
There was a problem hiding this comment.
No gradlew check -p server test failure after removing paranoiac conditional block 👍
|
Thank you @cbuescher 👍 I'll update this PR with your comments and add test cases. |
|
Hi @cbuescher, I've added test cases to ensure added behavior is covered. |
|
|
||
| if (mergeIntoObjectMapper.isEnabled() != mergeWithObjectMapper.isEnabled()) { | ||
| final String path = mergeWith.fullPath() + "." + mergeWithObjectMapper.simpleName() + ".enabled"; | ||
|
|
There was a problem hiding this comment.
Yes, these two lines are related, no need to add a blank line, I'll remove it.
server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperMergeTests.java
Show resolved
Hide resolved
server/src/test/java/org/elasticsearch/index/mapper/ObjectMapperMergeTests.java
Show resolved
Hide resolved
| private static Explicit<Boolean> dateDetection = new Explicit<>(false, false); | ||
| private static Explicit<Boolean> numericDetection = new Explicit<>(false, false); | ||
| private static Explicit<FormatDateTimeFormatter[]> dynamicDateTimeFormatters = new Explicit<>(new FormatDateTimeFormatter[0], false); | ||
| private static Explicit<DynamicTemplate[]> dynamicTemplates = new Explicit<>(new DynamicTemplate[0], false); |
There was a problem hiding this comment.
I was a bit suprised by these until I saw them being used in createRootObjectMapper(), maybe you could just instantiate them there locally, even if that means a few more instances. I think it makes the test more readable although this way is certainly "better" in terms of object creation.
There was a problem hiding this comment.
Yes, you're right. It would be a better balance between performances and readability. I'll do it, thank you.
|
Thank you @cbuescher for your review. I've added comments to your comments and I'll update my PR very soon. |
|
PR updated with your comments, thank you @cbuescher. |
cbuescher
left a comment
There was a problem hiding this comment.
I left two more small comment, but generally this looks almost ready.
|
|
||
| private static TextFieldMapper createTextFieldMapper(String name) { | ||
| final TextFieldType fieldType = new TextFieldType(); | ||
| final Settings indexSettings = Settings.builder().put(SETTING_VERSION_CREATED, "1").build(); |
There was a problem hiding this comment.
nit: I guess it doesn't matter here too much, but normally one of the version constants from org.elasticsearch.Version is used in these cases, usually Version.CURRENT
There was a problem hiding this comment.
Yes, that's better as it's closer to production code.
| dynamicDateTimeFormatters, dynamicTemplates, | ||
| dateDetection, numericDetection, | ||
| Settings.EMPTY | ||
| ); |
There was a problem hiding this comment.
nit: I'm not too familiar with mapper creation (especially in tests) myself, but I saw that the RootObjectMapper.Builder already takes care of a lot of the defaults, so maybe this can be simplified to something like:
final Settings indexSettings = Settings.builder().put(SETTING_VERSION_CREATED, Version.CURRENT).build();
BuilderContext context = new BuilderContext(indexSettings, new ContentPath());
RootObjectMapper rootObjectMapper = new RootObjectMapper.Builder(fieldName).enabled(enabled).build(context);
mappers.values().stream().forEach(rootObjectMapper::putMapper);
return rootObjectMapper;
Or maybe even the dummy settings could be a test-wide constant.
I don't think this is necesarry but removes a bit of boilerplate code (which I admit testing the mappers seems to require quite a bit).
There was a problem hiding this comment.
Yes, it's far more better, builders are far more expressive than lengthy constructors.
|
Thank you @cbuescher for your attention to details, I know it can be very time consuming. PR is up-to-date with your comments 👍 |
cbuescher
left a comment
There was a problem hiding this comment.
@cbismuth thanks, this looks good to me. I will kick of final CI test runs now, hope they pass in one go 🤞
@elasticmachine test this please
|
@elasticmachine test this please |
|
Great! Thanks @cbuescher! |
That's a lot of failures! I've downloaded the plain text output log and I'll replay failed tests on my laptop on Monday morning. Anyway, number of them seem to have the same stash dump, here is a sample below. |
|
@cbismuth That's from BWC issues that can happen when another change is merged to 6.x. Those should go away if you merge master into your branch. |
|
Thank you @cbuescher 👍 Would you like me to test it on a 6.5 branch and let you know? |
|
@cbismuth thanks, I think it should be a straigtforward backport, the question is more if this is breaking users applications even though the feature never worked as intended. But throwing an error is different than silently ignoring. It could be considered a bugfix though, in that case if would justify the backport. But I'll see what others think. |
|
It is indeed a good question 😉 |
|
@cbismuth I got some feedback that it would probably better not to backport the new strict checking since it would be considered breaking. However, it would be nice to change the PR slightly so that instead of throwing and error, a deprecation warning is emitted warning users that their request was silently ignored and we will throw an error here in the next major version. I will open a separate issue for this, it would be great if you would like to adapt this PR slightly to address it, but its also no problem if you don't like to do that. There a only a few thing that need changing for this I think, I can give you directions if you like to work on this as well. |
|
@cbismuth I have to correct myself, the general opinion shifted to backporting this change to 6.5 and add it to the breaking changes log. I will do this with the backport. |
This commit adds a check for "enabled" attribute change for types when a RestPutMappingAction is received. A MappingException is thrown when such a change is detected. Change are prevented in both ways: "false -> true" and "true -> false". Closes #33566
|
That's fine, thank you @cbuescher. |
This commit adds a check for "enabled" attribute change for types when a RestPutMappingAction is received. A MappingException is thrown when such a change is detected. Change are prevented in both ways: "false -> true" and "true -> false". Closes #33566
Removed the invalid tip that enabled can be updated for existing fields and clarified instead that it cannot. Related to elastic#33566 and elastic#33933
In #33933 we disallowed changing the `enabled` parameter in object mappings. However, the fix didn't cover the root object mapper. This PR adjusts the change to also include the root mapper and clarifies the error message.
In #33933 we disallowed changing the `enabled` parameter in object mappings. However, the fix didn't cover the root object mapper. This PR adjusts the change to also include the root mapper and clarifies the error message.
This commit adds a check for
enabledattribute change for types when aRestPutMappingActionis received. AMappingExceptionis thrown when such a change is detected.Change are prevented in both ways:
falsetotrueandtruetofalse.Here is a sample cURL output:
Tests are not part of this commit, they will be developed after design validation.