-
Notifications
You must be signed in to change notification settings - Fork 24.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Recover broken IndexMetaData as closed #17187
Conversation
ObjectHashSet<String> nodesIds = new ObjectHashSet<>(clusterService.state().nodes().masterNodes().keys()); | ||
logger.trace("performing state recovery from {}", nodesIds); | ||
TransportNodesListGatewayMetaState.NodesGatewayMetaState nodesState = listGatewayMetaState.list(nodesIds.toArray(String.class), null).actionGet(); | ||
String[] nodesIds = new ObjectHashSet<>(clusterService.state().nodes().masterNodes().keys()).toArray(String.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we are not going to have duplicates in map keys? Why not directly clusterService.state().nodes().masterNodes().keys().toArray()
?
IndexService service = null; | ||
try { | ||
// this will also fail if some plugin fails etc. which is nice since we can verify that early | ||
service = createIndexService(nodeServicesProvider, metaData, Collections.emptyList()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we merge mappings as well here to check if they are consistent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure what you mean by that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I could be wrong (not that familiar with the code in that area) but I think that in-memory data structures for mappings are not created by the createIndex
method. These are merged later (see e.g. MetaDataCreateIndexService:325). We could check here as well that all is good on the mapping level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we actually get a full fledged mapping in the constructor - MetaDataCreateIndexService
is different since it's done before we actually create the index so it has to process the default mapping. I think we are ok here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, MetaDataCreateIndexService
was a bad example. Still, the method MapperService.merge
which does mapping validation is (AFAICS) not called by the createIndex
method. This means that verifyIndexMetadata
does not run the mapping checks in MapperService.merge
. We check these however when we run MetaDataIndexUpgradeService.checkMappingsCompatibility
which is called by MetaDataIndexUpgradeService.upgradeIndexMetaData
when we start a node.
Left some comments but I really like the idea here 😄 . My main concern is how to make sure that |
agreed I think we can - lets do a followup
it's hard to assert to be honest but form architecture perspective I made all the global impacting things listeners that we do not pass in on the verify method so I think we are ok? |
@ywelsch I looked into your concern and refactored the solution such taht we are creating private cache instances for the verification IndexService. This should prevent any modifications. All other datastructures are immutable. |
In 5.0 we don't allow index settings to be specified on the node level ie. in yaml files or via commandline argument. This can cause problems during upgrade if this was used extensively. For instance if analyzers where specified on a node level this might cause the index to be closed when imported (see elastic#17187). In such a case all indices relying on this must be updated via `PUT /${index}/_settings`. Yet, this API has slightly different semantics since it overrides existing settings. To make this less painful this change adds a `preserve_existing` parameter on that API to ensure we have the same semantics as if the setting was applied on the node level. This change also adds a better error message and a change to the migration guide to ensure upgrades are smooth if index settings are specified on the node level. If a index setting is detected this change fails the node startup and prints a message like this: ``` ************************************************************************************* Found index level settings on node level configuration. Since elasticsearch 5.x index level settings can NOT be set on the nodes configuration like the elasticsearch.yaml, in system properties or command line arguments.In order to upgrade all indices the settings must be updated via the /${index}/_settings API. Unless all settings are dynamic all indices must be closed in order to apply the upgradeIndices created in the future should use index templates to set default values. Please ensure all required values are updated on all indices by executing: curl -XPUT 'http://localhost:9200/_all/_settings?preserve_existing=true' -d '{ "index.number_of_shards" : "1", "index.query.default_field" : "main_field", "index.translog.durability" : "async", "index.ttl.disable_purge" : "true" }' ************************************************************************************* ```
final Index index = indexMetaData.getIndex(); | ||
final Predicate<String> indexNameMatcher = (indexExpression) -> indexNameExpressionResolver.matchesIndex(index.getName(), indexExpression, clusterService.state()); | ||
final IndexSettings idxSettings = new IndexSettings(indexMetaData, this.settings, indexNameMatcher, indexScopeSetting); | ||
logger.debug("creating Index [{}], shards [{}]/[{}{}]", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we pass a reason to this method and mention it here? I always to scroll to find out whether this is a "true" index or just one that was created when importing/creating one.
I really like the change, but I'm afraid it's not enough, for example, when referring to an analyzer that used to be in the node settings (I tested it). The reason is that the mapper service doesn't instantiate anything until the merge method on it is called. This is imo something we should change (prepare everything in the constructor) but we don't have to do it in this PR. This patch works for me:
|
I was going to do that exact same thing since it would allow us to remove some places where we create an index for only that purpose. I can put this into the patch and add a test. Followups can clean up other places and we may move stuff into ctors. |
1c196ce
to
c27f330
Compare
@bleskes pushed an update with a new test |
final Index index = indexMetaData.getIndex(); | ||
final Predicate<String> indexNameMatcher = (indexExpression) -> indexNameExpressionResolver.matchesIndex(index.getName(), indexExpression, clusterService.state()); | ||
final IndexSettings idxSettings = new IndexSettings(indexMetaData, this.settings, indexNameMatcher, indexScopeSetting); | ||
logger.debug("creating Index [{}], shards [{}]/[{}{}]", | ||
logger.debug("creating Index [{}], shards [{}]/[{}{}] - reason [{}]", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thx :)
LGTM. Thanks |
Today if something is wrong with the IndexMetaData we detect it very late and most of the time if that happens we already allocated the index and get endless loops and full log files on data-nodes. This change tries to verify IndexService creattion during initial state recovery on the master and if the recovery fails the index is imported as `closed` and won't be allocated at all. Closes elastic#17187
c27f330
to
8127a06
Compare
In elastic#17187, we upgrade index state after upgrading index folder structure. As we don't have to write the upgraded state in the old index folder structure, we can cleanup how we write upgraded index state.
this verifyIndexMetadata spend too much time if a node has many indices and each index has many type. |
@hadeslion there is no such settings as this is important. If it takes so long you will also run into problems elsewhere as these things should be parsable. Can you give some numbers about how many indices and types you have? how long it takes etc? Is this during node startup or later on? |
@bleskes I have 100 indices. Some of them have 400-800 types. these indices just take 10-20s for each. The others have 1200-1600 types each, and it took 1-2minutes. |
With #17187, we verified IndexService creation during initial state recovery on the master and if the recovery failed the index was imported as closed, not allocating any shards. This was mainly done to prevent endless allocation loops and full log files on data-nodes when the indexmetadata contained broken settings / analyzers. Zen2 loads the cluster state eagerly, and this check currently runs on all nodes (not only the elected master), which can significantly slow down startup on data nodes. Furthermore, with replicated closed indices (#33888) on the horizon, importing the index as closed will no longer not allocate any shards. Fortunately, the original issue for endless allocation loops is no longer a problem due to #18467, where we limit the retries of failed allocations. The solution here is therefore to just undo #17187, as it's no longer necessary, and covered by #18467, which will solve the issue for Zen2 and replicated closed indices as well.
With #17187, we verified IndexService creation during initial state recovery on the master and if the recovery failed the index was imported as closed, not allocating any shards. This was mainly done to prevent endless allocation loops and full log files on data-nodes when the indexmetadata contained broken settings / analyzers. Zen2 loads the cluster state eagerly, and this check currently runs on all nodes (not only the elected master), which can significantly slow down startup on data nodes. Furthermore, with replicated closed indices (#33888) on the horizon, importing the index as closed will no longer not allocate any shards. Fortunately, the original issue for endless allocation loops is no longer a problem due to #18467, where we limit the retries of failed allocations. The solution here is therefore to just undo #17187, as it's no longer necessary, and covered by #18467, which will solve the issue for Zen2 and replicated closed indices as well.
With #17187, we verified IndexService creation during initial state recovery on the master and if the recovery failed the index was imported as closed, not allocating any shards. This was mainly done to prevent endless allocation loops and full log files on data-nodes when the indexmetadata contained broken settings / analyzers. Zen2 loads the cluster state eagerly, and this check currently runs on all nodes (not only the elected master), which can significantly slow down startup on data nodes. Furthermore, with replicated closed indices (#33888) on the horizon, importing the index as closed will no longer not allocate any shards. Fortunately, the original issue for endless allocation loops is no longer a problem due to #18467, where we limit the retries of failed allocations. The solution here is therefore to just undo #17187, as it's no longer necessary, and covered by #18467, which will solve the issue for Zen2 and replicated closed indices as well.
Today if something is wrong with the IndexMetaData we detect it very
late and most of the time if that happens we already allocated the index
and get endless loops and full log files on data-nodes. This change tries
to verify IndexService creattion during initial state recovery on the master
and if the recovery fails the index is imported as
closed
and won't be allocatedat all.
@bleskes @ywelsch @jasontedor may I have your feedback on the approach