Merge branch 'master' into keystore
This commit is contained in:
commit
cd6e3f4cea
|
@ -55,7 +55,7 @@ dependencies {
|
||||||
runtime 'org.apache.commons:commons-math3:3.2'
|
runtime 'org.apache.commons:commons-math3:3.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileJava.options.compilerArgs << "-Xlint:-cast,-deprecation,-rawtypes,-try,-unchecked"
|
compileJava.options.compilerArgs << "-Xlint:-cast,-deprecation,-rawtypes,-try,-unchecked,-processing"
|
||||||
// enable the JMH's BenchmarkProcessor to generate the final benchmark classes
|
// enable the JMH's BenchmarkProcessor to generate the final benchmark classes
|
||||||
// needs to be added separately otherwise Gradle will quote it and javac will fail
|
// needs to be added separately otherwise Gradle will quote it and javac will fail
|
||||||
compileJava.options.compilerArgs.addAll(["-processor", "org.openjdk.jmh.generators.BenchmarkProcessor"])
|
compileJava.options.compilerArgs.addAll(["-processor", "org.openjdk.jmh.generators.BenchmarkProcessor"])
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.gradle
|
||||||
import nebula.plugin.extraconfigurations.ProvidedBasePlugin
|
import nebula.plugin.extraconfigurations.ProvidedBasePlugin
|
||||||
import org.elasticsearch.gradle.precommit.PrecommitTasks
|
import org.elasticsearch.gradle.precommit.PrecommitTasks
|
||||||
import org.gradle.api.GradleException
|
import org.gradle.api.GradleException
|
||||||
|
import org.gradle.api.InvalidUserDataException
|
||||||
import org.gradle.api.JavaVersion
|
import org.gradle.api.JavaVersion
|
||||||
import org.gradle.api.Plugin
|
import org.gradle.api.Plugin
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
|
@ -54,6 +55,11 @@ class BuildPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void apply(Project project) {
|
void apply(Project project) {
|
||||||
|
if (project.pluginManager.hasPlugin('elasticsearch.standalone-rest-test')) {
|
||||||
|
throw new InvalidUserDataException('elasticsearch.standalone-test, '
|
||||||
|
+ 'elasticearch.standalone-rest-test, and elasticsearch.build '
|
||||||
|
+ 'are mutually exclusive')
|
||||||
|
}
|
||||||
project.pluginManager.apply('java')
|
project.pluginManager.apply('java')
|
||||||
project.pluginManager.apply('carrotsearch.randomized-testing')
|
project.pluginManager.apply('carrotsearch.randomized-testing')
|
||||||
// these plugins add lots of info to our jars
|
// these plugins add lots of info to our jars
|
||||||
|
|
|
@ -30,6 +30,7 @@ public class DocsTestPlugin extends RestTestPlugin {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Project project) {
|
public void apply(Project project) {
|
||||||
|
project.pluginManager.apply('elasticsearch.standalone-rest-test')
|
||||||
super.apply(project)
|
super.apply(project)
|
||||||
Map<String, String> defaultSubstitutions = [
|
Map<String, String> defaultSubstitutions = [
|
||||||
/* These match up with the asciidoc syntax for substitutions but
|
/* These match up with the asciidoc syntax for substitutions but
|
||||||
|
|
|
@ -72,10 +72,12 @@ class ClusterConfiguration {
|
||||||
boolean useMinimumMasterNodes = true
|
boolean useMinimumMasterNodes = true
|
||||||
|
|
||||||
@Input
|
@Input
|
||||||
String jvmArgs = "-Xms" + System.getProperty('tests.heap.size', '512m') +
|
String jvmArgs = "-ea" +
|
||||||
|
" " + "-Xms" + System.getProperty('tests.heap.size', '512m') +
|
||||||
" " + "-Xmx" + System.getProperty('tests.heap.size', '512m') +
|
" " + "-Xmx" + System.getProperty('tests.heap.size', '512m') +
|
||||||
" " + System.getProperty('tests.jvm.argline', '')
|
" " + System.getProperty('tests.jvm.argline', '')
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A closure to call which returns the unicast host to connect to for cluster formation.
|
* A closure to call which returns the unicast host to connect to for cluster formation.
|
||||||
*
|
*
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.gradle.api.tasks.Delete
|
||||||
import org.gradle.api.tasks.Exec
|
import org.gradle.api.tasks.Exec
|
||||||
|
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A helper for creating tasks to build a cluster that is used by a task, and tear down the cluster when the task is finished.
|
* A helper for creating tasks to build a cluster that is used by a task, and tear down the cluster when the task is finished.
|
||||||
|
@ -91,6 +92,8 @@ class ClusterFormationTasks {
|
||||||
configureBwcPluginDependency("${task.name}_elasticsearchBwcPlugins", project, entry.getValue(),
|
configureBwcPluginDependency("${task.name}_elasticsearchBwcPlugins", project, entry.getValue(),
|
||||||
project.configurations.elasticsearchBwcPlugins, config.bwcVersion)
|
project.configurations.elasticsearchBwcPlugins, config.bwcVersion)
|
||||||
}
|
}
|
||||||
|
project.configurations.elasticsearchBwcDistro.resolutionStrategy.cacheChangingModulesFor(0, TimeUnit.SECONDS)
|
||||||
|
project.configurations.elasticsearchBwcPlugins.resolutionStrategy.cacheChangingModulesFor(0, TimeUnit.SECONDS)
|
||||||
}
|
}
|
||||||
for (int i = 0; i < config.numNodes; i++) {
|
for (int i = 0; i < config.numNodes; i++) {
|
||||||
// we start N nodes and out of these N nodes there might be M bwc nodes.
|
// we start N nodes and out of these N nodes there might be M bwc nodes.
|
||||||
|
|
|
@ -18,15 +18,29 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.gradle.test
|
package org.elasticsearch.gradle.test
|
||||||
|
|
||||||
|
import org.elasticsearch.gradle.BuildPlugin
|
||||||
|
import org.gradle.api.InvalidUserDataException
|
||||||
import org.gradle.api.Plugin
|
import org.gradle.api.Plugin
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
|
|
||||||
/** A plugin to add rest integration tests. Used for qa projects. */
|
/**
|
||||||
|
* Adds support for starting an Elasticsearch cluster before running integration
|
||||||
|
* tests. Used in conjunction with {@link StandaloneRestTestPlugin} for qa
|
||||||
|
* projects and in conjunction with {@link BuildPlugin} for testing the rest
|
||||||
|
* client.
|
||||||
|
*/
|
||||||
public class RestTestPlugin implements Plugin<Project> {
|
public class RestTestPlugin implements Plugin<Project> {
|
||||||
|
List REQUIRED_PLUGINS = [
|
||||||
|
'elasticsearch.build',
|
||||||
|
'elasticsearch.standalone-rest-test']
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Project project) {
|
public void apply(Project project) {
|
||||||
project.pluginManager.apply(StandaloneTestBasePlugin)
|
if (false == REQUIRED_PLUGINS.any {project.pluginManager.hasPlugin(it)}) {
|
||||||
|
throw new InvalidUserDataException('elasticsearch.rest-test '
|
||||||
|
+ 'requires either elasticsearch.build or '
|
||||||
|
+ 'elasticsearch.standalone-test')
|
||||||
|
}
|
||||||
|
|
||||||
RestIntegTestTask integTest = project.tasks.create('integTest', RestIntegTestTask.class)
|
RestIntegTestTask integTest = project.tasks.create('integTest', RestIntegTestTask.class)
|
||||||
integTest.cluster.distribution = 'zip' // rest tests should run with the real zip
|
integTest.cluster.distribution = 'zip' // rest tests should run with the real zip
|
||||||
|
|
|
@ -24,15 +24,26 @@ import com.carrotsearch.gradle.junit4.RandomizedTestingPlugin
|
||||||
import org.elasticsearch.gradle.BuildPlugin
|
import org.elasticsearch.gradle.BuildPlugin
|
||||||
import org.elasticsearch.gradle.VersionProperties
|
import org.elasticsearch.gradle.VersionProperties
|
||||||
import org.elasticsearch.gradle.precommit.PrecommitTasks
|
import org.elasticsearch.gradle.precommit.PrecommitTasks
|
||||||
|
import org.gradle.api.InvalidUserDataException
|
||||||
import org.gradle.api.Plugin
|
import org.gradle.api.Plugin
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.Task
|
||||||
import org.gradle.api.plugins.JavaBasePlugin
|
import org.gradle.api.plugins.JavaBasePlugin
|
||||||
|
|
||||||
/** Configures the build to have a rest integration test. */
|
/**
|
||||||
public class StandaloneTestBasePlugin implements Plugin<Project> {
|
* Configures the build to compile tests against Elasticsearch's test framework
|
||||||
|
* and run REST tests. Use BuildPlugin if you want to build main code as well
|
||||||
|
* as tests.
|
||||||
|
*/
|
||||||
|
public class StandaloneRestTestPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Project project) {
|
public void apply(Project project) {
|
||||||
|
if (project.pluginManager.hasPlugin('elasticsearch.build')) {
|
||||||
|
throw new InvalidUserDataException('elasticsearch.standalone-test, '
|
||||||
|
+ 'elasticsearch.standalone-test, and elasticsearch.build are '
|
||||||
|
+ 'mutually exclusive')
|
||||||
|
}
|
||||||
project.pluginManager.apply(JavaBasePlugin)
|
project.pluginManager.apply(JavaBasePlugin)
|
||||||
project.pluginManager.apply(RandomizedTestingPlugin)
|
project.pluginManager.apply(RandomizedTestingPlugin)
|
||||||
|
|
|
@ -25,12 +25,15 @@ import org.gradle.api.Plugin
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
import org.gradle.api.plugins.JavaBasePlugin
|
import org.gradle.api.plugins.JavaBasePlugin
|
||||||
|
|
||||||
/** A plugin to add tests only. Used for QA tests that run arbitrary unit tests. */
|
/**
|
||||||
|
* Configures the build to compile against Elasticsearch's test framework and
|
||||||
|
* run integration and unit tests. Use BuildPlugin if you want to build main
|
||||||
|
* code as well as tests. */
|
||||||
public class StandaloneTestPlugin implements Plugin<Project> {
|
public class StandaloneTestPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(Project project) {
|
public void apply(Project project) {
|
||||||
project.pluginManager.apply(StandaloneTestBasePlugin)
|
project.pluginManager.apply(StandaloneRestTestPlugin)
|
||||||
|
|
||||||
Map testOptions = [
|
Map testOptions = [
|
||||||
name: 'test',
|
name: 'test',
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
#
|
||||||
|
# Licensed to Elasticsearch under one or more contributor
|
||||||
|
# license agreements. See the NOTICE file distributed with
|
||||||
|
# this work for additional information regarding copyright
|
||||||
|
# ownership. Elasticsearch licenses this file to you under
|
||||||
|
# the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing,
|
||||||
|
# software distributed under the License is distributed on an
|
||||||
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
# KIND, either express or implied. See the License for the
|
||||||
|
# specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
|
||||||
|
implementation-class=org.elasticsearch.gradle.test.StandaloneRestTestPlugin
|
|
@ -10,9 +10,6 @@
|
||||||
<suppress files="org[/\\]elasticsearch[/\\]painless[/\\]antlr[/\\]PainlessLexer\.java" checks="." />
|
<suppress files="org[/\\]elasticsearch[/\\]painless[/\\]antlr[/\\]PainlessLexer\.java" checks="." />
|
||||||
<suppress files="org[/\\]elasticsearch[/\\]painless[/\\]antlr[/\\]PainlessParser(|BaseVisitor|Visitor)\.java" checks="." />
|
<suppress files="org[/\\]elasticsearch[/\\]painless[/\\]antlr[/\\]PainlessParser(|BaseVisitor|Visitor)\.java" checks="." />
|
||||||
|
|
||||||
<!-- ThrowableProxy is a forked copy from Log4j to hack around a bug; this can be removed when the hack is removed -->
|
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]apache[/\\]logging[/\\]log4j[/\\]core[/\\]impl[/\\]ThrowableProxy.java" checks="RegexpSinglelineJava" />
|
|
||||||
|
|
||||||
<!-- Hopefully temporary suppression of LineLength on files that don't pass it. We should remove these when we the
|
<!-- Hopefully temporary suppression of LineLength on files that don't pass it. We should remove these when we the
|
||||||
files start to pass. -->
|
files start to pass. -->
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]apache[/\\]lucene[/\\]queries[/\\]BlendedTermQuery.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]apache[/\\]lucene[/\\]queries[/\\]BlendedTermQuery.java" checks="LineLength" />
|
||||||
|
@ -352,12 +349,10 @@
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]get[/\\]ShardGetService.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]get[/\\]ShardGetService.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]DocumentFieldMappers.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]DocumentFieldMappers.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]DocumentMapper.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]DocumentMapper.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]DocumentMapperParser.java" checks="LineLength" />
|
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]DocumentParser.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]DocumentParser.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]FieldMapper.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]FieldMapper.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]FieldTypeLookup.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]FieldTypeLookup.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]MappedFieldType.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]MappedFieldType.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]Mapper.java" checks="LineLength" />
|
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]MapperService.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]MapperService.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]Mapping.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]Mapping.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]MetadataFieldMapper.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]mapper[/\\]MetadataFieldMapper.java" checks="LineLength" />
|
||||||
|
@ -391,7 +386,6 @@
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]query[/\\]QueryBuilders.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]query[/\\]QueryBuilders.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]query[/\\]QueryValidationException.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]query[/\\]QueryValidationException.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]query[/\\]support[/\\]InnerHitsQueryParserHelper.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]query[/\\]support[/\\]InnerHitsQueryParserHelper.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]query[/\\]support[/\\]QueryParsers.java" checks="LineLength" />
|
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]search[/\\]MatchQuery.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]search[/\\]MatchQuery.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]search[/\\]MultiMatchQuery.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]search[/\\]MultiMatchQuery.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]search[/\\]geo[/\\]IndexedGeoBoundingBoxQuery.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]index[/\\]search[/\\]geo[/\\]IndexedGeoBoundingBoxQuery.java" checks="LineLength" />
|
||||||
|
@ -1001,7 +995,6 @@
|
||||||
<suppress files="test[/\\]framework[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]test[/\\]engine[/\\]AssertingSearcher.java" checks="LineLength" />
|
<suppress files="test[/\\]framework[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]test[/\\]engine[/\\]AssertingSearcher.java" checks="LineLength" />
|
||||||
<suppress files="test[/\\]framework[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]test[/\\]engine[/\\]MockEngineSupport.java" checks="LineLength" />
|
<suppress files="test[/\\]framework[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]test[/\\]engine[/\\]MockEngineSupport.java" checks="LineLength" />
|
||||||
<suppress files="test[/\\]framework[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]test[/\\]hamcrest[/\\]ElasticsearchAssertions.java" checks="LineLength" />
|
<suppress files="test[/\\]framework[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]test[/\\]hamcrest[/\\]ElasticsearchAssertions.java" checks="LineLength" />
|
||||||
<suppress files="test[/\\]framework[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]test[/\\]junit[/\\]listeners[/\\]LoggingListener.java" checks="LineLength" />
|
|
||||||
<suppress files="test[/\\]framework[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]test[/\\]store[/\\]MockFSDirectoryService.java" checks="LineLength" />
|
<suppress files="test[/\\]framework[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]test[/\\]store[/\\]MockFSDirectoryService.java" checks="LineLength" />
|
||||||
<suppress files="test[/\\]framework[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]test[/\\]store[/\\]MockFSIndexStore.java" checks="LineLength" />
|
<suppress files="test[/\\]framework[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]test[/\\]store[/\\]MockFSIndexStore.java" checks="LineLength" />
|
||||||
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]cli[/\\]CliTool.java" checks="LineLength" />
|
<suppress files="core[/\\]src[/\\]main[/\\]java[/\\]org[/\\]elasticsearch[/\\]common[/\\]cli[/\\]CliTool.java" checks="LineLength" />
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
elasticsearch = 6.0.0-alpha1
|
elasticsearch = 6.0.0-alpha1
|
||||||
lucene = 6.4.0-snapshot-ec38570
|
lucene = 6.4.0-snapshot-084f7a0
|
||||||
|
|
||||||
# optional dependencies
|
# optional dependencies
|
||||||
spatial4j = 0.6
|
spatial4j = 0.6
|
||||||
|
@ -20,5 +20,6 @@ commonslogging = 1.1.3
|
||||||
commonscodec = 1.10
|
commonscodec = 1.10
|
||||||
hamcrest = 1.3
|
hamcrest = 1.3
|
||||||
securemock = 1.2
|
securemock = 1.2
|
||||||
|
mocksocket = 1.1
|
||||||
# benchmark dependencies
|
# benchmark dependencies
|
||||||
jmh = 1.17.3
|
jmh = 1.17.3
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
apply plugin: 'elasticsearch.build'
|
||||||
|
apply plugin: 'elasticsearch.rest-test'
|
||||||
|
|
||||||
|
group = 'org.elasticsearch.client'
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compile "org.elasticsearch:elasticsearch:${version}"
|
||||||
|
compile "org.elasticsearch.client:rest:${version}"
|
||||||
|
|
||||||
|
testCompile "org.elasticsearch.client:test:${version}"
|
||||||
|
testCompile "org.elasticsearch.test:framework:${version}"
|
||||||
|
testCompile "com.carrotsearch.randomizedtesting:randomizedtesting-runner:${versions.randomizedrunner}"
|
||||||
|
testCompile "junit:junit:${versions.junit}"
|
||||||
|
testCompile "org.hamcrest:hamcrest-all:${versions.hamcrest}"
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencyLicenses {
|
||||||
|
// Don't check licenses for dependency that are part of the elasticsearch project
|
||||||
|
// But any other dependency should have its license/notice/sha1
|
||||||
|
dependencies = project.configurations.runtime.fileCollection {
|
||||||
|
it.group.startsWith('org.elasticsearch') == false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.client;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
import org.apache.http.Header;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* High level REST client that wraps an instance of the low level {@link RestClient} and allows to build requests and read responses.
|
||||||
|
* The provided {@link RestClient} is externally built and closed.
|
||||||
|
*/
|
||||||
|
public final class RestHighLevelClient {
|
||||||
|
|
||||||
|
private static final Log logger = LogFactory.getLog(RestHighLevelClient.class);
|
||||||
|
|
||||||
|
private final RestClient client;
|
||||||
|
|
||||||
|
public RestHighLevelClient(RestClient client) {
|
||||||
|
this.client = Objects.requireNonNull(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean ping(Header... headers) {
|
||||||
|
try {
|
||||||
|
client.performRequest("HEAD", "/", headers);
|
||||||
|
return true;
|
||||||
|
} catch(IOException exception) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.client;
|
||||||
|
|
||||||
|
import org.elasticsearch.test.rest.ESRestTestCase;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Before;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public abstract class ESRestHighLevelClientTestCase extends ESRestTestCase {
|
||||||
|
|
||||||
|
private static RestHighLevelClient restHighLevelClient;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void initHighLevelClient() throws IOException {
|
||||||
|
super.initClient();
|
||||||
|
if (restHighLevelClient == null) {
|
||||||
|
restHighLevelClient = new RestHighLevelClient(client());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void cleanupClient() throws IOException {
|
||||||
|
restHighLevelClient = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static RestHighLevelClient highLevelClient() {
|
||||||
|
return restHighLevelClient;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,18 +17,11 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.elasticsearch.common.xcontent;
|
package org.elasticsearch.client;
|
||||||
|
|
||||||
import org.elasticsearch.common.ParseFieldMatcher;
|
public class MainActionIT extends ESRestHighLevelClientTestCase {
|
||||||
|
|
||||||
import java.io.IOException;
|
public void testPing() {
|
||||||
|
assertTrue(highLevelClient().ping());
|
||||||
/**
|
}
|
||||||
* Indicates that the class supports XContent deserialization.
|
|
||||||
*/
|
|
||||||
public interface FromXContentBuilder<T> {
|
|
||||||
/**
|
|
||||||
* Parses an object with the type T from parser
|
|
||||||
*/
|
|
||||||
T fromXContent(XContentParser parser, ParseFieldMatcher parseFieldMatcher) throws IOException;
|
|
||||||
}
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.client;
|
||||||
|
|
||||||
|
import org.apache.http.Header;
|
||||||
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.mockito.ArgumentMatcher;
|
||||||
|
import org.mockito.internal.matchers.ArrayEquals;
|
||||||
|
import org.mockito.internal.matchers.VarargMatcher;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
|
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
|
import static org.mockito.Matchers.argThat;
|
||||||
|
import static org.mockito.Matchers.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class RestHighLevelClientTests extends ESTestCase {
|
||||||
|
|
||||||
|
private RestClient restClient;
|
||||||
|
private RestHighLevelClient restHighLevelClient;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void initClient() throws IOException {
|
||||||
|
restClient = mock(RestClient.class);
|
||||||
|
restHighLevelClient = new RestHighLevelClient(restClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPing() throws IOException {
|
||||||
|
assertTrue(restHighLevelClient.ping());
|
||||||
|
verify(restClient).performRequest(eq("HEAD"), eq("/"), argThat(new HeadersVarargMatcher()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPingFailure() throws IOException {
|
||||||
|
when(restClient.performRequest(any(), any())).thenThrow(new IllegalStateException());
|
||||||
|
expectThrows(IllegalStateException.class, () -> restHighLevelClient.ping());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPingFailed() throws IOException {
|
||||||
|
when(restClient.performRequest(any(), any())).thenThrow(new SocketTimeoutException());
|
||||||
|
assertFalse(restHighLevelClient.ping());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testPingWithHeaders() throws IOException {
|
||||||
|
Header[] headers = RestClientTestUtil.randomHeaders(random(), "Header");
|
||||||
|
assertTrue(restHighLevelClient.ping(headers));
|
||||||
|
verify(restClient).performRequest(eq("HEAD"), eq("/"), argThat(new HeadersVarargMatcher(headers)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private class HeadersVarargMatcher extends ArgumentMatcher<Header[]> implements VarargMatcher {
|
||||||
|
private Header[] expectedHeaders;
|
||||||
|
|
||||||
|
HeadersVarargMatcher(Header... expectedHeaders) {
|
||||||
|
this.expectedHeaders = expectedHeaders;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(Object varargArgument) {
|
||||||
|
if (varargArgument instanceof Header[]) {
|
||||||
|
Header[] actualHeaders = (Header[]) varargArgument;
|
||||||
|
return new ArrayEquals(expectedHeaders).matches(actualHeaders);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,6 +43,7 @@ dependencies {
|
||||||
testCompile "junit:junit:${versions.junit}"
|
testCompile "junit:junit:${versions.junit}"
|
||||||
testCompile "org.hamcrest:hamcrest-all:${versions.hamcrest}"
|
testCompile "org.hamcrest:hamcrest-all:${versions.hamcrest}"
|
||||||
testCompile "org.elasticsearch:securemock:${versions.securemock}"
|
testCompile "org.elasticsearch:securemock:${versions.securemock}"
|
||||||
|
testCompile "org.elasticsearch:mocksocket:${versions.mocksocket}"
|
||||||
testCompile "org.codehaus.mojo:animal-sniffer-annotations:1.15"
|
testCompile "org.codehaus.mojo:animal-sniffer-annotations:1.15"
|
||||||
signature "org.codehaus.mojo.signature:java17:1.0@signature"
|
signature "org.codehaus.mojo.signature:java17:1.0@signature"
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import com.sun.net.httpserver.HttpHandler;
|
||||||
import com.sun.net.httpserver.HttpServer;
|
import com.sun.net.httpserver.HttpServer;
|
||||||
import org.apache.http.HttpHost;
|
import org.apache.http.HttpHost;
|
||||||
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
|
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
|
||||||
|
import org.elasticsearch.mocksocket.MockHttpServer;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
@ -80,7 +81,7 @@ public class RestClientMultipleHostsIntegTests extends RestClientTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpServer createHttpServer() throws Exception {
|
private static HttpServer createHttpServer() throws Exception {
|
||||||
HttpServer httpServer = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
|
HttpServer httpServer = MockHttpServer.createHttp(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
|
||||||
httpServer.start();
|
httpServer.start();
|
||||||
//returns a different status code depending on the path
|
//returns a different status code depending on the path
|
||||||
for (int statusCode : getAllStatusCodes()) {
|
for (int statusCode : getAllStatusCodes()) {
|
||||||
|
|
|
@ -29,6 +29,7 @@ import org.apache.http.HttpHost;
|
||||||
import org.apache.http.entity.StringEntity;
|
import org.apache.http.entity.StringEntity;
|
||||||
import org.apache.http.util.EntityUtils;
|
import org.apache.http.util.EntityUtils;
|
||||||
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
|
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
|
||||||
|
import org.elasticsearch.mocksocket.MockHttpServer;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
|
||||||
|
@ -39,7 +40,6 @@ import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -49,7 +49,6 @@ import static org.elasticsearch.client.RestClientTestUtil.getAllStatusCodes;
|
||||||
import static org.elasticsearch.client.RestClientTestUtil.getHttpMethods;
|
import static org.elasticsearch.client.RestClientTestUtil.getHttpMethods;
|
||||||
import static org.elasticsearch.client.RestClientTestUtil.randomStatusCode;
|
import static org.elasticsearch.client.RestClientTestUtil.randomStatusCode;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -76,8 +75,7 @@ public class RestClientSingleHostIntegTests extends RestClientTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
httpServer = createHttpServer();
|
httpServer = createHttpServer();
|
||||||
int numHeaders = randomIntBetween(0, 5);
|
defaultHeaders = RestClientTestUtil.randomHeaders(getRandom(), "Header-default");
|
||||||
defaultHeaders = generateHeaders("Header-default", "Header-array", numHeaders);
|
|
||||||
RestClientBuilder restClientBuilder = RestClient.builder(
|
RestClientBuilder restClientBuilder = RestClient.builder(
|
||||||
new HttpHost(httpServer.getAddress().getHostString(), httpServer.getAddress().getPort())).setDefaultHeaders(defaultHeaders);
|
new HttpHost(httpServer.getAddress().getHostString(), httpServer.getAddress().getPort())).setDefaultHeaders(defaultHeaders);
|
||||||
if (pathPrefix.length() > 0) {
|
if (pathPrefix.length() > 0) {
|
||||||
|
@ -87,7 +85,7 @@ public class RestClientSingleHostIntegTests extends RestClientTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpServer createHttpServer() throws Exception {
|
private static HttpServer createHttpServer() throws Exception {
|
||||||
HttpServer httpServer = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
|
HttpServer httpServer = MockHttpServer.createHttp(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
|
||||||
httpServer.start();
|
httpServer.start();
|
||||||
//returns a different status code depending on the path
|
//returns a different status code depending on the path
|
||||||
for (int statusCode : getAllStatusCodes()) {
|
for (int statusCode : getAllStatusCodes()) {
|
||||||
|
@ -150,17 +148,11 @@ public class RestClientSingleHostIntegTests extends RestClientTestCase {
|
||||||
if (method.equals("HEAD") == false) {
|
if (method.equals("HEAD") == false) {
|
||||||
standardHeaders.add("Content-length");
|
standardHeaders.add("Content-length");
|
||||||
}
|
}
|
||||||
|
final Header[] requestHeaders = RestClientTestUtil.randomHeaders(getRandom(), "Header");
|
||||||
final int numHeaders = randomIntBetween(1, 5);
|
|
||||||
final Header[] headers = generateHeaders("Header", "Header-array", numHeaders);
|
|
||||||
final Map<String, List<String>> expectedHeaders = new HashMap<>();
|
|
||||||
|
|
||||||
addHeaders(expectedHeaders, defaultHeaders, headers);
|
|
||||||
|
|
||||||
final int statusCode = randomStatusCode(getRandom());
|
final int statusCode = randomStatusCode(getRandom());
|
||||||
Response esResponse;
|
Response esResponse;
|
||||||
try {
|
try {
|
||||||
esResponse = restClient.performRequest(method, "/" + statusCode, Collections.<String, String>emptyMap(), headers);
|
esResponse = restClient.performRequest(method, "/" + statusCode, Collections.<String, String>emptyMap(), requestHeaders);
|
||||||
} catch(ResponseException e) {
|
} catch(ResponseException e) {
|
||||||
esResponse = e.getResponse();
|
esResponse = e.getResponse();
|
||||||
}
|
}
|
||||||
|
@ -168,24 +160,13 @@ public class RestClientSingleHostIntegTests extends RestClientTestCase {
|
||||||
assertEquals(method, esResponse.getRequestLine().getMethod());
|
assertEquals(method, esResponse.getRequestLine().getMethod());
|
||||||
assertEquals(statusCode, esResponse.getStatusLine().getStatusCode());
|
assertEquals(statusCode, esResponse.getStatusLine().getStatusCode());
|
||||||
assertEquals((pathPrefix.length() > 0 ? pathPrefix : "") + "/" + statusCode, esResponse.getRequestLine().getUri());
|
assertEquals((pathPrefix.length() > 0 ? pathPrefix : "") + "/" + statusCode, esResponse.getRequestLine().getUri());
|
||||||
|
assertHeaders(defaultHeaders, requestHeaders, esResponse.getHeaders(), standardHeaders);
|
||||||
for (final Header responseHeader : esResponse.getHeaders()) {
|
for (final Header responseHeader : esResponse.getHeaders()) {
|
||||||
final String name = responseHeader.getName();
|
String name = responseHeader.getName();
|
||||||
final String value = responseHeader.getValue();
|
if (name.startsWith("Header") == false) {
|
||||||
if (name.startsWith("Header")) {
|
|
||||||
final List<String> values = expectedHeaders.get(name);
|
|
||||||
assertNotNull("found response header [" + name + "] that wasn't originally sent: " + value, values);
|
|
||||||
assertTrue("found incorrect response header [" + name + "]: " + value, values.remove(value));
|
|
||||||
|
|
||||||
// we've collected them all
|
|
||||||
if (values.isEmpty()) {
|
|
||||||
expectedHeaders.remove(name);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
assertTrue("unknown header was returned " + name, standardHeaders.remove(name));
|
assertTrue("unknown header was returned " + name, standardHeaders.remove(name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertTrue("some headers that were sent weren't returned: " + expectedHeaders, expectedHeaders.isEmpty());
|
|
||||||
assertTrue("some expected standard headers weren't returned: " + standardHeaders, standardHeaders.isEmpty());
|
assertTrue("some expected standard headers weren't returned: " + standardHeaders, standardHeaders.isEmpty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,7 +56,6 @@ import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
@ -70,7 +69,6 @@ import static org.hamcrest.CoreMatchers.equalTo;
|
||||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
@ -131,9 +129,7 @@ public class RestClientSingleHostTests extends RestClientTestCase {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
defaultHeaders = RestClientTestUtil.randomHeaders(getRandom(), "Header-default");
|
||||||
int numHeaders = randomIntBetween(0, 3);
|
|
||||||
defaultHeaders = generateHeaders("Header-default", "Header-array", numHeaders);
|
|
||||||
httpHost = new HttpHost("localhost", 9200);
|
httpHost = new HttpHost("localhost", 9200);
|
||||||
failureListener = new HostsTrackingFailureListener();
|
failureListener = new HostsTrackingFailureListener();
|
||||||
restClient = new RestClient(httpClient, 10000, defaultHeaders, new HttpHost[]{httpHost}, null, failureListener);
|
restClient = new RestClient(httpClient, 10000, defaultHeaders, new HttpHost[]{httpHost}, null, failureListener);
|
||||||
|
@ -339,33 +335,16 @@ public class RestClientSingleHostTests extends RestClientTestCase {
|
||||||
*/
|
*/
|
||||||
public void testHeaders() throws IOException {
|
public void testHeaders() throws IOException {
|
||||||
for (String method : getHttpMethods()) {
|
for (String method : getHttpMethods()) {
|
||||||
final int numHeaders = randomIntBetween(1, 5);
|
final Header[] requestHeaders = RestClientTestUtil.randomHeaders(getRandom(), "Header");
|
||||||
final Header[] headers = generateHeaders("Header", null, numHeaders);
|
|
||||||
final Map<String, List<String>> expectedHeaders = new HashMap<>();
|
|
||||||
|
|
||||||
addHeaders(expectedHeaders, defaultHeaders, headers);
|
|
||||||
|
|
||||||
final int statusCode = randomStatusCode(getRandom());
|
final int statusCode = randomStatusCode(getRandom());
|
||||||
Response esResponse;
|
Response esResponse;
|
||||||
try {
|
try {
|
||||||
esResponse = restClient.performRequest(method, "/" + statusCode, headers);
|
esResponse = restClient.performRequest(method, "/" + statusCode, requestHeaders);
|
||||||
} catch(ResponseException e) {
|
} catch(ResponseException e) {
|
||||||
esResponse = e.getResponse();
|
esResponse = e.getResponse();
|
||||||
}
|
}
|
||||||
assertThat(esResponse.getStatusLine().getStatusCode(), equalTo(statusCode));
|
assertThat(esResponse.getStatusLine().getStatusCode(), equalTo(statusCode));
|
||||||
for (Header responseHeader : esResponse.getHeaders()) {
|
assertHeaders(defaultHeaders, requestHeaders, esResponse.getHeaders(), Collections.<String>emptySet());
|
||||||
final String name = responseHeader.getName();
|
|
||||||
final String value = responseHeader.getValue();
|
|
||||||
final List<String> values = expectedHeaders.get(name);
|
|
||||||
assertNotNull("found response header [" + name + "] that wasn't originally sent: " + value, values);
|
|
||||||
assertTrue("found incorrect response header [" + name + "]: " + value, values.remove(value));
|
|
||||||
|
|
||||||
// we've collected them all
|
|
||||||
if (values.isEmpty()) {
|
|
||||||
expectedHeaders.remove(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assertTrue("some headers that were sent weren't returned " + expectedHeaders, expectedHeaders.isEmpty());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -424,10 +403,9 @@ public class RestClientSingleHostTests extends RestClientTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
Header[] headers = new Header[0];
|
Header[] headers = new Header[0];
|
||||||
final int numHeaders = randomIntBetween(1, 5);
|
final Set<String> uniqueNames = new HashSet<>();
|
||||||
final Set<String> uniqueNames = new HashSet<>(numHeaders);
|
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
headers = generateHeaders("Header", "Header-array", numHeaders);
|
headers = RestClientTestUtil.randomHeaders(getRandom(), "Header");
|
||||||
for (Header header : headers) {
|
for (Header header : headers) {
|
||||||
request.addHeader(header);
|
request.addHeader(header);
|
||||||
uniqueNames.add(header.getName());
|
uniqueNames.add(header.getName());
|
||||||
|
|
|
@ -43,6 +43,7 @@ dependencies {
|
||||||
testCompile "junit:junit:${versions.junit}"
|
testCompile "junit:junit:${versions.junit}"
|
||||||
testCompile "org.hamcrest:hamcrest-all:${versions.hamcrest}"
|
testCompile "org.hamcrest:hamcrest-all:${versions.hamcrest}"
|
||||||
testCompile "org.elasticsearch:securemock:${versions.securemock}"
|
testCompile "org.elasticsearch:securemock:${versions.securemock}"
|
||||||
|
testCompile "org.elasticsearch:mocksocket:${versions.mocksocket}"
|
||||||
testCompile "org.codehaus.mojo:animal-sniffer-annotations:1.15"
|
testCompile "org.codehaus.mojo:animal-sniffer-annotations:1.15"
|
||||||
signature "org.codehaus.mojo.signature:java17:1.0@signature"
|
signature "org.codehaus.mojo.signature:java17:1.0@signature"
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.elasticsearch.client.Response;
|
||||||
import org.elasticsearch.client.ResponseException;
|
import org.elasticsearch.client.ResponseException;
|
||||||
import org.elasticsearch.client.RestClient;
|
import org.elasticsearch.client.RestClient;
|
||||||
import org.elasticsearch.client.RestClientTestCase;
|
import org.elasticsearch.client.RestClientTestCase;
|
||||||
|
import org.elasticsearch.mocksocket.MockHttpServer;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
|
@ -141,7 +142,7 @@ public class ElasticsearchHostsSnifferTests extends RestClientTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HttpServer createHttpServer(final SniffResponse sniffResponse, final int sniffTimeoutMillis) throws IOException {
|
private static HttpServer createHttpServer(final SniffResponse sniffResponse, final int sniffTimeoutMillis) throws IOException {
|
||||||
HttpServer httpServer = HttpServer.create(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
|
HttpServer httpServer = MockHttpServer.createHttp(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
|
||||||
httpServer.createContext("/_nodes/http", new ResponseHandler(sniffTimeoutMillis, sniffResponse));
|
httpServer.createContext("/_nodes/http", new ResponseHandler(sniffTimeoutMillis, sniffResponse));
|
||||||
return httpServer;
|
return httpServer;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,16 +30,19 @@ import com.carrotsearch.randomizedtesting.annotations.ThreadLeakLingering;
|
||||||
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
|
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakScope;
|
||||||
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakZombies;
|
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakZombies;
|
||||||
import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite;
|
import com.carrotsearch.randomizedtesting.annotations.TimeoutSuite;
|
||||||
|
|
||||||
import org.apache.http.Header;
|
import org.apache.http.Header;
|
||||||
import org.apache.http.message.BasicHeader;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
@TestMethodProviders({
|
@TestMethodProviders({
|
||||||
JUnit3MethodProvider.class
|
JUnit3MethodProvider.class
|
||||||
})
|
})
|
||||||
|
@ -53,70 +56,56 @@ import java.util.Set;
|
||||||
public abstract class RestClientTestCase extends RandomizedTest {
|
public abstract class RestClientTestCase extends RandomizedTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the specified number of {@link Header}s.
|
* Assert that the actual headers are the expected ones given the original default and request headers. Some headers can be ignored,
|
||||||
* <p>
|
* for instance in case the http client is adding its own automatically.
|
||||||
* Generated header names will be the {@code baseName} plus its index or, rarely, the {@code arrayName} if it's supplied.
|
|
||||||
*
|
*
|
||||||
* @param baseName The base name to use for all headers.
|
* @param defaultHeaders the default headers set to the REST client instance
|
||||||
* @param arrayName The optional ({@code null}able) array name to use randomly.
|
* @param requestHeaders the request headers sent with a particular request
|
||||||
* @param headers The number of headers to create.
|
* @param actualHeaders the actual headers as a result of the provided default and request headers
|
||||||
* @return Never {@code null}.
|
* @param ignoreHeaders header keys to be ignored as they are not part of default nor request headers, yet they
|
||||||
|
* will be part of the actual ones
|
||||||
*/
|
*/
|
||||||
protected static Header[] generateHeaders(final String baseName, final String arrayName, final int headers) {
|
protected static void assertHeaders(final Header[] defaultHeaders, final Header[] requestHeaders,
|
||||||
final Header[] generated = new Header[headers];
|
final Header[] actualHeaders, final Set<String> ignoreHeaders) {
|
||||||
for (int i = 0; i < headers; i++) {
|
final Map<String, List<String>> expectedHeaders = new HashMap<>();
|
||||||
String headerName = baseName + i;
|
final Set<String> requestHeaderKeys = new HashSet<>();
|
||||||
if (arrayName != null && rarely()) {
|
for (final Header header : requestHeaders) {
|
||||||
headerName = arrayName;
|
final String name = header.getName();
|
||||||
|
addValueToListEntry(expectedHeaders, name, header.getValue());
|
||||||
|
requestHeaderKeys.add(name);
|
||||||
|
}
|
||||||
|
for (final Header defaultHeader : defaultHeaders) {
|
||||||
|
final String name = defaultHeader.getName();
|
||||||
|
if (requestHeaderKeys.contains(name) == false) {
|
||||||
|
addValueToListEntry(expectedHeaders, name, defaultHeader.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Set<String> actualIgnoredHeaders = new HashSet<>();
|
||||||
|
for (Header responseHeader : actualHeaders) {
|
||||||
|
final String name = responseHeader.getName();
|
||||||
|
if (ignoreHeaders.contains(name)) {
|
||||||
|
expectedHeaders.remove(name);
|
||||||
|
actualIgnoredHeaders.add(name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final String value = responseHeader.getValue();
|
||||||
|
final List<String> values = expectedHeaders.get(name);
|
||||||
|
assertNotNull("found response header [" + name + "] that wasn't originally sent: " + value, values);
|
||||||
|
assertTrue("found incorrect response header [" + name + "]: " + value, values.remove(value));
|
||||||
|
if (values.isEmpty()) {
|
||||||
|
expectedHeaders.remove(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals("some headers meant to be ignored were not part of the actual headers", ignoreHeaders, actualIgnoredHeaders);
|
||||||
|
assertTrue("some headers that were sent weren't returned " + expectedHeaders, expectedHeaders.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
generated[i] = new BasicHeader(headerName, randomAsciiOfLengthBetween(3, 10));
|
private static void addValueToListEntry(final Map<String, List<String>> map, final String name, final String value) {
|
||||||
}
|
|
||||||
return generated;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new {@link List} within the {@code map} if none exists for {@code name} or append to the existing list.
|
|
||||||
*
|
|
||||||
* @param map The map to manipulate.
|
|
||||||
* @param name The name to create/append the list for.
|
|
||||||
* @param value The value to add.
|
|
||||||
*/
|
|
||||||
private static void createOrAppendList(final Map<String, List<String>> map, final String name, final String value) {
|
|
||||||
List<String> values = map.get(name);
|
List<String> values = map.get(name);
|
||||||
|
|
||||||
if (values == null) {
|
if (values == null) {
|
||||||
values = new ArrayList<>();
|
values = new ArrayList<>();
|
||||||
map.put(name, values);
|
map.put(name, values);
|
||||||
}
|
}
|
||||||
|
|
||||||
values.add(value);
|
values.add(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the {@code headers} to the {@code map} so that related tests can more easily assert that they exist.
|
|
||||||
* <p>
|
|
||||||
* If both the {@code defaultHeaders} and {@code headers} contain the same {@link Header}, based on its
|
|
||||||
* {@linkplain Header#getName() name}, then this will only use the {@code Header}(s) from {@code headers}.
|
|
||||||
*
|
|
||||||
* @param map The map to build with name/value(s) pairs.
|
|
||||||
* @param defaultHeaders The headers to add to the map representing default headers.
|
|
||||||
* @param headers The headers to add to the map representing request-level headers.
|
|
||||||
* @see #createOrAppendList(Map, String, String)
|
|
||||||
*/
|
|
||||||
protected static void addHeaders(final Map<String, List<String>> map, final Header[] defaultHeaders, final Header[] headers) {
|
|
||||||
final Set<String> uniqueHeaders = new HashSet<>();
|
|
||||||
for (final Header header : headers) {
|
|
||||||
final String name = header.getName();
|
|
||||||
createOrAppendList(map, name, header.getValue());
|
|
||||||
uniqueHeaders.add(name);
|
|
||||||
}
|
|
||||||
for (final Header defaultHeader : defaultHeaders) {
|
|
||||||
final String name = defaultHeader.getName();
|
|
||||||
if (uniqueHeaders.contains(name) == false) {
|
|
||||||
createOrAppendList(map, name, defaultHeader.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,11 @@
|
||||||
|
|
||||||
package org.elasticsearch.client;
|
package org.elasticsearch.client;
|
||||||
|
|
||||||
|
import com.carrotsearch.randomizedtesting.generators.RandomNumbers;
|
||||||
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
|
||||||
|
import com.carrotsearch.randomizedtesting.generators.RandomStrings;
|
||||||
|
import org.apache.http.Header;
|
||||||
|
import org.apache.http.message.BasicHeader;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -81,4 +85,23 @@ final class RestClientTestUtil {
|
||||||
static List<Integer> getAllStatusCodes() {
|
static List<Integer> getAllStatusCodes() {
|
||||||
return ALL_STATUS_CODES;
|
return ALL_STATUS_CODES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a random number of {@link Header}s.
|
||||||
|
* Generated header names will either be the {@code baseName} plus its index, or exactly the provided {@code baseName} so that the
|
||||||
|
* we test also support for multiple headers with same key and different values.
|
||||||
|
*/
|
||||||
|
static Header[] randomHeaders(Random random, final String baseName) {
|
||||||
|
int numHeaders = RandomNumbers.randomIntBetween(random, 0, 5);
|
||||||
|
final Header[] headers = new Header[numHeaders];
|
||||||
|
for (int i = 0; i < numHeaders; i++) {
|
||||||
|
String headerName = baseName;
|
||||||
|
//randomly exercise the code path that supports multiple headers with same key
|
||||||
|
if (random.nextBoolean()) {
|
||||||
|
headerName = headerName + i;
|
||||||
|
}
|
||||||
|
headers[i] = new BasicHeader(headerName, RandomStrings.randomAsciiOfLengthBetween(random, 3, 10));
|
||||||
|
}
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,13 +37,12 @@ import java.util.Collections;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A builder to create an instance of {@link TransportClient}
|
* A builder to create an instance of {@link TransportClient}. This class pre-installs the
|
||||||
* This class pre-installs the
|
|
||||||
* {@link Netty4Plugin},
|
* {@link Netty4Plugin},
|
||||||
* {@link ReindexPlugin},
|
* {@link ReindexPlugin},
|
||||||
* {@link PercolatorPlugin},
|
* {@link PercolatorPlugin},
|
||||||
* and {@link MustachePlugin}
|
* and {@link MustachePlugin}
|
||||||
* for the client. These plugins are all elasticsearch core modules required.
|
* plugins for the client. These plugins are all the required modules for Elasticsearch.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"unchecked","varargs"})
|
@SuppressWarnings({"unchecked","varargs"})
|
||||||
public class PreBuiltTransportClient extends TransportClient {
|
public class PreBuiltTransportClient extends TransportClient {
|
||||||
|
@ -63,6 +62,8 @@ public class PreBuiltTransportClient extends TransportClient {
|
||||||
final String noUnsafe = System.getProperty(noUnsafeKey);
|
final String noUnsafe = System.getProperty(noUnsafeKey);
|
||||||
if (noUnsafe == null) {
|
if (noUnsafe == null) {
|
||||||
// disable Netty from using unsafe
|
// disable Netty from using unsafe
|
||||||
|
// while permissions are needed to set this, if a security exception is thrown the permission needed can either be granted or
|
||||||
|
// the system property can be set directly before starting the JVM; therefore, we do not catch a security exception here
|
||||||
System.setProperty(noUnsafeKey, Boolean.toString(true));
|
System.setProperty(noUnsafeKey, Boolean.toString(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,6 +71,8 @@ public class PreBuiltTransportClient extends TransportClient {
|
||||||
final String noKeySetOptimization = System.getProperty(noKeySetOptimizationKey);
|
final String noKeySetOptimization = System.getProperty(noKeySetOptimizationKey);
|
||||||
if (noKeySetOptimization == null) {
|
if (noKeySetOptimization == null) {
|
||||||
// disable Netty from replacing the selector key set
|
// disable Netty from replacing the selector key set
|
||||||
|
// while permissions are needed to set this, if a security exception is thrown the permission needed can either be granted or
|
||||||
|
// the system property can be set directly before starting the JVM; therefore, we do not catch a security exception here
|
||||||
System.setProperty(noKeySetOptimizationKey, Boolean.toString(true));
|
System.setProperty(noKeySetOptimizationKey, Boolean.toString(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,9 +85,9 @@ public class PreBuiltTransportClient extends TransportClient {
|
||||||
PercolatorPlugin.class,
|
PercolatorPlugin.class,
|
||||||
MustachePlugin.class));
|
MustachePlugin.class));
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new transport client with pre-installed plugins.
|
* Creates a new transport client with pre-installed plugins.
|
||||||
|
*
|
||||||
* @param settings the settings passed to this transport client
|
* @param settings the settings passed to this transport client
|
||||||
* @param plugins an optional array of additional plugins to run with this client
|
* @param plugins an optional array of additional plugins to run with this client
|
||||||
*/
|
*/
|
||||||
|
@ -93,9 +96,9 @@ public class PreBuiltTransportClient extends TransportClient {
|
||||||
this(settings, Arrays.asList(plugins));
|
this(settings, Arrays.asList(plugins));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new transport client with pre-installed plugins.
|
* Creates a new transport client with pre-installed plugins.
|
||||||
|
*
|
||||||
* @param settings the settings passed to this transport client
|
* @param settings the settings passed to this transport client
|
||||||
* @param plugins a collection of additional plugins to run with this client
|
* @param plugins a collection of additional plugins to run with this client
|
||||||
*/
|
*/
|
||||||
|
@ -105,11 +108,14 @@ public class PreBuiltTransportClient extends TransportClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new transport client with pre-installed plugins.
|
* Creates a new transport client with pre-installed plugins.
|
||||||
|
*
|
||||||
* @param settings the settings passed to this transport client
|
* @param settings the settings passed to this transport client
|
||||||
* @param plugins a collection of additional plugins to run with this client
|
* @param plugins a collection of additional plugins to run with this client
|
||||||
* @param hostFailureListener a failure listener that is invoked if a node is disconnected. This can be <code>null</code>
|
* @param hostFailureListener a failure listener that is invoked if a node is disconnected; this can be <code>null</code>
|
||||||
*/
|
*/
|
||||||
public PreBuiltTransportClient(Settings settings, Collection<Class<? extends Plugin>> plugins,
|
public PreBuiltTransportClient(
|
||||||
|
Settings settings,
|
||||||
|
Collection<Class<? extends Plugin>> plugins,
|
||||||
HostFailureListener hostFailureListener) {
|
HostFailureListener hostFailureListener) {
|
||||||
super(settings, Settings.EMPTY, addPlugins(plugins, PRE_INSTALLED_PLUGINS), hostFailureListener);
|
super(settings, Settings.EMPTY, addPlugins(plugins, PRE_INSTALLED_PLUGINS), hostFailureListener);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
ad1553dd2eed3a7cd5778bc7520821ac926b56df
|
|
@ -1 +0,0 @@
|
||||||
770114e0188dd8b4f30e5878b4f6c8677cecf1be
|
|
|
@ -0,0 +1 @@
|
||||||
|
dde630b1d09ff928a1f358951747cfad5c46b334
|
|
@ -1 +0,0 @@
|
||||||
f4eb0257e8419beaa9f84da6a51375fda4e491f2
|
|
|
@ -0,0 +1 @@
|
||||||
|
1789bff323a0c013b126f4e51f1f269ebc631277
|
|
@ -1 +0,0 @@
|
||||||
c80ad16cd36c41012abb8a8bb1c7328c6d680b4a
|
|
|
@ -0,0 +1 @@
|
||||||
|
8cb17916d0e63705f1f715fe0d03ed32916a077a
|
|
@ -1 +0,0 @@
|
||||||
070d4e370f4fe0b8a04b2bce5b4381201b0c783f
|
|
|
@ -0,0 +1 @@
|
||||||
|
79d6ba8fa629a52ad3eb829d085836f5fd2f7a87
|
|
@ -1 +0,0 @@
|
||||||
131d9a86f5943675493a85def0e692842f396458
|
|
|
@ -0,0 +1 @@
|
||||||
|
19794d8f15402c991d9533bfcd67e2e7a34677ef
|
|
@ -1 +0,0 @@
|
||||||
385b2202036b50a764e4d2b032e21496b74a1c8e
|
|
|
@ -0,0 +1 @@
|
||||||
|
33e42d3019e072752258bd778912c8d4365470a1
|
|
@ -1 +0,0 @@
|
||||||
e8742a44ef4849a17d5e59ef36e9a52a8f2370c2
|
|
|
@ -0,0 +1 @@
|
||||||
|
a1b3271b3800da349c8b98f7b1a25b2b6192252a
|
|
@ -1 +0,0 @@
|
||||||
7ce2e4948fb66393a34f4200a6131cfde43e47bd
|
|
|
@ -0,0 +1 @@
|
||||||
|
792716d805fcc5091931874c2f2f86f35da8b401
|
|
@ -1 +0,0 @@
|
||||||
6c1c385a597ce797b0049d9b2281b09593e1488a
|
|
|
@ -0,0 +1 @@
|
||||||
|
c3f8bbc6ebe8d31da41fcdb1fa73f13d8170ee62
|
|
@ -1 +0,0 @@
|
||||||
fafaa22906c067e6894f9f2b18ad03ded98e2f38
|
|
|
@ -0,0 +1 @@
|
||||||
|
263901a19686c6cce7dd5c32a4934c42c62454dc
|
|
@ -1 +0,0 @@
|
||||||
19c64a84617f42bb4c11b1e266df4009cd37fdd0
|
|
|
@ -0,0 +1 @@
|
||||||
|
85426164fcc264a7e3bacc1a70602513540a261a
|
|
@ -1 +0,0 @@
|
||||||
bc8613fb61c0ae95dd3680b0f65e3380c3fd0d6c
|
|
|
@ -0,0 +1 @@
|
||||||
|
332cbfaa6b1ee0bf4d820018872988e15cd413d2
|
|
@ -1 +0,0 @@
|
||||||
0fa2c3e722294e863f3c70a15e97a18397391fb4
|
|
|
@ -0,0 +1 @@
|
||||||
|
3fe3e902b971f4aa2b4a3a417ba5dcf83e968428
|
|
@ -1 +0,0 @@
|
||||||
db74c6313965ffdd10d9b19be2eed4ae2c76d2e3
|
|
|
@ -0,0 +1 @@
|
||||||
|
c4863fe45853163abfbe5c8b8bd7bdcf9a9c7b40
|
|
@ -1 +0,0 @@
|
||||||
b85ae1121b5fd56df985615a3cdd7b3879e9b92d
|
|
|
@ -1,291 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright ownership.
|
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
||||||
* (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.lucene.analysis.synonym;
|
|
||||||
|
|
||||||
import static org.apache.lucene.util.automaton.Operations.DEFAULT_MAX_DETERMINIZED_STATES;
|
|
||||||
|
|
||||||
import org.apache.lucene.analysis.TokenStream;
|
|
||||||
import org.apache.lucene.analysis.tokenattributes.BytesTermAttribute;
|
|
||||||
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
|
|
||||||
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
|
|
||||||
import org.apache.lucene.analysis.tokenattributes.PositionLengthAttribute;
|
|
||||||
import org.apache.lucene.analysis.tokenattributes.TermToBytesRefAttribute;
|
|
||||||
import org.apache.lucene.util.BytesRef;
|
|
||||||
import org.apache.lucene.util.IntsRef;
|
|
||||||
import org.apache.lucene.util.automaton.Automaton;
|
|
||||||
import org.apache.lucene.util.automaton.FiniteStringsIterator;
|
|
||||||
import org.apache.lucene.util.automaton.Operations;
|
|
||||||
import org.apache.lucene.util.automaton.Transition;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a list of {@link TokenStream} where each stream is the tokens that make up a finite string in graph token stream. To do this,
|
|
||||||
* the graph token stream is converted to an {@link Automaton} and from there we use a {@link FiniteStringsIterator} to collect the various
|
|
||||||
* token streams for each finite string.
|
|
||||||
*/
|
|
||||||
public class GraphTokenStreamFiniteStrings {
|
|
||||||
private final Automaton.Builder builder;
|
|
||||||
Automaton det;
|
|
||||||
private final Map<BytesRef, Integer> termToID = new HashMap<>();
|
|
||||||
private final Map<Integer, BytesRef> idToTerm = new HashMap<>();
|
|
||||||
private int anyTermID = -1;
|
|
||||||
|
|
||||||
public GraphTokenStreamFiniteStrings() {
|
|
||||||
this.builder = new Automaton.Builder();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class BytesRefArrayTokenStream extends TokenStream {
|
|
||||||
private final BytesTermAttribute termAtt = addAttribute(BytesTermAttribute.class);
|
|
||||||
private final BytesRef[] terms;
|
|
||||||
private int offset;
|
|
||||||
|
|
||||||
BytesRefArrayTokenStream(BytesRef[] terms) {
|
|
||||||
this.terms = terms;
|
|
||||||
offset = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean incrementToken() throws IOException {
|
|
||||||
if (offset < terms.length) {
|
|
||||||
clearAttributes();
|
|
||||||
termAtt.setBytesRef(terms[offset]);
|
|
||||||
offset = offset + 1;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets
|
|
||||||
*/
|
|
||||||
public List<TokenStream> getTokenStreams(final TokenStream in) throws IOException {
|
|
||||||
// build automation
|
|
||||||
build(in);
|
|
||||||
|
|
||||||
List<TokenStream> tokenStreams = new ArrayList<>();
|
|
||||||
final FiniteStringsIterator finiteStrings = new FiniteStringsIterator(det);
|
|
||||||
for (IntsRef string; (string = finiteStrings.next()) != null; ) {
|
|
||||||
final BytesRef[] tokens = new BytesRef[string.length];
|
|
||||||
for (int idx = string.offset, len = string.offset + string.length; idx < len; idx++) {
|
|
||||||
tokens[idx - string.offset] = idToTerm.get(string.ints[idx]);
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenStreams.add(new BytesRefArrayTokenStream(tokens));
|
|
||||||
}
|
|
||||||
|
|
||||||
return tokenStreams;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void build(final TokenStream in) throws IOException {
|
|
||||||
if (det != null) {
|
|
||||||
throw new IllegalStateException("Automation already built");
|
|
||||||
}
|
|
||||||
|
|
||||||
final TermToBytesRefAttribute termBytesAtt = in.addAttribute(TermToBytesRefAttribute.class);
|
|
||||||
final PositionIncrementAttribute posIncAtt = in.addAttribute(PositionIncrementAttribute.class);
|
|
||||||
final PositionLengthAttribute posLengthAtt = in.addAttribute(PositionLengthAttribute.class);
|
|
||||||
final OffsetAttribute offsetAtt = in.addAttribute(OffsetAttribute.class);
|
|
||||||
|
|
||||||
in.reset();
|
|
||||||
|
|
||||||
int pos = -1;
|
|
||||||
int lastPos = 0;
|
|
||||||
int maxOffset = 0;
|
|
||||||
int maxPos = -1;
|
|
||||||
int state = -1;
|
|
||||||
while (in.incrementToken()) {
|
|
||||||
int posInc = posIncAtt.getPositionIncrement();
|
|
||||||
assert pos > -1 || posInc > 0;
|
|
||||||
|
|
||||||
if (posInc > 1) {
|
|
||||||
throw new IllegalArgumentException("cannot handle holes; to accept any term, use '*' term");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (posInc > 0) {
|
|
||||||
// New node:
|
|
||||||
pos += posInc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int endPos = pos + posLengthAtt.getPositionLength();
|
|
||||||
while (state < endPos) {
|
|
||||||
state = createState();
|
|
||||||
}
|
|
||||||
|
|
||||||
BytesRef term = termBytesAtt.getBytesRef();
|
|
||||||
//System.out.println(pos + "-" + endPos + ": " + term.utf8ToString() + ": posInc=" + posInc);
|
|
||||||
if (term.length == 1 && term.bytes[term.offset] == (byte) '*') {
|
|
||||||
addAnyTransition(pos, endPos);
|
|
||||||
} else {
|
|
||||||
addTransition(pos, endPos, term);
|
|
||||||
}
|
|
||||||
|
|
||||||
maxOffset = Math.max(maxOffset, offsetAtt.endOffset());
|
|
||||||
maxPos = Math.max(maxPos, endPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
in.end();
|
|
||||||
|
|
||||||
// TODO: look at endOffset? ts2a did...
|
|
||||||
|
|
||||||
// TODO: this (setting "last" state as the only accept state) may be too simplistic?
|
|
||||||
setAccept(state, true);
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a new state; state 0 is always the initial state.
|
|
||||||
*/
|
|
||||||
private int createState() {
|
|
||||||
return builder.createState();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks the specified state as accept or not.
|
|
||||||
*/
|
|
||||||
private void setAccept(int state, boolean accept) {
|
|
||||||
builder.setAccept(state, accept);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a transition to the automaton.
|
|
||||||
*/
|
|
||||||
private void addTransition(int source, int dest, String term) {
|
|
||||||
addTransition(source, dest, new BytesRef(term));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a transition to the automaton.
|
|
||||||
*/
|
|
||||||
private void addTransition(int source, int dest, BytesRef term) {
|
|
||||||
if (term == null) {
|
|
||||||
throw new NullPointerException("term should not be null");
|
|
||||||
}
|
|
||||||
builder.addTransition(source, dest, getTermID(term));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a transition matching any term.
|
|
||||||
*/
|
|
||||||
private void addAnyTransition(int source, int dest) {
|
|
||||||
builder.addTransition(source, dest, getTermID(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this once you are done adding states/transitions.
|
|
||||||
*/
|
|
||||||
private void finish() {
|
|
||||||
finish(DEFAULT_MAX_DETERMINIZED_STATES);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call this once you are done adding states/transitions.
|
|
||||||
*
|
|
||||||
* @param maxDeterminizedStates Maximum number of states created when determinizing the automaton. Higher numbers allow this operation
|
|
||||||
* to consume more memory but allow more complex automatons.
|
|
||||||
*/
|
|
||||||
private void finish(int maxDeterminizedStates) {
|
|
||||||
Automaton automaton = builder.finish();
|
|
||||||
|
|
||||||
// System.out.println("before det:\n" + automaton.toDot());
|
|
||||||
|
|
||||||
Transition t = new Transition();
|
|
||||||
|
|
||||||
// TODO: should we add "eps back to initial node" for all states,
|
|
||||||
// and det that? then we don't need to revisit initial node at
|
|
||||||
// every position? but automaton could blow up? And, this makes it
|
|
||||||
// harder to skip useless positions at search time?
|
|
||||||
|
|
||||||
if (anyTermID != -1) {
|
|
||||||
|
|
||||||
// Make sure there are no leading or trailing ANY:
|
|
||||||
int count = automaton.initTransition(0, t);
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
automaton.getNextTransition(t);
|
|
||||||
if (anyTermID >= t.min && anyTermID <= t.max) {
|
|
||||||
throw new IllegalStateException("automaton cannot lead with an ANY transition");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int numStates = automaton.getNumStates();
|
|
||||||
for (int i = 0; i < numStates; i++) {
|
|
||||||
count = automaton.initTransition(i, t);
|
|
||||||
for (int j = 0; j < count; j++) {
|
|
||||||
automaton.getNextTransition(t);
|
|
||||||
if (automaton.isAccept(t.dest) && anyTermID >= t.min && anyTermID <= t.max) {
|
|
||||||
throw new IllegalStateException("automaton cannot end with an ANY transition");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int termCount = termToID.size();
|
|
||||||
|
|
||||||
// We have to carefully translate these transitions so automaton
|
|
||||||
// realizes they also match all other terms:
|
|
||||||
Automaton newAutomaton = new Automaton();
|
|
||||||
for (int i = 0; i < numStates; i++) {
|
|
||||||
newAutomaton.createState();
|
|
||||||
newAutomaton.setAccept(i, automaton.isAccept(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < numStates; i++) {
|
|
||||||
count = automaton.initTransition(i, t);
|
|
||||||
for (int j = 0; j < count; j++) {
|
|
||||||
automaton.getNextTransition(t);
|
|
||||||
int min, max;
|
|
||||||
if (t.min <= anyTermID && anyTermID <= t.max) {
|
|
||||||
// Match any term
|
|
||||||
min = 0;
|
|
||||||
max = termCount - 1;
|
|
||||||
} else {
|
|
||||||
min = t.min;
|
|
||||||
max = t.max;
|
|
||||||
}
|
|
||||||
newAutomaton.addTransition(t.source, t.dest, min, max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
newAutomaton.finishState();
|
|
||||||
automaton = newAutomaton;
|
|
||||||
}
|
|
||||||
|
|
||||||
det = Operations.removeDeadStates(Operations.determinize(automaton, maxDeterminizedStates));
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getTermID(BytesRef term) {
|
|
||||||
Integer id = termToID.get(term);
|
|
||||||
if (id == null) {
|
|
||||||
id = termToID.size();
|
|
||||||
if (term != null) {
|
|
||||||
term = BytesRef.deepCopyOf(term);
|
|
||||||
}
|
|
||||||
termToID.put(term, id);
|
|
||||||
idToTerm.put(id, term);
|
|
||||||
if (term == null) {
|
|
||||||
anyTermID = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,588 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright ownership.
|
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
||||||
* (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.lucene.analysis.synonym;
|
|
||||||
|
|
||||||
import org.apache.lucene.analysis.TokenFilter;
|
|
||||||
import org.apache.lucene.analysis.TokenStream;
|
|
||||||
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
|
|
||||||
import org.apache.lucene.analysis.tokenattributes.FlagsAttribute;
|
|
||||||
import org.apache.lucene.analysis.tokenattributes.OffsetAttribute;
|
|
||||||
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
|
|
||||||
import org.apache.lucene.analysis.tokenattributes.PositionLengthAttribute;
|
|
||||||
import org.apache.lucene.analysis.tokenattributes.TypeAttribute;
|
|
||||||
import org.apache.lucene.store.ByteArrayDataInput;
|
|
||||||
import org.apache.lucene.util.AttributeSource;
|
|
||||||
import org.apache.lucene.util.BytesRef;
|
|
||||||
import org.apache.lucene.util.CharsRefBuilder;
|
|
||||||
import org.apache.lucene.util.RollingBuffer;
|
|
||||||
import org.apache.lucene.util.fst.FST;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
// TODO: maybe we should resolve token -> wordID then run
|
|
||||||
// FST on wordIDs, for better perf?
|
|
||||||
|
|
||||||
// TODO: a more efficient approach would be Aho/Corasick's
|
|
||||||
// algorithm
|
|
||||||
// http://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_string_matching_algorithm
|
|
||||||
// It improves over the current approach here
|
|
||||||
// because it does not fully re-start matching at every
|
|
||||||
// token. For example if one pattern is "a b c x"
|
|
||||||
// and another is "b c d" and the input is "a b c d", on
|
|
||||||
// trying to parse "a b c x" but failing when you got to x,
|
|
||||||
// rather than starting over again your really should
|
|
||||||
// immediately recognize that "b c d" matches at the next
|
|
||||||
// input. I suspect this won't matter that much in
|
|
||||||
// practice, but it's possible on some set of synonyms it
|
|
||||||
// will. We'd have to modify Aho/Corasick to enforce our
|
|
||||||
// conflict resolving (eg greedy matching) because that algo
|
|
||||||
// finds all matches. This really amounts to adding a .*
|
|
||||||
// closure to the FST and then determinizing it.
|
|
||||||
//
|
|
||||||
// Another possible solution is described at http://www.cis.uni-muenchen.de/people/Schulz/Pub/dictle5.ps
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Applies single- or multi-token synonyms from a {@link SynonymMap}
|
|
||||||
* to an incoming {@link TokenStream}, producing a fully correct graph
|
|
||||||
* output. This is a replacement for {@link SynonymFilter}, which produces
|
|
||||||
* incorrect graphs for multi-token synonyms.
|
|
||||||
*
|
|
||||||
* <b>NOTE</b>: this cannot consume an incoming graph; results will
|
|
||||||
* be undefined.
|
|
||||||
*/
|
|
||||||
public final class SynonymGraphFilter extends TokenFilter {
|
|
||||||
|
|
||||||
public static final String TYPE_SYNONYM = "SYNONYM";
|
|
||||||
public static final int GRAPH_FLAG = 8;
|
|
||||||
|
|
||||||
private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
|
|
||||||
private final PositionIncrementAttribute posIncrAtt = addAttribute(PositionIncrementAttribute.class);
|
|
||||||
private final PositionLengthAttribute posLenAtt = addAttribute(PositionLengthAttribute.class);
|
|
||||||
private final FlagsAttribute flagsAtt = addAttribute(FlagsAttribute.class);
|
|
||||||
|
|
||||||
private final TypeAttribute typeAtt = addAttribute(TypeAttribute.class);
|
|
||||||
private final OffsetAttribute offsetAtt = addAttribute(OffsetAttribute.class);
|
|
||||||
|
|
||||||
private final SynonymMap synonyms;
|
|
||||||
private final boolean ignoreCase;
|
|
||||||
|
|
||||||
private final FST<BytesRef> fst;
|
|
||||||
|
|
||||||
private final FST.BytesReader fstReader;
|
|
||||||
private final FST.Arc<BytesRef> scratchArc;
|
|
||||||
private final ByteArrayDataInput bytesReader = new ByteArrayDataInput();
|
|
||||||
private final BytesRef scratchBytes = new BytesRef();
|
|
||||||
private final CharsRefBuilder scratchChars = new CharsRefBuilder();
|
|
||||||
private final LinkedList<BufferedOutputToken> outputBuffer = new LinkedList<>();
|
|
||||||
|
|
||||||
private int nextNodeOut;
|
|
||||||
private int lastNodeOut;
|
|
||||||
private int maxLookaheadUsed;
|
|
||||||
|
|
||||||
// For testing:
|
|
||||||
private int captureCount;
|
|
||||||
|
|
||||||
private boolean liveToken;
|
|
||||||
|
|
||||||
// Start/end offset of the current match:
|
|
||||||
private int matchStartOffset;
|
|
||||||
private int matchEndOffset;
|
|
||||||
|
|
||||||
// True once the input TokenStream is exhausted:
|
|
||||||
private boolean finished;
|
|
||||||
|
|
||||||
private int lookaheadNextRead;
|
|
||||||
private int lookaheadNextWrite;
|
|
||||||
|
|
||||||
private RollingBuffer<BufferedInputToken> lookahead = new RollingBuffer<BufferedInputToken>() {
|
|
||||||
@Override
|
|
||||||
protected BufferedInputToken newInstance() {
|
|
||||||
return new BufferedInputToken();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static class BufferedInputToken implements RollingBuffer.Resettable {
|
|
||||||
final CharsRefBuilder term = new CharsRefBuilder();
|
|
||||||
AttributeSource.State state;
|
|
||||||
int startOffset = -1;
|
|
||||||
int endOffset = -1;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
state = null;
|
|
||||||
term.clear();
|
|
||||||
|
|
||||||
// Intentionally invalid to ferret out bugs:
|
|
||||||
startOffset = -1;
|
|
||||||
endOffset = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class BufferedOutputToken {
|
|
||||||
final String term;
|
|
||||||
|
|
||||||
// Non-null if this was an incoming token:
|
|
||||||
final State state;
|
|
||||||
|
|
||||||
final int startNode;
|
|
||||||
final int endNode;
|
|
||||||
|
|
||||||
public BufferedOutputToken(State state, String term, int startNode, int endNode) {
|
|
||||||
this.state = state;
|
|
||||||
this.term = term;
|
|
||||||
this.startNode = startNode;
|
|
||||||
this.endNode = endNode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SynonymGraphFilter(TokenStream input, SynonymMap synonyms, boolean ignoreCase) {
|
|
||||||
super(input);
|
|
||||||
this.synonyms = synonyms;
|
|
||||||
this.fst = synonyms.fst;
|
|
||||||
if (fst == null) {
|
|
||||||
throw new IllegalArgumentException("fst must be non-null");
|
|
||||||
}
|
|
||||||
this.fstReader = fst.getBytesReader();
|
|
||||||
scratchArc = new FST.Arc<>();
|
|
||||||
this.ignoreCase = ignoreCase;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean incrementToken() throws IOException {
|
|
||||||
//System.out.println("\nS: incrToken lastNodeOut=" + lastNodeOut + " nextNodeOut=" + nextNodeOut);
|
|
||||||
|
|
||||||
assert lastNodeOut <= nextNodeOut;
|
|
||||||
|
|
||||||
if (outputBuffer.isEmpty() == false) {
|
|
||||||
// We still have pending outputs from a prior synonym match:
|
|
||||||
releaseBufferedToken();
|
|
||||||
//System.out.println(" syn: ret buffered=" + this);
|
|
||||||
assert liveToken == false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to parse a new synonym match at the current token:
|
|
||||||
|
|
||||||
if (parse()) {
|
|
||||||
// A new match was found:
|
|
||||||
releaseBufferedToken();
|
|
||||||
//System.out.println(" syn: after parse, ret buffered=" + this);
|
|
||||||
assert liveToken == false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lookaheadNextRead == lookaheadNextWrite) {
|
|
||||||
|
|
||||||
// Fast path: parse pulled one token, but it didn't match
|
|
||||||
// the start for any synonym, so we now return it "live" w/o having
|
|
||||||
// cloned all of its atts:
|
|
||||||
if (finished) {
|
|
||||||
//System.out.println(" syn: ret END");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert liveToken;
|
|
||||||
liveToken = false;
|
|
||||||
|
|
||||||
// NOTE: no need to change posInc since it's relative, i.e. whatever
|
|
||||||
// node our output is upto will just increase by the incoming posInc.
|
|
||||||
// We also don't need to change posLen, but only because we cannot
|
|
||||||
// consume a graph, so the incoming token can never span a future
|
|
||||||
// synonym match.
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// We still have buffered lookahead tokens from a previous
|
|
||||||
// parse attempt that required lookahead; just replay them now:
|
|
||||||
//System.out.println(" restore buffer");
|
|
||||||
assert lookaheadNextRead < lookaheadNextWrite : "read=" + lookaheadNextRead + " write=" + lookaheadNextWrite;
|
|
||||||
BufferedInputToken token = lookahead.get(lookaheadNextRead);
|
|
||||||
lookaheadNextRead++;
|
|
||||||
|
|
||||||
restoreState(token.state);
|
|
||||||
|
|
||||||
lookahead.freeBefore(lookaheadNextRead);
|
|
||||||
|
|
||||||
//System.out.println(" after restore offset=" + offsetAtt.startOffset() + "-" + offsetAtt.endOffset());
|
|
||||||
assert liveToken == false;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastNodeOut += posIncrAtt.getPositionIncrement();
|
|
||||||
nextNodeOut = lastNodeOut + posLenAtt.getPositionLength();
|
|
||||||
|
|
||||||
//System.out.println(" syn: ret lookahead=" + this);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void releaseBufferedToken() throws IOException {
|
|
||||||
//System.out.println(" releaseBufferedToken");
|
|
||||||
|
|
||||||
BufferedOutputToken token = outputBuffer.pollFirst();
|
|
||||||
|
|
||||||
if (token.state != null) {
|
|
||||||
// This is an original input token (keepOrig=true case):
|
|
||||||
//System.out.println(" hasState");
|
|
||||||
restoreState(token.state);
|
|
||||||
//System.out.println(" startOffset=" + offsetAtt.startOffset() + " endOffset=" + offsetAtt.endOffset());
|
|
||||||
} else {
|
|
||||||
clearAttributes();
|
|
||||||
//System.out.println(" no state");
|
|
||||||
termAtt.append(token.term);
|
|
||||||
|
|
||||||
// We better have a match already:
|
|
||||||
assert matchStartOffset != -1;
|
|
||||||
|
|
||||||
offsetAtt.setOffset(matchStartOffset, matchEndOffset);
|
|
||||||
//System.out.println(" startOffset=" + matchStartOffset + " endOffset=" + matchEndOffset);
|
|
||||||
typeAtt.setType(TYPE_SYNONYM);
|
|
||||||
}
|
|
||||||
|
|
||||||
//System.out.println(" lastNodeOut=" + lastNodeOut);
|
|
||||||
//System.out.println(" term=" + termAtt);
|
|
||||||
|
|
||||||
posIncrAtt.setPositionIncrement(token.startNode - lastNodeOut);
|
|
||||||
lastNodeOut = token.startNode;
|
|
||||||
posLenAtt.setPositionLength(token.endNode - token.startNode);
|
|
||||||
flagsAtt.setFlags(flagsAtt.getFlags() | GRAPH_FLAG); // set the graph flag
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scans the next input token(s) to see if a synonym matches. Returns true
|
|
||||||
* if a match was found.
|
|
||||||
*/
|
|
||||||
private boolean parse() throws IOException {
|
|
||||||
// System.out.println(Thread.currentThread().getName() + ": S: parse: " + System.identityHashCode(this));
|
|
||||||
|
|
||||||
// Holds the longest match we've seen so far:
|
|
||||||
BytesRef matchOutput = null;
|
|
||||||
int matchInputLength = 0;
|
|
||||||
|
|
||||||
BytesRef pendingOutput = fst.outputs.getNoOutput();
|
|
||||||
fst.getFirstArc(scratchArc);
|
|
||||||
|
|
||||||
assert scratchArc.output == fst.outputs.getNoOutput();
|
|
||||||
|
|
||||||
// How many tokens in the current match
|
|
||||||
int matchLength = 0;
|
|
||||||
boolean doFinalCapture = false;
|
|
||||||
|
|
||||||
int lookaheadUpto = lookaheadNextRead;
|
|
||||||
matchStartOffset = -1;
|
|
||||||
|
|
||||||
byToken:
|
|
||||||
while (true) {
|
|
||||||
//System.out.println(" cycle lookaheadUpto=" + lookaheadUpto + " maxPos=" + lookahead.getMaxPos());
|
|
||||||
|
|
||||||
// Pull next token's chars:
|
|
||||||
final char[] buffer;
|
|
||||||
final int bufferLen;
|
|
||||||
final int inputEndOffset;
|
|
||||||
|
|
||||||
if (lookaheadUpto <= lookahead.getMaxPos()) {
|
|
||||||
// Still in our lookahead buffer
|
|
||||||
BufferedInputToken token = lookahead.get(lookaheadUpto);
|
|
||||||
lookaheadUpto++;
|
|
||||||
buffer = token.term.chars();
|
|
||||||
bufferLen = token.term.length();
|
|
||||||
inputEndOffset = token.endOffset;
|
|
||||||
//System.out.println(" use buffer now max=" + lookahead.getMaxPos());
|
|
||||||
if (matchStartOffset == -1) {
|
|
||||||
matchStartOffset = token.startOffset;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
|
|
||||||
// We used up our lookahead buffer of input tokens
|
|
||||||
// -- pull next real input token:
|
|
||||||
|
|
||||||
assert finished || liveToken == false;
|
|
||||||
|
|
||||||
if (finished) {
|
|
||||||
//System.out.println(" break: finished");
|
|
||||||
break;
|
|
||||||
} else if (input.incrementToken()) {
|
|
||||||
//System.out.println(" input.incrToken");
|
|
||||||
liveToken = true;
|
|
||||||
buffer = termAtt.buffer();
|
|
||||||
bufferLen = termAtt.length();
|
|
||||||
if (matchStartOffset == -1) {
|
|
||||||
matchStartOffset = offsetAtt.startOffset();
|
|
||||||
}
|
|
||||||
inputEndOffset = offsetAtt.endOffset();
|
|
||||||
|
|
||||||
lookaheadUpto++;
|
|
||||||
} else {
|
|
||||||
// No more input tokens
|
|
||||||
finished = true;
|
|
||||||
//System.out.println(" break: now set finished");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
matchLength++;
|
|
||||||
//System.out.println(" cycle term=" + new String(buffer, 0, bufferLen));
|
|
||||||
|
|
||||||
// Run each char in this token through the FST:
|
|
||||||
int bufUpto = 0;
|
|
||||||
while (bufUpto < bufferLen) {
|
|
||||||
final int codePoint = Character.codePointAt(buffer, bufUpto, bufferLen);
|
|
||||||
if (fst.findTargetArc(ignoreCase ? Character.toLowerCase(codePoint) : codePoint, scratchArc, scratchArc, fstReader) ==
|
|
||||||
null) {
|
|
||||||
break byToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accum the output
|
|
||||||
pendingOutput = fst.outputs.add(pendingOutput, scratchArc.output);
|
|
||||||
bufUpto += Character.charCount(codePoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert bufUpto == bufferLen;
|
|
||||||
|
|
||||||
// OK, entire token matched; now see if this is a final
|
|
||||||
// state in the FST (a match):
|
|
||||||
if (scratchArc.isFinal()) {
|
|
||||||
matchOutput = fst.outputs.add(pendingOutput, scratchArc.nextFinalOutput);
|
|
||||||
matchInputLength = matchLength;
|
|
||||||
matchEndOffset = inputEndOffset;
|
|
||||||
//System.out.println(" ** match");
|
|
||||||
}
|
|
||||||
|
|
||||||
// See if the FST can continue matching (ie, needs to
|
|
||||||
// see the next input token):
|
|
||||||
if (fst.findTargetArc(SynonymMap.WORD_SEPARATOR, scratchArc, scratchArc, fstReader) == null) {
|
|
||||||
// No further rules can match here; we're done
|
|
||||||
// searching for matching rules starting at the
|
|
||||||
// current input position.
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
// More matching is possible -- accum the output (if
|
|
||||||
// any) of the WORD_SEP arc:
|
|
||||||
pendingOutput = fst.outputs.add(pendingOutput, scratchArc.output);
|
|
||||||
doFinalCapture = true;
|
|
||||||
if (liveToken) {
|
|
||||||
capture();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (doFinalCapture && liveToken && finished == false) {
|
|
||||||
// Must capture the final token if we captured any prior tokens:
|
|
||||||
capture();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (matchOutput != null) {
|
|
||||||
|
|
||||||
if (liveToken) {
|
|
||||||
// Single input token synonym; we must buffer it now:
|
|
||||||
capture();
|
|
||||||
}
|
|
||||||
|
|
||||||
// There is a match!
|
|
||||||
bufferOutputTokens(matchOutput, matchInputLength);
|
|
||||||
lookaheadNextRead += matchInputLength;
|
|
||||||
//System.out.println(" precmatch; set lookaheadNextRead=" + lookaheadNextRead + " now max=" + lookahead.getMaxPos());
|
|
||||||
lookahead.freeBefore(lookaheadNextRead);
|
|
||||||
//System.out.println(" match; set lookaheadNextRead=" + lookaheadNextRead + " now max=" + lookahead.getMaxPos());
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
//System.out.println(" no match; lookaheadNextRead=" + lookaheadNextRead);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//System.out.println(" parse done inputSkipCount=" + inputSkipCount + " nextRead=" + nextRead + " nextWrite=" + nextWrite);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expands the output graph into the necessary tokens, adding
|
|
||||||
* synonyms as side paths parallel to the input tokens, and
|
|
||||||
* buffers them in the output token buffer.
|
|
||||||
*/
|
|
||||||
private void bufferOutputTokens(BytesRef bytes, int matchInputLength) {
|
|
||||||
bytesReader.reset(bytes.bytes, bytes.offset, bytes.length);
|
|
||||||
|
|
||||||
final int code = bytesReader.readVInt();
|
|
||||||
final boolean keepOrig = (code & 0x1) == 0;
|
|
||||||
//System.out.println(" buffer: keepOrig=" + keepOrig + " matchInputLength=" + matchInputLength);
|
|
||||||
|
|
||||||
// How many nodes along all paths; we need this to assign the
|
|
||||||
// node ID for the final end node where all paths merge back:
|
|
||||||
int totalPathNodes;
|
|
||||||
if (keepOrig) {
|
|
||||||
assert matchInputLength > 0;
|
|
||||||
totalPathNodes = matchInputLength - 1;
|
|
||||||
} else {
|
|
||||||
totalPathNodes = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// How many synonyms we will insert over this match:
|
|
||||||
final int count = code >>> 1;
|
|
||||||
|
|
||||||
// TODO: we could encode this instead into the FST:
|
|
||||||
|
|
||||||
// 1st pass: count how many new nodes we need
|
|
||||||
List<List<String>> paths = new ArrayList<>();
|
|
||||||
for (int outputIDX = 0; outputIDX < count; outputIDX++) {
|
|
||||||
int wordID = bytesReader.readVInt();
|
|
||||||
synonyms.words.get(wordID, scratchBytes);
|
|
||||||
scratchChars.copyUTF8Bytes(scratchBytes);
|
|
||||||
int lastStart = 0;
|
|
||||||
|
|
||||||
List<String> path = new ArrayList<>();
|
|
||||||
paths.add(path);
|
|
||||||
int chEnd = scratchChars.length();
|
|
||||||
for (int chUpto = 0; chUpto <= chEnd; chUpto++) {
|
|
||||||
if (chUpto == chEnd || scratchChars.charAt(chUpto) == SynonymMap.WORD_SEPARATOR) {
|
|
||||||
path.add(new String(scratchChars.chars(), lastStart, chUpto - lastStart));
|
|
||||||
lastStart = 1 + chUpto;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert path.size() > 0;
|
|
||||||
totalPathNodes += path.size() - 1;
|
|
||||||
}
|
|
||||||
//System.out.println(" totalPathNodes=" + totalPathNodes);
|
|
||||||
|
|
||||||
// 2nd pass: buffer tokens for the graph fragment
|
|
||||||
|
|
||||||
// NOTE: totalPathNodes will be 0 in the case where the matched
|
|
||||||
// input is a single token and all outputs are also a single token
|
|
||||||
|
|
||||||
// We "spawn" a side-path for each of the outputs for this matched
|
|
||||||
// synonym, all ending back at this end node:
|
|
||||||
|
|
||||||
int startNode = nextNodeOut;
|
|
||||||
|
|
||||||
int endNode = startNode + totalPathNodes + 1;
|
|
||||||
//System.out.println(" " + paths.size() + " new side-paths");
|
|
||||||
|
|
||||||
// First, fanout all tokens departing start node for these new side paths:
|
|
||||||
int newNodeCount = 0;
|
|
||||||
for (List<String> path : paths) {
|
|
||||||
int pathEndNode;
|
|
||||||
//System.out.println(" path size=" + path.size());
|
|
||||||
if (path.size() == 1) {
|
|
||||||
// Single token output, so there are no intermediate nodes:
|
|
||||||
pathEndNode = endNode;
|
|
||||||
} else {
|
|
||||||
pathEndNode = nextNodeOut + newNodeCount + 1;
|
|
||||||
newNodeCount += path.size() - 1;
|
|
||||||
}
|
|
||||||
outputBuffer.add(new BufferedOutputToken(null, path.get(0), startNode, pathEndNode));
|
|
||||||
}
|
|
||||||
|
|
||||||
// We must do the original tokens last, else the offsets "go backwards":
|
|
||||||
if (keepOrig) {
|
|
||||||
BufferedInputToken token = lookahead.get(lookaheadNextRead);
|
|
||||||
int inputEndNode;
|
|
||||||
if (matchInputLength == 1) {
|
|
||||||
// Single token matched input, so there are no intermediate nodes:
|
|
||||||
inputEndNode = endNode;
|
|
||||||
} else {
|
|
||||||
inputEndNode = nextNodeOut + newNodeCount + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//System.out.println(" keepOrig first token: " + token.term);
|
|
||||||
|
|
||||||
outputBuffer.add(new BufferedOutputToken(token.state, token.term.toString(), startNode, inputEndNode));
|
|
||||||
}
|
|
||||||
|
|
||||||
nextNodeOut = endNode;
|
|
||||||
|
|
||||||
// Do full side-path for each syn output:
|
|
||||||
for (int pathID = 0; pathID < paths.size(); pathID++) {
|
|
||||||
List<String> path = paths.get(pathID);
|
|
||||||
if (path.size() > 1) {
|
|
||||||
int lastNode = outputBuffer.get(pathID).endNode;
|
|
||||||
for (int i = 1; i < path.size() - 1; i++) {
|
|
||||||
outputBuffer.add(new BufferedOutputToken(null, path.get(i), lastNode, lastNode + 1));
|
|
||||||
lastNode++;
|
|
||||||
}
|
|
||||||
outputBuffer.add(new BufferedOutputToken(null, path.get(path.size() - 1), lastNode, endNode));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keepOrig && matchInputLength > 1) {
|
|
||||||
// Do full "side path" with the original tokens:
|
|
||||||
int lastNode = outputBuffer.get(paths.size()).endNode;
|
|
||||||
for (int i = 1; i < matchInputLength - 1; i++) {
|
|
||||||
BufferedInputToken token = lookahead.get(lookaheadNextRead + i);
|
|
||||||
outputBuffer.add(new BufferedOutputToken(token.state, token.term.toString(), lastNode, lastNode + 1));
|
|
||||||
lastNode++;
|
|
||||||
}
|
|
||||||
BufferedInputToken token = lookahead.get(lookaheadNextRead + matchInputLength - 1);
|
|
||||||
outputBuffer.add(new BufferedOutputToken(token.state, token.term.toString(), lastNode, endNode));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
System.out.println(" after buffer: " + outputBuffer.size() + " tokens:");
|
|
||||||
for(BufferedOutputToken token : outputBuffer) {
|
|
||||||
System.out.println(" tok: " + token.term + " startNode=" + token.startNode + " endNode=" + token.endNode);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Buffers the current input token into lookahead buffer.
|
|
||||||
*/
|
|
||||||
private void capture() {
|
|
||||||
assert liveToken;
|
|
||||||
liveToken = false;
|
|
||||||
BufferedInputToken token = lookahead.get(lookaheadNextWrite);
|
|
||||||
lookaheadNextWrite++;
|
|
||||||
|
|
||||||
token.state = captureState();
|
|
||||||
token.startOffset = offsetAtt.startOffset();
|
|
||||||
token.endOffset = offsetAtt.endOffset();
|
|
||||||
assert token.term.length() == 0;
|
|
||||||
token.term.append(termAtt);
|
|
||||||
|
|
||||||
captureCount++;
|
|
||||||
maxLookaheadUsed = Math.max(maxLookaheadUsed, lookahead.getBufferSize());
|
|
||||||
//System.out.println(" maxLookaheadUsed=" + maxLookaheadUsed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() throws IOException {
|
|
||||||
super.reset();
|
|
||||||
lookahead.reset();
|
|
||||||
lookaheadNextWrite = 0;
|
|
||||||
lookaheadNextRead = 0;
|
|
||||||
captureCount = 0;
|
|
||||||
lastNodeOut = -1;
|
|
||||||
nextNodeOut = 0;
|
|
||||||
matchStartOffset = -1;
|
|
||||||
matchEndOffset = -1;
|
|
||||||
finished = false;
|
|
||||||
liveToken = false;
|
|
||||||
outputBuffer.clear();
|
|
||||||
maxLookaheadUsed = 0;
|
|
||||||
//System.out.println("S: reset");
|
|
||||||
}
|
|
||||||
|
|
||||||
// for testing
|
|
||||||
int getCaptureCount() {
|
|
||||||
return captureCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// for testing
|
|
||||||
int getMaxLookaheadUsed() {
|
|
||||||
return maxLookaheadUsed;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,115 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
||||||
* contributor license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright ownership.
|
|
||||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
||||||
* (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.apache.lucene.search;
|
|
||||||
|
|
||||||
import org.apache.lucene.index.IndexReader;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A query that wraps multiple sub-queries generated from a graph token stream.
|
|
||||||
*/
|
|
||||||
public final class GraphQuery extends Query {
|
|
||||||
private final Query[] queries;
|
|
||||||
private final boolean hasBoolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor sets the queries and checks if any of them are
|
|
||||||
* a boolean query.
|
|
||||||
*
|
|
||||||
* @param queries the non-null array of queries
|
|
||||||
*/
|
|
||||||
public GraphQuery(Query... queries) {
|
|
||||||
this.queries = Objects.requireNonNull(queries).clone();
|
|
||||||
for (Query query : queries) {
|
|
||||||
if (query instanceof BooleanQuery) {
|
|
||||||
hasBoolean = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hasBoolean = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the queries
|
|
||||||
*
|
|
||||||
* @return unmodifiable list of Query
|
|
||||||
*/
|
|
||||||
public List<Query> getQueries() {
|
|
||||||
return Collections.unmodifiableList(Arrays.asList(queries));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If there is at least one boolean query or not.
|
|
||||||
*
|
|
||||||
* @return true if there is a boolean, false if not
|
|
||||||
*/
|
|
||||||
public boolean hasBoolean() {
|
|
||||||
return hasBoolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rewrites to a single query or a boolean query where each query is a SHOULD clause.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public Query rewrite(IndexReader reader) throws IOException {
|
|
||||||
if (queries.length == 0) {
|
|
||||||
return new BooleanQuery.Builder().build();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (queries.length == 1) {
|
|
||||||
return queries[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
BooleanQuery.Builder q = new BooleanQuery.Builder();
|
|
||||||
q.setDisableCoord(true);
|
|
||||||
for (Query clause : queries) {
|
|
||||||
q.add(clause, BooleanClause.Occur.SHOULD);
|
|
||||||
}
|
|
||||||
|
|
||||||
return q.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(String field) {
|
|
||||||
StringBuilder builder = new StringBuilder("Graph(");
|
|
||||||
for (int i = 0; i < queries.length; i++) {
|
|
||||||
if (i != 0) {
|
|
||||||
builder.append(", ");
|
|
||||||
}
|
|
||||||
builder.append(Objects.toString(queries[i]));
|
|
||||||
}
|
|
||||||
builder.append(")");
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object other) {
|
|
||||||
return sameClassAs(other) &&
|
|
||||||
Arrays.equals(queries, ((GraphQuery) other).queries);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return 31 * classHash() + Arrays.hashCode(queries);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -28,8 +28,9 @@ import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.monitor.jvm.JvmInfo;
|
import org.elasticsearch.monitor.jvm.JvmInfo;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
public class Version {
|
public class Version implements Comparable<Version> {
|
||||||
/*
|
/*
|
||||||
* The logic for ID is: XXYYZZAA, where XX is major version, YY is minor version, ZZ is revision, and AA is alpha/beta/rc indicator AA
|
* The logic for ID is: XXYYZZAA, where XX is major version, YY is minor version, ZZ is revision, and AA is alpha/beta/rc indicator AA
|
||||||
* values below 25 are for alpha builder (since 5.0), and above 25 and below 50 are beta builds, and below 99 are RC builds, with 99
|
* values below 25 are for alpha builder (since 5.0), and above 25 and below 50 are beta builds, and below 99 are RC builds, with 99
|
||||||
|
@ -102,6 +103,8 @@ public class Version {
|
||||||
public static final Version V_5_1_2_UNRELEASED = new Version(V_5_1_2_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_3_0);
|
public static final Version V_5_1_2_UNRELEASED = new Version(V_5_1_2_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_3_0);
|
||||||
public static final int V_5_2_0_ID_UNRELEASED = 5020099;
|
public static final int V_5_2_0_ID_UNRELEASED = 5020099;
|
||||||
public static final Version V_5_2_0_UNRELEASED = new Version(V_5_2_0_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_3_0);
|
public static final Version V_5_2_0_UNRELEASED = new Version(V_5_2_0_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_3_0);
|
||||||
|
public static final int V_5_3_0_ID_UNRELEASED = 5030099;
|
||||||
|
public static final Version V_5_3_0_UNRELEASED = new Version(V_5_3_0_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_4_0);
|
||||||
public static final int V_6_0_0_alpha1_ID_UNRELEASED = 6000001;
|
public static final int V_6_0_0_alpha1_ID_UNRELEASED = 6000001;
|
||||||
public static final Version V_6_0_0_alpha1_UNRELEASED =
|
public static final Version V_6_0_0_alpha1_UNRELEASED =
|
||||||
new Version(V_6_0_0_alpha1_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_4_0);
|
new Version(V_6_0_0_alpha1_ID_UNRELEASED, org.apache.lucene.util.Version.LUCENE_6_4_0);
|
||||||
|
@ -122,6 +125,8 @@ public class Version {
|
||||||
switch (id) {
|
switch (id) {
|
||||||
case V_6_0_0_alpha1_ID_UNRELEASED:
|
case V_6_0_0_alpha1_ID_UNRELEASED:
|
||||||
return V_6_0_0_alpha1_UNRELEASED;
|
return V_6_0_0_alpha1_UNRELEASED;
|
||||||
|
case V_5_3_0_ID_UNRELEASED:
|
||||||
|
return V_5_3_0_UNRELEASED;
|
||||||
case V_5_2_0_ID_UNRELEASED:
|
case V_5_2_0_ID_UNRELEASED:
|
||||||
return V_5_2_0_UNRELEASED;
|
return V_5_2_0_UNRELEASED;
|
||||||
case V_5_1_2_ID_UNRELEASED:
|
case V_5_1_2_ID_UNRELEASED:
|
||||||
|
@ -310,6 +315,11 @@ public class Version {
|
||||||
return version.id >= id;
|
return version.id >= id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Version other) {
|
||||||
|
return Integer.compare(this.id, other.id);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the minimum compatible version based on the current
|
* Returns the minimum compatible version based on the current
|
||||||
* version. Ie a node needs to have at least the return version in order
|
* version. Ie a node needs to have at least the return version in order
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
package org.elasticsearch.action.admin.cluster.allocation;
|
package org.elasticsearch.action.admin.cluster.allocation;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchParseException;
|
import org.elasticsearch.ElasticsearchParseException;
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.ActionRequestValidationException;
|
import org.elasticsearch.action.ActionRequestValidationException;
|
||||||
import org.elasticsearch.action.support.master.MasterNodeRequest;
|
import org.elasticsearch.action.support.master.MasterNodeRequest;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
|
@ -46,30 +47,42 @@ public class ClusterAllocationExplainRequest extends MasterNodeRequest<ClusterAl
|
||||||
PARSER.declareString(ClusterAllocationExplainRequest::setIndex, new ParseField("index"));
|
PARSER.declareString(ClusterAllocationExplainRequest::setIndex, new ParseField("index"));
|
||||||
PARSER.declareInt(ClusterAllocationExplainRequest::setShard, new ParseField("shard"));
|
PARSER.declareInt(ClusterAllocationExplainRequest::setShard, new ParseField("shard"));
|
||||||
PARSER.declareBoolean(ClusterAllocationExplainRequest::setPrimary, new ParseField("primary"));
|
PARSER.declareBoolean(ClusterAllocationExplainRequest::setPrimary, new ParseField("primary"));
|
||||||
|
PARSER.declareString(ClusterAllocationExplainRequest::setCurrentNode, new ParseField("current_node"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private String index;
|
private String index;
|
||||||
|
@Nullable
|
||||||
private Integer shard;
|
private Integer shard;
|
||||||
|
@Nullable
|
||||||
private Boolean primary;
|
private Boolean primary;
|
||||||
|
@Nullable
|
||||||
|
private String currentNode;
|
||||||
private boolean includeYesDecisions = false;
|
private boolean includeYesDecisions = false;
|
||||||
private boolean includeDiskInfo = false;
|
private boolean includeDiskInfo = false;
|
||||||
|
|
||||||
/** Explain the first unassigned shard */
|
/**
|
||||||
|
* Create a new allocation explain request to explain any unassigned shard in the cluster.
|
||||||
|
*/
|
||||||
public ClusterAllocationExplainRequest() {
|
public ClusterAllocationExplainRequest() {
|
||||||
this.index = null;
|
this.index = null;
|
||||||
this.shard = null;
|
this.shard = null;
|
||||||
this.primary = null;
|
this.primary = null;
|
||||||
|
this.currentNode = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new allocation explain request. If {@code primary} is false, the first unassigned replica
|
* Create a new allocation explain request. If {@code primary} is false, the first unassigned replica
|
||||||
* will be picked for explanation. If no replicas are unassigned, the first assigned replica will
|
* will be picked for explanation. If no replicas are unassigned, the first assigned replica will
|
||||||
* be explained.
|
* be explained.
|
||||||
|
*
|
||||||
|
* Package private for testing.
|
||||||
*/
|
*/
|
||||||
public ClusterAllocationExplainRequest(String index, int shard, boolean primary) {
|
ClusterAllocationExplainRequest(String index, int shard, boolean primary, @Nullable String currentNode) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
this.shard = shard;
|
this.shard = shard;
|
||||||
this.primary = primary;
|
this.primary = primary;
|
||||||
|
this.currentNode = currentNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -93,54 +106,103 @@ public class ClusterAllocationExplainRequest extends MasterNodeRequest<ClusterAl
|
||||||
* Returns {@code true} iff the first unassigned shard is to be used
|
* Returns {@code true} iff the first unassigned shard is to be used
|
||||||
*/
|
*/
|
||||||
public boolean useAnyUnassignedShard() {
|
public boolean useAnyUnassignedShard() {
|
||||||
return this.index == null && this.shard == null && this.primary == null;
|
return this.index == null && this.shard == null && this.primary == null && this.currentNode == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the index name of the shard to explain.
|
||||||
|
*/
|
||||||
public ClusterAllocationExplainRequest setIndex(String index) {
|
public ClusterAllocationExplainRequest setIndex(String index) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the index name of the shard to explain, or {@code null} to use any unassigned shard (see {@link #useAnyUnassignedShard()}).
|
||||||
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getIndex() {
|
public String getIndex() {
|
||||||
return this.index;
|
return this.index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the shard id of the shard to explain.
|
||||||
|
*/
|
||||||
public ClusterAllocationExplainRequest setShard(Integer shard) {
|
public ClusterAllocationExplainRequest setShard(Integer shard) {
|
||||||
this.shard = shard;
|
this.shard = shard;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the shard id of the shard to explain, or {@code null} to use any unassigned shard (see {@link #useAnyUnassignedShard()}).
|
||||||
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public Integer getShard() {
|
public Integer getShard() {
|
||||||
return this.shard;
|
return this.shard;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to explain the allocation of the primary shard or a replica shard copy
|
||||||
|
* for the shard id (see {@link #getShard()}).
|
||||||
|
*/
|
||||||
public ClusterAllocationExplainRequest setPrimary(Boolean primary) {
|
public ClusterAllocationExplainRequest setPrimary(Boolean primary) {
|
||||||
this.primary = primary;
|
this.primary = primary;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if explaining the primary shard for the shard id (see {@link #getShard()}),
|
||||||
|
* {@code false} if explaining a replica shard copy for the shard id, or {@code null} to use any
|
||||||
|
* unassigned shard (see {@link #useAnyUnassignedShard()}).
|
||||||
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public Boolean isPrimary() {
|
public Boolean isPrimary() {
|
||||||
return this.primary;
|
return this.primary;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests the explain API to explain an already assigned replica shard currently allocated to
|
||||||
|
* the given node.
|
||||||
|
*/
|
||||||
|
public ClusterAllocationExplainRequest setCurrentNode(String currentNodeId) {
|
||||||
|
this.currentNode = currentNodeId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the node holding the replica shard to be explained. Returns {@code null} if any replica shard
|
||||||
|
* can be explained.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public String getCurrentNode() {
|
||||||
|
return currentNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set to {@code true} to include yes decisions for a particular node.
|
||||||
|
*/
|
||||||
public void includeYesDecisions(boolean includeYesDecisions) {
|
public void includeYesDecisions(boolean includeYesDecisions) {
|
||||||
this.includeYesDecisions = includeYesDecisions;
|
this.includeYesDecisions = includeYesDecisions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns true if all decisions should be included. Otherwise only "NO" and "THROTTLE" decisions are returned */
|
/**
|
||||||
|
* Returns {@code true} if yes decisions should be included. Otherwise only "no" and "throttle"
|
||||||
|
* decisions are returned.
|
||||||
|
*/
|
||||||
public boolean includeYesDecisions() {
|
public boolean includeYesDecisions() {
|
||||||
return this.includeYesDecisions;
|
return this.includeYesDecisions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@code true} to include information about the gathered disk information of nodes in the cluster */
|
/**
|
||||||
|
* Set to {@code true} to include information about the gathered disk information of nodes in the cluster.
|
||||||
|
*/
|
||||||
public void includeDiskInfo(boolean includeDiskInfo) {
|
public void includeDiskInfo(boolean includeDiskInfo) {
|
||||||
this.includeDiskInfo = includeDiskInfo;
|
this.includeDiskInfo = includeDiskInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns true if information about disk usage and shard sizes should also be returned */
|
/**
|
||||||
|
* Returns {@code true} if information about disk usage and shard sizes should also be returned.
|
||||||
|
*/
|
||||||
public boolean includeDiskInfo() {
|
public boolean includeDiskInfo() {
|
||||||
return this.includeDiskInfo;
|
return this.includeDiskInfo;
|
||||||
}
|
}
|
||||||
|
@ -154,6 +216,9 @@ public class ClusterAllocationExplainRequest extends MasterNodeRequest<ClusterAl
|
||||||
sb.append("index=").append(index);
|
sb.append("index=").append(index);
|
||||||
sb.append(",shard=").append(shard);
|
sb.append(",shard=").append(shard);
|
||||||
sb.append(",primary?=").append(primary);
|
sb.append(",primary?=").append(primary);
|
||||||
|
if (currentNode != null) {
|
||||||
|
sb.append(",currentNode=").append(currentNode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sb.append(",includeYesDecisions?=").append(includeYesDecisions);
|
sb.append(",includeYesDecisions?=").append(includeYesDecisions);
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
|
@ -170,21 +235,32 @@ public class ClusterAllocationExplainRequest extends MasterNodeRequest<ClusterAl
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readFrom(StreamInput in) throws IOException {
|
public void readFrom(StreamInput in) throws IOException {
|
||||||
|
checkVersion(in.getVersion());
|
||||||
super.readFrom(in);
|
super.readFrom(in);
|
||||||
this.index = in.readOptionalString();
|
this.index = in.readOptionalString();
|
||||||
this.shard = in.readOptionalVInt();
|
this.shard = in.readOptionalVInt();
|
||||||
this.primary = in.readOptionalBoolean();
|
this.primary = in.readOptionalBoolean();
|
||||||
|
this.currentNode = in.readOptionalString();
|
||||||
this.includeYesDecisions = in.readBoolean();
|
this.includeYesDecisions = in.readBoolean();
|
||||||
this.includeDiskInfo = in.readBoolean();
|
this.includeDiskInfo = in.readBoolean();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
|
checkVersion(out.getVersion());
|
||||||
super.writeTo(out);
|
super.writeTo(out);
|
||||||
out.writeOptionalString(index);
|
out.writeOptionalString(index);
|
||||||
out.writeOptionalVInt(shard);
|
out.writeOptionalVInt(shard);
|
||||||
out.writeOptionalBoolean(primary);
|
out.writeOptionalBoolean(primary);
|
||||||
|
out.writeOptionalString(currentNode);
|
||||||
out.writeBoolean(includeYesDecisions);
|
out.writeBoolean(includeYesDecisions);
|
||||||
out.writeBoolean(includeDiskInfo);
|
out.writeBoolean(includeDiskInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkVersion(Version version) {
|
||||||
|
if (version.before(Version.V_5_2_0_UNRELEASED)) {
|
||||||
|
throw new IllegalArgumentException("cannot explain shards in a mixed-cluster with pre-" + Version.V_5_2_0_UNRELEASED +
|
||||||
|
" nodes, node version [" + version + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
|
|
||||||
package org.elasticsearch.action.admin.cluster.allocation;
|
package org.elasticsearch.action.admin.cluster.allocation;
|
||||||
|
|
||||||
import org.elasticsearch.action.support.IndicesOptions;
|
|
||||||
import org.elasticsearch.action.support.master.MasterNodeOperationRequestBuilder;
|
import org.elasticsearch.action.support.master.MasterNodeOperationRequestBuilder;
|
||||||
import org.elasticsearch.client.ElasticsearchClient;
|
import org.elasticsearch.client.ElasticsearchClient;
|
||||||
|
|
||||||
|
@ -65,6 +64,15 @@ public class ClusterAllocationExplainRequestBuilder
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests the explain API to explain an already assigned replica shard currently allocated to
|
||||||
|
* the given node.
|
||||||
|
*/
|
||||||
|
public ClusterAllocationExplainRequestBuilder setCurrentNode(String currentNode) {
|
||||||
|
request.setCurrentNode(currentNode);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signal that the first unassigned shard should be used
|
* Signal that the first unassigned shard should be used
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
package org.elasticsearch.action.admin.cluster.allocation;
|
package org.elasticsearch.action.admin.cluster.allocation;
|
||||||
|
|
||||||
import org.elasticsearch.action.ActionResponse;
|
import org.elasticsearch.action.ActionResponse;
|
||||||
import org.elasticsearch.cluster.ClusterName;
|
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
|
|
||||||
|
|
|
@ -21,285 +21,184 @@ package org.elasticsearch.action.admin.cluster.allocation;
|
||||||
|
|
||||||
import org.elasticsearch.cluster.ClusterInfo;
|
import org.elasticsearch.cluster.ClusterInfo;
|
||||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||||
|
import org.elasticsearch.cluster.routing.ShardRouting;
|
||||||
|
import org.elasticsearch.cluster.routing.ShardRoutingState;
|
||||||
import org.elasticsearch.cluster.routing.UnassignedInfo;
|
import org.elasticsearch.cluster.routing.UnassignedInfo;
|
||||||
|
import org.elasticsearch.cluster.routing.allocation.AllocationDecision;
|
||||||
|
import org.elasticsearch.cluster.routing.allocation.ShardAllocationDecision;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.common.io.stream.Writeable;
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.index.shard.ShardId;
|
import org.elasticsearch.index.shard.ShardId;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
|
||||||
|
import static org.elasticsearch.cluster.routing.allocation.AbstractAllocationDecision.discoveryNodeToXContent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@code ClusterAllocationExplanation} is an explanation of why a shard may or may not be allocated to nodes. It also includes weights
|
* A {@code ClusterAllocationExplanation} is an explanation of why a shard is unassigned,
|
||||||
* for where the shard is likely to be assigned. It is an immutable class
|
* or if it is not unassigned, then which nodes it could possibly be relocated to.
|
||||||
|
* It is an immutable class.
|
||||||
*/
|
*/
|
||||||
public final class ClusterAllocationExplanation implements ToXContent, Writeable {
|
public final class ClusterAllocationExplanation implements ToXContent, Writeable {
|
||||||
|
|
||||||
private final ShardId shard;
|
private final ShardRouting shardRouting;
|
||||||
private final boolean primary;
|
private final DiscoveryNode currentNode;
|
||||||
private final boolean hasPendingAsyncFetch;
|
private final DiscoveryNode relocationTargetNode;
|
||||||
private final String assignedNodeId;
|
|
||||||
private final UnassignedInfo unassignedInfo;
|
|
||||||
private final long allocationDelayMillis;
|
|
||||||
private final long remainingDelayMillis;
|
|
||||||
private final Map<DiscoveryNode, NodeExplanation> nodeExplanations;
|
|
||||||
private final ClusterInfo clusterInfo;
|
private final ClusterInfo clusterInfo;
|
||||||
|
private final ShardAllocationDecision shardAllocationDecision;
|
||||||
|
|
||||||
public ClusterAllocationExplanation(ShardId shard, boolean primary, @Nullable String assignedNodeId, long allocationDelayMillis,
|
public ClusterAllocationExplanation(ShardRouting shardRouting, @Nullable DiscoveryNode currentNode,
|
||||||
long remainingDelayMillis, @Nullable UnassignedInfo unassignedInfo, boolean hasPendingAsyncFetch,
|
@Nullable DiscoveryNode relocationTargetNode, @Nullable ClusterInfo clusterInfo,
|
||||||
Map<DiscoveryNode, NodeExplanation> nodeExplanations, @Nullable ClusterInfo clusterInfo) {
|
ShardAllocationDecision shardAllocationDecision) {
|
||||||
this.shard = shard;
|
this.shardRouting = shardRouting;
|
||||||
this.primary = primary;
|
this.currentNode = currentNode;
|
||||||
this.hasPendingAsyncFetch = hasPendingAsyncFetch;
|
this.relocationTargetNode = relocationTargetNode;
|
||||||
this.assignedNodeId = assignedNodeId;
|
|
||||||
this.unassignedInfo = unassignedInfo;
|
|
||||||
this.allocationDelayMillis = allocationDelayMillis;
|
|
||||||
this.remainingDelayMillis = remainingDelayMillis;
|
|
||||||
this.nodeExplanations = nodeExplanations;
|
|
||||||
this.clusterInfo = clusterInfo;
|
this.clusterInfo = clusterInfo;
|
||||||
|
this.shardAllocationDecision = shardAllocationDecision;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClusterAllocationExplanation(StreamInput in) throws IOException {
|
public ClusterAllocationExplanation(StreamInput in) throws IOException {
|
||||||
this.shard = ShardId.readShardId(in);
|
this.shardRouting = new ShardRouting(in);
|
||||||
this.primary = in.readBoolean();
|
this.currentNode = in.readOptionalWriteable(DiscoveryNode::new);
|
||||||
this.hasPendingAsyncFetch = in.readBoolean();
|
this.relocationTargetNode = in.readOptionalWriteable(DiscoveryNode::new);
|
||||||
this.assignedNodeId = in.readOptionalString();
|
this.clusterInfo = in.readOptionalWriteable(ClusterInfo::new);
|
||||||
this.unassignedInfo = in.readOptionalWriteable(UnassignedInfo::new);
|
this.shardAllocationDecision = new ShardAllocationDecision(in);
|
||||||
this.allocationDelayMillis = in.readVLong();
|
|
||||||
this.remainingDelayMillis = in.readVLong();
|
|
||||||
|
|
||||||
int mapSize = in.readVInt();
|
|
||||||
Map<DiscoveryNode, NodeExplanation> nodeToExplanation = new HashMap<>(mapSize);
|
|
||||||
for (int i = 0; i < mapSize; i++) {
|
|
||||||
NodeExplanation nodeExplanation = new NodeExplanation(in);
|
|
||||||
nodeToExplanation.put(nodeExplanation.getNode(), nodeExplanation);
|
|
||||||
}
|
|
||||||
this.nodeExplanations = nodeToExplanation;
|
|
||||||
if (in.readBoolean()) {
|
|
||||||
this.clusterInfo = new ClusterInfo(in);
|
|
||||||
} else {
|
|
||||||
this.clusterInfo = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
this.getShard().writeTo(out);
|
shardRouting.writeTo(out);
|
||||||
out.writeBoolean(this.isPrimary());
|
out.writeOptionalWriteable(currentNode);
|
||||||
out.writeBoolean(this.isStillFetchingShardData());
|
out.writeOptionalWriteable(relocationTargetNode);
|
||||||
out.writeOptionalString(this.getAssignedNodeId());
|
out.writeOptionalWriteable(clusterInfo);
|
||||||
out.writeOptionalWriteable(this.getUnassignedInfo());
|
shardAllocationDecision.writeTo(out);
|
||||||
out.writeVLong(allocationDelayMillis);
|
|
||||||
out.writeVLong(remainingDelayMillis);
|
|
||||||
|
|
||||||
out.writeVInt(this.nodeExplanations.size());
|
|
||||||
for (NodeExplanation explanation : this.nodeExplanations.values()) {
|
|
||||||
explanation.writeTo(out);
|
|
||||||
}
|
|
||||||
if (this.clusterInfo != null) {
|
|
||||||
out.writeBoolean(true);
|
|
||||||
this.clusterInfo.writeTo(out);
|
|
||||||
} else {
|
|
||||||
out.writeBoolean(false);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the shard that the explanation is about */
|
/**
|
||||||
|
* Returns the shard that the explanation is about.
|
||||||
|
*/
|
||||||
public ShardId getShard() {
|
public ShardId getShard() {
|
||||||
return this.shard;
|
return shardRouting.shardId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return true if the explained shard is primary, false otherwise */
|
/**
|
||||||
|
* Returns {@code true} if the explained shard is primary, {@code false} otherwise.
|
||||||
|
*/
|
||||||
public boolean isPrimary() {
|
public boolean isPrimary() {
|
||||||
return this.primary;
|
return shardRouting.primary();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return turn if shard data is still being fetched for the allocation */
|
/**
|
||||||
public boolean isStillFetchingShardData() {
|
* Returns the current {@link ShardRoutingState} of the shard.
|
||||||
return this.hasPendingAsyncFetch;
|
*/
|
||||||
|
public ShardRoutingState getShardState() {
|
||||||
|
return shardRouting.state();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return turn if the shard is assigned to a node */
|
/**
|
||||||
public boolean isAssigned() {
|
* Returns the currently assigned node, or {@code null} if the shard is unassigned.
|
||||||
return this.assignedNodeId != null;
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
/** Return the assigned node id or null if not assigned */
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getAssignedNodeId() {
|
public DiscoveryNode getCurrentNode() {
|
||||||
return this.assignedNodeId;
|
return currentNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the unassigned info for the shard or null if the shard is assigned */
|
/**
|
||||||
|
* Returns the relocating target node, or {@code null} if the shard is not in the {@link ShardRoutingState#RELOCATING} state.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public DiscoveryNode getRelocationTargetNode() {
|
||||||
|
return relocationTargetNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the unassigned info for the shard, or {@code null} if the shard is active.
|
||||||
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public UnassignedInfo getUnassignedInfo() {
|
public UnassignedInfo getUnassignedInfo() {
|
||||||
return this.unassignedInfo;
|
return shardRouting.unassignedInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Return the configured delay before the shard can be allocated in milliseconds */
|
/**
|
||||||
public long getAllocationDelayMillis() {
|
* Returns the cluster disk info for the cluster, or {@code null} if none available.
|
||||||
return this.allocationDelayMillis;
|
*/
|
||||||
}
|
|
||||||
|
|
||||||
/** Return the remaining allocation delay for this shard in milliseconds */
|
|
||||||
public long getRemainingDelayMillis() {
|
|
||||||
return this.remainingDelayMillis;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return a map of node to the explanation for that node */
|
|
||||||
public Map<DiscoveryNode, NodeExplanation> getNodeExplanations() {
|
|
||||||
return this.nodeExplanations;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Return the cluster disk info for the cluster or null if none available */
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public ClusterInfo getClusterInfo() {
|
public ClusterInfo getClusterInfo() {
|
||||||
return this.clusterInfo;
|
return this.clusterInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** \
|
||||||
|
* Returns the shard allocation decision for attempting to assign or move the shard.
|
||||||
|
*/
|
||||||
|
public ShardAllocationDecision getShardAllocationDecision() {
|
||||||
|
return shardAllocationDecision;
|
||||||
|
}
|
||||||
|
|
||||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
builder.startObject(); {
|
builder.startObject(); {
|
||||||
builder.startObject("shard"); {
|
builder.field("index", shardRouting.getIndexName());
|
||||||
builder.field("index", shard.getIndexName());
|
builder.field("shard", shardRouting.getId());
|
||||||
builder.field("index_uuid", shard.getIndex().getUUID());
|
builder.field("primary", shardRouting.primary());
|
||||||
builder.field("id", shard.getId());
|
builder.field("current_state", shardRouting.state().toString().toLowerCase(Locale.ROOT));
|
||||||
builder.field("primary", primary);
|
if (shardRouting.unassignedInfo() != null) {
|
||||||
|
unassignedInfoToXContent(shardRouting.unassignedInfo(), builder);
|
||||||
}
|
}
|
||||||
builder.endObject(); // end shard
|
if (currentNode != null) {
|
||||||
builder.field("assigned", this.assignedNodeId != null);
|
builder.startObject("current_node");
|
||||||
// If assigned, show the node id of the node it's assigned to
|
{
|
||||||
if (assignedNodeId != null) {
|
discoveryNodeToXContent(currentNode, true, builder);
|
||||||
builder.field("assigned_node_id", this.assignedNodeId);
|
if (shardAllocationDecision.getMoveDecision().isDecisionTaken()
|
||||||
}
|
&& shardAllocationDecision.getMoveDecision().getCurrentNodeRanking() > 0) {
|
||||||
builder.field("shard_state_fetch_pending", this.hasPendingAsyncFetch);
|
builder.field("weight_ranking", shardAllocationDecision.getMoveDecision().getCurrentNodeRanking());
|
||||||
// If we have unassigned info, show that
|
|
||||||
if (unassignedInfo != null) {
|
|
||||||
unassignedInfo.toXContent(builder, params);
|
|
||||||
builder.timeValueField("allocation_delay_in_millis", "allocation_delay", TimeValue.timeValueMillis(allocationDelayMillis));
|
|
||||||
builder.timeValueField("remaining_delay_in_millis", "remaining_delay", TimeValue.timeValueMillis(remainingDelayMillis));
|
|
||||||
}
|
|
||||||
builder.startObject("nodes"); {
|
|
||||||
for (NodeExplanation explanation : nodeExplanations.values()) {
|
|
||||||
explanation.toXContent(builder, params);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
builder.endObject(); // end nodes
|
builder.endObject();
|
||||||
|
}
|
||||||
if (this.clusterInfo != null) {
|
if (this.clusterInfo != null) {
|
||||||
builder.startObject("cluster_info"); {
|
builder.startObject("cluster_info"); {
|
||||||
this.clusterInfo.toXContent(builder, params);
|
this.clusterInfo.toXContent(builder, params);
|
||||||
}
|
}
|
||||||
builder.endObject(); // end "cluster_info"
|
builder.endObject(); // end "cluster_info"
|
||||||
}
|
}
|
||||||
|
if (shardAllocationDecision.isDecisionTaken()) {
|
||||||
|
shardAllocationDecision.toXContent(builder, params);
|
||||||
|
} else {
|
||||||
|
String explanation;
|
||||||
|
if (shardRouting.state() == ShardRoutingState.RELOCATING) {
|
||||||
|
explanation = "the shard is in the process of relocating from node [" + currentNode.getName() + "] " +
|
||||||
|
"to node [" + relocationTargetNode.getName() + "], wait until relocation has completed";
|
||||||
|
} else {
|
||||||
|
assert shardRouting.state() == ShardRoutingState.INITIALIZING;
|
||||||
|
explanation = "the shard is in the process of initializing on node [" + currentNode.getName() + "], " +
|
||||||
|
"wait until initialization has completed";
|
||||||
|
}
|
||||||
|
builder.field("explanation", explanation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
builder.endObject(); // end wrapping object
|
builder.endObject(); // end wrapping object
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** An Enum representing the final decision for a shard allocation on a node */
|
private XContentBuilder unassignedInfoToXContent(UnassignedInfo unassignedInfo, XContentBuilder builder)
|
||||||
public enum FinalDecision {
|
throws IOException {
|
||||||
// Yes, the shard can be assigned
|
|
||||||
YES((byte) 0),
|
|
||||||
// No, the shard cannot be assigned
|
|
||||||
NO((byte) 1),
|
|
||||||
// The shard is already assigned to this node
|
|
||||||
ALREADY_ASSIGNED((byte) 2);
|
|
||||||
|
|
||||||
private final byte id;
|
builder.startObject("unassigned_info");
|
||||||
|
builder.field("reason", unassignedInfo.getReason());
|
||||||
FinalDecision (byte id) {
|
builder.field("at", UnassignedInfo.DATE_TIME_FORMATTER.printer().print(unassignedInfo.getUnassignedTimeInMillis()));
|
||||||
this.id = id;
|
if (unassignedInfo.getNumFailedAllocations() > 0) {
|
||||||
|
builder.field("failed_allocation_attempts", unassignedInfo.getNumFailedAllocations());
|
||||||
}
|
}
|
||||||
|
String details = unassignedInfo.getDetails();
|
||||||
private static FinalDecision fromId(byte id) {
|
if (details != null) {
|
||||||
switch (id) {
|
builder.field("details", details);
|
||||||
case 0: return YES;
|
|
||||||
case 1: return NO;
|
|
||||||
case 2: return ALREADY_ASSIGNED;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown id for final decision: [" + id + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
switch (id) {
|
|
||||||
case 0: return "YES";
|
|
||||||
case 1: return "NO";
|
|
||||||
case 2: return "ALREADY_ASSIGNED";
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown id for final decision: [" + id + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static FinalDecision readFrom(StreamInput in) throws IOException {
|
|
||||||
return fromId(in.readByte());
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeTo(StreamOutput out) throws IOException {
|
|
||||||
out.writeByte(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** An Enum representing the state of the shard store's copy of the data on a node */
|
|
||||||
public enum StoreCopy {
|
|
||||||
// No data for this shard is on the node
|
|
||||||
NONE((byte) 0),
|
|
||||||
// A copy of the data is available on this node
|
|
||||||
AVAILABLE((byte) 1),
|
|
||||||
// The copy of the data on the node is corrupt
|
|
||||||
CORRUPT((byte) 2),
|
|
||||||
// There was an error reading this node's copy of the data
|
|
||||||
IO_ERROR((byte) 3),
|
|
||||||
// The copy of the data on the node is stale
|
|
||||||
STALE((byte) 4),
|
|
||||||
// It's unknown what the copy of the data is
|
|
||||||
UNKNOWN((byte) 5);
|
|
||||||
|
|
||||||
private final byte id;
|
|
||||||
|
|
||||||
StoreCopy (byte id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StoreCopy fromId(byte id) {
|
|
||||||
switch (id) {
|
|
||||||
case 0: return NONE;
|
|
||||||
case 1: return AVAILABLE;
|
|
||||||
case 2: return CORRUPT;
|
|
||||||
case 3: return IO_ERROR;
|
|
||||||
case 4: return STALE;
|
|
||||||
case 5: return UNKNOWN;
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown id for store copy: [" + id + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
switch (id) {
|
|
||||||
case 0: return "NONE";
|
|
||||||
case 1: return "AVAILABLE";
|
|
||||||
case 2: return "CORRUPT";
|
|
||||||
case 3: return "IO_ERROR";
|
|
||||||
case 4: return "STALE";
|
|
||||||
case 5: return "UNKNOWN";
|
|
||||||
default:
|
|
||||||
throw new IllegalArgumentException("unknown id for store copy: [" + id + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static StoreCopy readFrom(StreamInput in) throws IOException {
|
|
||||||
return fromId(in.readByte());
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeTo(StreamOutput out) throws IOException {
|
|
||||||
out.writeByte(id);
|
|
||||||
}
|
}
|
||||||
|
builder.field("last_allocation_status", AllocationDecision.fromAllocationStatus(unassignedInfo.getLastAllocationStatus()));
|
||||||
|
builder.endObject();
|
||||||
|
return builder;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,147 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed to Elasticsearch under one or more contributor
|
|
||||||
* license agreements. See the NOTICE file distributed with
|
|
||||||
* this work for additional information regarding copyright
|
|
||||||
* ownership. Elasticsearch licenses this file to you under
|
|
||||||
* the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
* not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.elasticsearch.action.admin.cluster.allocation;
|
|
||||||
|
|
||||||
import org.elasticsearch.ExceptionsHelper;
|
|
||||||
import org.elasticsearch.action.admin.indices.shards.IndicesShardStoresResponse;
|
|
||||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.Decision;
|
|
||||||
import org.elasticsearch.common.Nullable;
|
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
|
||||||
import org.elasticsearch.common.io.stream.Writeable;
|
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Map;
|
|
||||||
/** The cluster allocation explanation for a single node */
|
|
||||||
public class NodeExplanation implements Writeable, ToXContent {
|
|
||||||
private final DiscoveryNode node;
|
|
||||||
private final Decision nodeDecision;
|
|
||||||
private final Float nodeWeight;
|
|
||||||
private final IndicesShardStoresResponse.StoreStatus storeStatus;
|
|
||||||
private final ClusterAllocationExplanation.FinalDecision finalDecision;
|
|
||||||
private final ClusterAllocationExplanation.StoreCopy storeCopy;
|
|
||||||
private final String finalExplanation;
|
|
||||||
|
|
||||||
public NodeExplanation(final DiscoveryNode node, final Decision nodeDecision, final Float nodeWeight,
|
|
||||||
@Nullable final IndicesShardStoresResponse.StoreStatus storeStatus,
|
|
||||||
final ClusterAllocationExplanation.FinalDecision finalDecision,
|
|
||||||
final String finalExplanation,
|
|
||||||
final ClusterAllocationExplanation.StoreCopy storeCopy) {
|
|
||||||
this.node = node;
|
|
||||||
this.nodeDecision = nodeDecision;
|
|
||||||
this.nodeWeight = nodeWeight;
|
|
||||||
this.storeStatus = storeStatus;
|
|
||||||
this.finalDecision = finalDecision;
|
|
||||||
this.finalExplanation = finalExplanation;
|
|
||||||
this.storeCopy = storeCopy;
|
|
||||||
}
|
|
||||||
|
|
||||||
public NodeExplanation(StreamInput in) throws IOException {
|
|
||||||
this.node = new DiscoveryNode(in);
|
|
||||||
this.nodeDecision = Decision.readFrom(in);
|
|
||||||
this.nodeWeight = in.readFloat();
|
|
||||||
if (in.readBoolean()) {
|
|
||||||
this.storeStatus = IndicesShardStoresResponse.StoreStatus.readStoreStatus(in);
|
|
||||||
} else {
|
|
||||||
this.storeStatus = null;
|
|
||||||
}
|
|
||||||
this.finalDecision = ClusterAllocationExplanation.FinalDecision.readFrom(in);
|
|
||||||
this.finalExplanation = in.readString();
|
|
||||||
this.storeCopy = ClusterAllocationExplanation.StoreCopy.readFrom(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
|
||||||
node.writeTo(out);
|
|
||||||
nodeDecision.writeTo(out);
|
|
||||||
out.writeFloat(nodeWeight);
|
|
||||||
if (storeStatus == null) {
|
|
||||||
out.writeBoolean(false);
|
|
||||||
} else {
|
|
||||||
out.writeBoolean(true);
|
|
||||||
storeStatus.writeTo(out);
|
|
||||||
}
|
|
||||||
finalDecision.writeTo(out);
|
|
||||||
out.writeString(finalExplanation);
|
|
||||||
storeCopy.writeTo(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
|
||||||
builder.startObject(node.getId()); {
|
|
||||||
builder.field("node_name", node.getName());
|
|
||||||
builder.startObject("node_attributes"); {
|
|
||||||
for (Map.Entry<String, String> attrEntry : node.getAttributes().entrySet()) {
|
|
||||||
builder.field(attrEntry.getKey(), attrEntry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.endObject(); // end attributes
|
|
||||||
builder.startObject("store"); {
|
|
||||||
builder.field("shard_copy", storeCopy.toString());
|
|
||||||
if (storeStatus != null) {
|
|
||||||
final Throwable storeErr = storeStatus.getStoreException();
|
|
||||||
if (storeErr != null) {
|
|
||||||
builder.field("store_exception", ExceptionsHelper.detailedMessage(storeErr));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
builder.endObject(); // end store
|
|
||||||
builder.field("final_decision", finalDecision.toString());
|
|
||||||
builder.field("final_explanation", finalExplanation);
|
|
||||||
builder.field("weight", nodeWeight);
|
|
||||||
builder.startArray("decisions");
|
|
||||||
nodeDecision.toXContent(builder, params);
|
|
||||||
builder.endArray();
|
|
||||||
}
|
|
||||||
builder.endObject(); // end node <uuid>
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DiscoveryNode getNode() {
|
|
||||||
return this.node;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Decision getDecision() {
|
|
||||||
return this.nodeDecision;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Float getWeight() {
|
|
||||||
return this.nodeWeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public IndicesShardStoresResponse.StoreStatus getStoreStatus() {
|
|
||||||
return this.storeStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClusterAllocationExplanation.FinalDecision getFinalDecision() {
|
|
||||||
return this.finalDecision;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFinalExplanation() {
|
|
||||||
return this.finalExplanation;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClusterAllocationExplanation.StoreCopy getStoreCopy() {
|
|
||||||
return this.storeCopy;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,13 +19,7 @@
|
||||||
|
|
||||||
package org.elasticsearch.action.admin.cluster.allocation;
|
package org.elasticsearch.action.admin.cluster.allocation;
|
||||||
|
|
||||||
import org.apache.lucene.index.CorruptIndexException;
|
|
||||||
import org.elasticsearch.ElasticsearchException;
|
|
||||||
import org.elasticsearch.ExceptionsHelper;
|
|
||||||
import org.elasticsearch.action.ActionListener;
|
import org.elasticsearch.action.ActionListener;
|
||||||
import org.elasticsearch.action.admin.indices.shards.IndicesShardStoresRequest;
|
|
||||||
import org.elasticsearch.action.admin.indices.shards.IndicesShardStoresResponse;
|
|
||||||
import org.elasticsearch.action.admin.indices.shards.TransportIndicesShardStoresAction;
|
|
||||||
import org.elasticsearch.action.support.ActionFilters;
|
import org.elasticsearch.action.support.ActionFilters;
|
||||||
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
|
import org.elasticsearch.action.support.master.TransportMasterNodeAction;
|
||||||
import org.elasticsearch.cluster.ClusterInfo;
|
import org.elasticsearch.cluster.ClusterInfo;
|
||||||
|
@ -33,34 +27,25 @@ import org.elasticsearch.cluster.ClusterInfoService;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.block.ClusterBlockException;
|
import org.elasticsearch.cluster.block.ClusterBlockException;
|
||||||
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
|
||||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||||
import org.elasticsearch.cluster.metadata.MetaData;
|
|
||||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||||
import org.elasticsearch.cluster.routing.RecoverySource;
|
|
||||||
import org.elasticsearch.cluster.routing.RoutingNode;
|
|
||||||
import org.elasticsearch.cluster.routing.RoutingNodes;
|
import org.elasticsearch.cluster.routing.RoutingNodes;
|
||||||
import org.elasticsearch.cluster.routing.ShardRouting;
|
import org.elasticsearch.cluster.routing.ShardRouting;
|
||||||
import org.elasticsearch.cluster.routing.UnassignedInfo;
|
import org.elasticsearch.cluster.routing.allocation.MoveDecision;
|
||||||
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
|
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
|
||||||
|
import org.elasticsearch.cluster.routing.allocation.AllocateUnassignedDecision;
|
||||||
|
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation.DebugMode;
|
||||||
|
import org.elasticsearch.cluster.routing.allocation.ShardAllocationDecision;
|
||||||
import org.elasticsearch.cluster.routing.allocation.allocator.ShardsAllocator;
|
import org.elasticsearch.cluster.routing.allocation.allocator.ShardsAllocator;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
|
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDeciders;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.Decision;
|
|
||||||
import org.elasticsearch.cluster.service.ClusterService;
|
import org.elasticsearch.cluster.service.ClusterService;
|
||||||
import org.elasticsearch.common.collect.ImmutableOpenIntMap;
|
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
|
||||||
import org.elasticsearch.gateway.GatewayAllocator;
|
import org.elasticsearch.gateway.GatewayAllocator;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.transport.TransportService;
|
import org.elasticsearch.transport.TransportService;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import static org.elasticsearch.cluster.routing.UnassignedInfo.INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@code TransportClusterAllocationExplainAction} is responsible for actually executing the explanation of a shard's allocation on the
|
* The {@code TransportClusterAllocationExplainAction} is responsible for actually executing the explanation of a shard's allocation on the
|
||||||
|
@ -72,7 +57,6 @@ public class TransportClusterAllocationExplainAction
|
||||||
private final ClusterInfoService clusterInfoService;
|
private final ClusterInfoService clusterInfoService;
|
||||||
private final AllocationDeciders allocationDeciders;
|
private final AllocationDeciders allocationDeciders;
|
||||||
private final ShardsAllocator shardAllocator;
|
private final ShardsAllocator shardAllocator;
|
||||||
private final TransportIndicesShardStoresAction shardStoresAction;
|
|
||||||
private final GatewayAllocator gatewayAllocator;
|
private final GatewayAllocator gatewayAllocator;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -80,14 +64,12 @@ public class TransportClusterAllocationExplainAction
|
||||||
ThreadPool threadPool, ActionFilters actionFilters,
|
ThreadPool threadPool, ActionFilters actionFilters,
|
||||||
IndexNameExpressionResolver indexNameExpressionResolver,
|
IndexNameExpressionResolver indexNameExpressionResolver,
|
||||||
ClusterInfoService clusterInfoService, AllocationDeciders allocationDeciders,
|
ClusterInfoService clusterInfoService, AllocationDeciders allocationDeciders,
|
||||||
ShardsAllocator shardAllocator, TransportIndicesShardStoresAction shardStoresAction,
|
ShardsAllocator shardAllocator, GatewayAllocator gatewayAllocator) {
|
||||||
GatewayAllocator gatewayAllocator) {
|
|
||||||
super(settings, ClusterAllocationExplainAction.NAME, transportService, clusterService, threadPool, actionFilters,
|
super(settings, ClusterAllocationExplainAction.NAME, transportService, clusterService, threadPool, actionFilters,
|
||||||
indexNameExpressionResolver, ClusterAllocationExplainRequest::new);
|
indexNameExpressionResolver, ClusterAllocationExplainRequest::new);
|
||||||
this.clusterInfoService = clusterInfoService;
|
this.clusterInfoService = clusterInfoService;
|
||||||
this.allocationDeciders = allocationDeciders;
|
this.allocationDeciders = allocationDeciders;
|
||||||
this.shardAllocator = shardAllocator;
|
this.shardAllocator = shardAllocator;
|
||||||
this.shardStoresAction = shardStoresAction;
|
|
||||||
this.gatewayAllocator = gatewayAllocator;
|
this.gatewayAllocator = gatewayAllocator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,172 +88,6 @@ public class TransportClusterAllocationExplainAction
|
||||||
return new ClusterAllocationExplainResponse();
|
return new ClusterAllocationExplainResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the decisions for the given {@code ShardRouting} on the given {@code RoutingNode}. If {@code includeYesDecisions} is not true,
|
|
||||||
* only non-YES (NO and THROTTLE) decisions are returned.
|
|
||||||
*/
|
|
||||||
public static Decision tryShardOnNode(ShardRouting shard, RoutingNode node, RoutingAllocation allocation, boolean includeYesDecisions) {
|
|
||||||
Decision d = allocation.deciders().canAllocate(shard, node, allocation);
|
|
||||||
if (includeYesDecisions) {
|
|
||||||
return d;
|
|
||||||
} else {
|
|
||||||
Decision.Multi nonYesDecisions = new Decision.Multi();
|
|
||||||
List<Decision> decisions = d.getDecisions();
|
|
||||||
for (Decision decision : decisions) {
|
|
||||||
if (decision.type() != Decision.Type.YES) {
|
|
||||||
nonYesDecisions.add(decision);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nonYesDecisions;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a {@code WeightedDecision} object for the given shard given all the metadata. This also attempts to construct the human
|
|
||||||
* readable FinalDecision and final explanation as part of the explanation.
|
|
||||||
*/
|
|
||||||
public static NodeExplanation calculateNodeExplanation(ShardRouting shard,
|
|
||||||
IndexMetaData indexMetaData,
|
|
||||||
DiscoveryNode node,
|
|
||||||
Decision nodeDecision,
|
|
||||||
Float nodeWeight,
|
|
||||||
IndicesShardStoresResponse.StoreStatus storeStatus,
|
|
||||||
String assignedNodeId,
|
|
||||||
Set<String> activeAllocationIds,
|
|
||||||
boolean hasPendingAsyncFetch) {
|
|
||||||
final ClusterAllocationExplanation.FinalDecision finalDecision;
|
|
||||||
final ClusterAllocationExplanation.StoreCopy storeCopy;
|
|
||||||
final String finalExplanation;
|
|
||||||
|
|
||||||
if (storeStatus == null) {
|
|
||||||
// No copies of the data
|
|
||||||
storeCopy = ClusterAllocationExplanation.StoreCopy.NONE;
|
|
||||||
} else {
|
|
||||||
final Exception storeErr = storeStatus.getStoreException();
|
|
||||||
if (storeErr != null) {
|
|
||||||
if (ExceptionsHelper.unwrapCause(storeErr) instanceof CorruptIndexException) {
|
|
||||||
storeCopy = ClusterAllocationExplanation.StoreCopy.CORRUPT;
|
|
||||||
} else {
|
|
||||||
storeCopy = ClusterAllocationExplanation.StoreCopy.IO_ERROR;
|
|
||||||
}
|
|
||||||
} else if (activeAllocationIds.isEmpty()) {
|
|
||||||
// The ids are only empty if dealing with a legacy index
|
|
||||||
// TODO: fetch the shard state versions and display here?
|
|
||||||
storeCopy = ClusterAllocationExplanation.StoreCopy.UNKNOWN;
|
|
||||||
} else if (activeAllocationIds.contains(storeStatus.getAllocationId())) {
|
|
||||||
storeCopy = ClusterAllocationExplanation.StoreCopy.AVAILABLE;
|
|
||||||
} else {
|
|
||||||
// Otherwise, this is a stale copy of the data (allocation ids don't match)
|
|
||||||
storeCopy = ClusterAllocationExplanation.StoreCopy.STALE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.getId().equals(assignedNodeId)) {
|
|
||||||
finalDecision = ClusterAllocationExplanation.FinalDecision.ALREADY_ASSIGNED;
|
|
||||||
finalExplanation = "the shard is already assigned to this node";
|
|
||||||
} else if (shard.unassigned() && shard.primary() == false &&
|
|
||||||
shard.unassignedInfo().getReason() != UnassignedInfo.Reason.INDEX_CREATED && nodeDecision.type() != Decision.Type.YES) {
|
|
||||||
finalExplanation = "the shard cannot be assigned because allocation deciders return a " + nodeDecision.type().name() +
|
|
||||||
" decision";
|
|
||||||
finalDecision = ClusterAllocationExplanation.FinalDecision.NO;
|
|
||||||
} else if (shard.unassigned() && shard.primary() == false &&
|
|
||||||
shard.unassignedInfo().getReason() != UnassignedInfo.Reason.INDEX_CREATED && hasPendingAsyncFetch) {
|
|
||||||
finalExplanation = "the shard's state is still being fetched so it cannot be allocated";
|
|
||||||
finalDecision = ClusterAllocationExplanation.FinalDecision.NO;
|
|
||||||
} else if (shard.primary() && shard.unassigned() &&
|
|
||||||
(shard.recoverySource().getType() == RecoverySource.Type.EXISTING_STORE ||
|
|
||||||
shard.recoverySource().getType() == RecoverySource.Type.SNAPSHOT)
|
|
||||||
&& hasPendingAsyncFetch) {
|
|
||||||
finalExplanation = "the shard's state is still being fetched so it cannot be allocated";
|
|
||||||
finalDecision = ClusterAllocationExplanation.FinalDecision.NO;
|
|
||||||
} else if (shard.primary() && shard.unassigned() && shard.recoverySource().getType() == RecoverySource.Type.EXISTING_STORE &&
|
|
||||||
storeCopy == ClusterAllocationExplanation.StoreCopy.STALE) {
|
|
||||||
finalExplanation = "the copy of the shard is stale, allocation ids do not match";
|
|
||||||
finalDecision = ClusterAllocationExplanation.FinalDecision.NO;
|
|
||||||
} else if (shard.primary() && shard.unassigned() && shard.recoverySource().getType() == RecoverySource.Type.EXISTING_STORE &&
|
|
||||||
storeCopy == ClusterAllocationExplanation.StoreCopy.NONE) {
|
|
||||||
finalExplanation = "there is no copy of the shard available";
|
|
||||||
finalDecision = ClusterAllocationExplanation.FinalDecision.NO;
|
|
||||||
} else if (shard.primary() && shard.unassigned() && shard.recoverySource().getType() == RecoverySource.Type.EXISTING_STORE &&
|
|
||||||
storeCopy == ClusterAllocationExplanation.StoreCopy.CORRUPT) {
|
|
||||||
finalExplanation = "the copy of the shard is corrupt";
|
|
||||||
finalDecision = ClusterAllocationExplanation.FinalDecision.NO;
|
|
||||||
} else if (shard.primary() && shard.unassigned() && shard.recoverySource().getType() == RecoverySource.Type.EXISTING_STORE &&
|
|
||||||
storeCopy == ClusterAllocationExplanation.StoreCopy.IO_ERROR) {
|
|
||||||
finalExplanation = "the copy of the shard cannot be read";
|
|
||||||
finalDecision = ClusterAllocationExplanation.FinalDecision.NO;
|
|
||||||
} else {
|
|
||||||
if (nodeDecision.type() == Decision.Type.NO) {
|
|
||||||
finalDecision = ClusterAllocationExplanation.FinalDecision.NO;
|
|
||||||
finalExplanation = "the shard cannot be assigned because one or more allocation decider returns a 'NO' decision";
|
|
||||||
} else {
|
|
||||||
// TODO: handle throttling decision better here
|
|
||||||
finalDecision = ClusterAllocationExplanation.FinalDecision.YES;
|
|
||||||
if (storeCopy == ClusterAllocationExplanation.StoreCopy.AVAILABLE) {
|
|
||||||
finalExplanation = "the shard can be assigned and the node contains a valid copy of the shard data";
|
|
||||||
} else {
|
|
||||||
finalExplanation = "the shard can be assigned";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new NodeExplanation(node, nodeDecision, nodeWeight, storeStatus, finalDecision, finalExplanation, storeCopy);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For the given {@code ShardRouting}, return the explanation of the allocation for that shard on all nodes. If {@code
|
|
||||||
* includeYesDecisions} is true, returns all decisions, otherwise returns only 'NO' and 'THROTTLE' decisions.
|
|
||||||
*/
|
|
||||||
public static ClusterAllocationExplanation explainShard(ShardRouting shard, RoutingAllocation allocation, RoutingNodes routingNodes,
|
|
||||||
boolean includeYesDecisions, ShardsAllocator shardAllocator,
|
|
||||||
List<IndicesShardStoresResponse.StoreStatus> shardStores,
|
|
||||||
GatewayAllocator gatewayAllocator, ClusterInfo clusterInfo) {
|
|
||||||
// don't short circuit deciders, we want a full explanation
|
|
||||||
allocation.debugDecision(true);
|
|
||||||
// get the existing unassigned info if available
|
|
||||||
UnassignedInfo ui = shard.unassignedInfo();
|
|
||||||
|
|
||||||
Map<DiscoveryNode, Decision> nodeToDecision = new HashMap<>();
|
|
||||||
for (RoutingNode node : routingNodes) {
|
|
||||||
DiscoveryNode discoNode = node.node();
|
|
||||||
if (discoNode.isDataNode()) {
|
|
||||||
Decision d = tryShardOnNode(shard, node, allocation, includeYesDecisions);
|
|
||||||
nodeToDecision.put(discoNode, d);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
long remainingDelayMillis = 0;
|
|
||||||
final MetaData metadata = allocation.metaData();
|
|
||||||
final IndexMetaData indexMetaData = metadata.index(shard.index());
|
|
||||||
long allocationDelayMillis = INDEX_DELAYED_NODE_LEFT_TIMEOUT_SETTING.get(indexMetaData.getSettings()).getMillis();
|
|
||||||
if (ui != null && ui.isDelayed()) {
|
|
||||||
long remainingDelayNanos = ui.getRemainingDelay(System.nanoTime(), indexMetaData.getSettings());
|
|
||||||
remainingDelayMillis = TimeValue.timeValueNanos(remainingDelayNanos).millis();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate weights for each of the nodes
|
|
||||||
Map<DiscoveryNode, Float> weights = shardAllocator.weighShard(allocation, shard);
|
|
||||||
|
|
||||||
Map<DiscoveryNode, IndicesShardStoresResponse.StoreStatus> nodeToStatus = new HashMap<>(shardStores.size());
|
|
||||||
for (IndicesShardStoresResponse.StoreStatus status : shardStores) {
|
|
||||||
nodeToStatus.put(status.getNode(), status);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<DiscoveryNode, NodeExplanation> explanations = new HashMap<>(shardStores.size());
|
|
||||||
for (Map.Entry<DiscoveryNode, Decision> entry : nodeToDecision.entrySet()) {
|
|
||||||
DiscoveryNode node = entry.getKey();
|
|
||||||
Decision decision = entry.getValue();
|
|
||||||
Float weight = weights.get(node);
|
|
||||||
IndicesShardStoresResponse.StoreStatus storeStatus = nodeToStatus.get(node);
|
|
||||||
NodeExplanation nodeExplanation = calculateNodeExplanation(shard, indexMetaData, node, decision, weight,
|
|
||||||
storeStatus, shard.currentNodeId(), indexMetaData.inSyncAllocationIds(shard.getId()),
|
|
||||||
allocation.hasPendingAsyncFetch());
|
|
||||||
explanations.put(node, nodeExplanation);
|
|
||||||
}
|
|
||||||
return new ClusterAllocationExplanation(shard.shardId(), shard.primary(),
|
|
||||||
shard.currentNodeId(), allocationDelayMillis, remainingDelayMillis, ui,
|
|
||||||
gatewayAllocator.hasFetchPending(shard.shardId(), shard.primary()), explanations, clusterInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void masterOperation(final ClusterAllocationExplainRequest request, final ClusterState state,
|
protected void masterOperation(final ClusterAllocationExplainRequest request, final ClusterState state,
|
||||||
final ActionListener<ClusterAllocationExplainResponse> listener) {
|
final ActionListener<ClusterAllocationExplainResponse> listener) {
|
||||||
|
@ -280,31 +96,96 @@ public class TransportClusterAllocationExplainAction
|
||||||
final RoutingAllocation allocation = new RoutingAllocation(allocationDeciders, routingNodes, state,
|
final RoutingAllocation allocation = new RoutingAllocation(allocationDeciders, routingNodes, state,
|
||||||
clusterInfo, System.nanoTime(), false);
|
clusterInfo, System.nanoTime(), false);
|
||||||
|
|
||||||
|
ShardRouting shardRouting = findShardToExplain(request, allocation);
|
||||||
|
logger.debug("explaining the allocation for [{}], found shard [{}]", request, shardRouting);
|
||||||
|
|
||||||
|
ClusterAllocationExplanation cae = explainShard(shardRouting, allocation,
|
||||||
|
request.includeDiskInfo() ? clusterInfo : null, request.includeYesDecisions(), gatewayAllocator, shardAllocator);
|
||||||
|
listener.onResponse(new ClusterAllocationExplainResponse(cae));
|
||||||
|
}
|
||||||
|
|
||||||
|
// public for testing
|
||||||
|
public static ClusterAllocationExplanation explainShard(ShardRouting shardRouting, RoutingAllocation allocation,
|
||||||
|
ClusterInfo clusterInfo, boolean includeYesDecisions,
|
||||||
|
GatewayAllocator gatewayAllocator, ShardsAllocator shardAllocator) {
|
||||||
|
allocation.setDebugMode(includeYesDecisions ? DebugMode.ON : DebugMode.EXCLUDE_YES_DECISIONS);
|
||||||
|
|
||||||
|
ShardAllocationDecision shardDecision;
|
||||||
|
if (shardRouting.initializing() || shardRouting.relocating()) {
|
||||||
|
shardDecision = ShardAllocationDecision.NOT_TAKEN;
|
||||||
|
} else {
|
||||||
|
AllocateUnassignedDecision allocateDecision = shardRouting.unassigned() ?
|
||||||
|
gatewayAllocator.decideUnassignedShardAllocation(shardRouting, allocation) : AllocateUnassignedDecision.NOT_TAKEN;
|
||||||
|
if (allocateDecision.isDecisionTaken() == false) {
|
||||||
|
shardDecision = shardAllocator.decideShardAllocation(shardRouting, allocation);
|
||||||
|
} else {
|
||||||
|
shardDecision = new ShardAllocationDecision(allocateDecision, MoveDecision.NOT_TAKEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ClusterAllocationExplanation(shardRouting,
|
||||||
|
shardRouting.currentNodeId() != null ? allocation.nodes().get(shardRouting.currentNodeId()) : null,
|
||||||
|
shardRouting.relocatingNodeId() != null ? allocation.nodes().get(shardRouting.relocatingNodeId()) : null,
|
||||||
|
clusterInfo, shardDecision);
|
||||||
|
}
|
||||||
|
|
||||||
|
// public for testing
|
||||||
|
public static ShardRouting findShardToExplain(ClusterAllocationExplainRequest request, RoutingAllocation allocation) {
|
||||||
ShardRouting foundShard = null;
|
ShardRouting foundShard = null;
|
||||||
if (request.useAnyUnassignedShard()) {
|
if (request.useAnyUnassignedShard()) {
|
||||||
// If we can use any shard, just pick the first unassigned one (if there are any)
|
// If we can use any shard, just pick the first unassigned one (if there are any)
|
||||||
RoutingNodes.UnassignedShards.UnassignedIterator ui = routingNodes.unassigned().iterator();
|
RoutingNodes.UnassignedShards.UnassignedIterator ui = allocation.routingNodes().unassigned().iterator();
|
||||||
if (ui.hasNext()) {
|
if (ui.hasNext()) {
|
||||||
foundShard = ui.next();
|
foundShard = ui.next();
|
||||||
}
|
}
|
||||||
|
if (foundShard == null) {
|
||||||
|
throw new IllegalStateException("unable to find any unassigned shards to explain [" + request + "]");
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
String index = request.getIndex();
|
String index = request.getIndex();
|
||||||
int shard = request.getShard();
|
int shard = request.getShard();
|
||||||
if (request.isPrimary()) {
|
if (request.isPrimary()) {
|
||||||
// If we're looking for the primary shard, there's only one copy, so pick it directly
|
// If we're looking for the primary shard, there's only one copy, so pick it directly
|
||||||
foundShard = allocation.routingTable().shardRoutingTable(index, shard).primaryShard();
|
foundShard = allocation.routingTable().shardRoutingTable(index, shard).primaryShard();
|
||||||
|
if (request.getCurrentNode() != null) {
|
||||||
|
DiscoveryNode primaryNode = allocation.nodes().resolveNode(request.getCurrentNode());
|
||||||
|
// the primary is assigned to a node other than the node specified in the request
|
||||||
|
if (primaryNode.getId().equals(foundShard.currentNodeId()) == false) {
|
||||||
|
throw new IllegalStateException("unable to find primary shard assigned to node [" + request.getCurrentNode() + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// If looking for a replica, go through all the replica shards
|
// If looking for a replica, go through all the replica shards
|
||||||
List<ShardRouting> replicaShardRoutings = allocation.routingTable().shardRoutingTable(index, shard).replicaShards();
|
List<ShardRouting> replicaShardRoutings = allocation.routingTable().shardRoutingTable(index, shard).replicaShards();
|
||||||
|
if (request.getCurrentNode() != null) {
|
||||||
|
// the request is to explain a replica shard already assigned on a particular node,
|
||||||
|
// so find that shard copy
|
||||||
|
DiscoveryNode replicaNode = allocation.nodes().resolveNode(request.getCurrentNode());
|
||||||
|
for (ShardRouting replica : replicaShardRoutings) {
|
||||||
|
if (replicaNode.getId().equals(replica.currentNodeId())) {
|
||||||
|
foundShard = replica;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (foundShard == null) {
|
||||||
|
throw new IllegalStateException("unable to find a replica shard assigned to node [" +
|
||||||
|
request.getCurrentNode() + "]");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (replicaShardRoutings.size() > 0) {
|
if (replicaShardRoutings.size() > 0) {
|
||||||
// Pick the first replica at the very least
|
// Pick the first replica at the very least
|
||||||
foundShard = replicaShardRoutings.get(0);
|
foundShard = replicaShardRoutings.get(0);
|
||||||
|
for (ShardRouting replica : replicaShardRoutings) {
|
||||||
// In case there are multiple replicas where some are assigned and some aren't,
|
// In case there are multiple replicas where some are assigned and some aren't,
|
||||||
// try to find one that is unassigned at least
|
// try to find one that is unassigned at least
|
||||||
for (ShardRouting replica : replicaShardRoutings) {
|
|
||||||
if (replica.unassigned()) {
|
if (replica.unassigned()) {
|
||||||
foundShard = replica;
|
foundShard = replica;
|
||||||
break;
|
break;
|
||||||
|
} else if (replica.started() && (foundShard.initializing() || foundShard.relocating())) {
|
||||||
|
// prefer started shards to initializing or relocating shards because started shards
|
||||||
|
// can be explained
|
||||||
|
foundShard = replica;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -312,34 +193,8 @@ public class TransportClusterAllocationExplainAction
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundShard == null) {
|
if (foundShard == null) {
|
||||||
listener.onFailure(new ElasticsearchException("unable to find any shards to explain [{}] in the routing table", request));
|
throw new IllegalStateException("unable to find any shards to explain [" + request + "] in the routing table");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
final ShardRouting shardRouting = foundShard;
|
return foundShard;
|
||||||
logger.debug("explaining the allocation for [{}], found shard [{}]", request, shardRouting);
|
|
||||||
|
|
||||||
getShardStores(shardRouting, new ActionListener<IndicesShardStoresResponse>() {
|
|
||||||
@Override
|
|
||||||
public void onResponse(IndicesShardStoresResponse shardStoreResponse) {
|
|
||||||
ImmutableOpenIntMap<List<IndicesShardStoresResponse.StoreStatus>> shardStatuses =
|
|
||||||
shardStoreResponse.getStoreStatuses().get(shardRouting.getIndexName());
|
|
||||||
List<IndicesShardStoresResponse.StoreStatus> shardStoreStatus = shardStatuses.get(shardRouting.id());
|
|
||||||
ClusterAllocationExplanation cae = explainShard(shardRouting, allocation, routingNodes,
|
|
||||||
request.includeYesDecisions(), shardAllocator, shardStoreStatus, gatewayAllocator,
|
|
||||||
request.includeDiskInfo() ? clusterInfo : null);
|
|
||||||
listener.onResponse(new ClusterAllocationExplainResponse(cae));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(Exception e) {
|
|
||||||
listener.onFailure(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void getShardStores(ShardRouting shard, final ActionListener<IndicesShardStoresResponse> listener) {
|
|
||||||
IndicesShardStoresRequest request = new IndicesShardStoresRequest(shard.getIndexName());
|
|
||||||
request.shardStatuses("all");
|
|
||||||
shardStoresAction.execute(request, listener);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.elasticsearch.action.support.ActionFilters;
|
||||||
import org.elasticsearch.action.support.ActiveShardCount;
|
import org.elasticsearch.action.support.ActiveShardCount;
|
||||||
import org.elasticsearch.action.support.IndicesOptions;
|
import org.elasticsearch.action.support.IndicesOptions;
|
||||||
import org.elasticsearch.action.support.master.TransportMasterNodeReadAction;
|
import org.elasticsearch.action.support.master.TransportMasterNodeReadAction;
|
||||||
|
import org.elasticsearch.cluster.LocalClusterUpdateTask;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.ClusterStateObserver;
|
import org.elasticsearch.cluster.ClusterStateObserver;
|
||||||
import org.elasticsearch.cluster.ClusterStateUpdateTask;
|
import org.elasticsearch.cluster.ClusterStateUpdateTask;
|
||||||
|
@ -85,6 +86,28 @@ public class TransportClusterHealthAction extends TransportMasterNodeReadAction<
|
||||||
protected void masterOperation(Task task, final ClusterHealthRequest request, final ClusterState unusedState, final ActionListener<ClusterHealthResponse> listener) {
|
protected void masterOperation(Task task, final ClusterHealthRequest request, final ClusterState unusedState, final ActionListener<ClusterHealthResponse> listener) {
|
||||||
if (request.waitForEvents() != null) {
|
if (request.waitForEvents() != null) {
|
||||||
final long endTimeMS = TimeValue.nsecToMSec(System.nanoTime()) + request.timeout().millis();
|
final long endTimeMS = TimeValue.nsecToMSec(System.nanoTime()) + request.timeout().millis();
|
||||||
|
if (request.local()) {
|
||||||
|
clusterService.submitStateUpdateTask("cluster_health (wait_for_events [" + request.waitForEvents() + "])", new LocalClusterUpdateTask(request.waitForEvents()) {
|
||||||
|
@Override
|
||||||
|
public ClusterTasksResult<LocalClusterUpdateTask> execute(ClusterState currentState) {
|
||||||
|
return unchanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clusterStateProcessed(String source, ClusterState oldState, ClusterState newState) {
|
||||||
|
final long timeoutInMillis = Math.max(0, endTimeMS - TimeValue.nsecToMSec(System.nanoTime()));
|
||||||
|
final TimeValue newTimeout = TimeValue.timeValueMillis(timeoutInMillis);
|
||||||
|
request.timeout(newTimeout);
|
||||||
|
executeHealth(request, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(String source, Exception e) {
|
||||||
|
logger.error((Supplier<?>) () -> new ParameterizedMessage("unexpected failure during [{}]", source), e);
|
||||||
|
listener.onFailure(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
clusterService.submitStateUpdateTask("cluster_health (wait_for_events [" + request.waitForEvents() + "])", new ClusterStateUpdateTask(request.waitForEvents()) {
|
clusterService.submitStateUpdateTask("cluster_health (wait_for_events [" + request.waitForEvents() + "])", new ClusterStateUpdateTask(request.waitForEvents()) {
|
||||||
@Override
|
@Override
|
||||||
public ClusterState execute(ClusterState currentState) {
|
public ClusterState execute(ClusterState currentState) {
|
||||||
|
@ -110,12 +133,8 @@ public class TransportClusterHealthAction extends TransportMasterNodeReadAction<
|
||||||
logger.error((Supplier<?>) () -> new ParameterizedMessage("unexpected failure during [{}]", source), e);
|
logger.error((Supplier<?>) () -> new ParameterizedMessage("unexpected failure during [{}]", source), e);
|
||||||
listener.onFailure(e);
|
listener.onFailure(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean runOnlyOnMaster() {
|
|
||||||
return !request.local();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
executeHealth(request, listener);
|
executeHealth(request, listener);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class ClusterRerouteResponse extends AcknowledgedResponse {
|
||||||
@Override
|
@Override
|
||||||
public void readFrom(StreamInput in) throws IOException {
|
public void readFrom(StreamInput in) throws IOException {
|
||||||
super.readFrom(in);
|
super.readFrom(in);
|
||||||
state = ClusterState.Builder.readFrom(in, null);
|
state = ClusterState.readFrom(in, null);
|
||||||
readAcknowledged(in);
|
readAcknowledged(in);
|
||||||
explanations = RoutingExplanations.readFrom(in);
|
explanations = RoutingExplanations.readFrom(in);
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,6 @@ public class TransportDeleteSnapshotAction extends TransportMasterNodeAction<Del
|
||||||
public void onFailure(Exception e) {
|
public void onFailure(Exception e) {
|
||||||
listener.onFailure(e);
|
listener.onFailure(e);
|
||||||
}
|
}
|
||||||
});
|
}, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class ClusterStateResponse extends ActionResponse {
|
||||||
public void readFrom(StreamInput in) throws IOException {
|
public void readFrom(StreamInput in) throws IOException {
|
||||||
super.readFrom(in);
|
super.readFrom(in);
|
||||||
clusterName = new ClusterName(in);
|
clusterName = new ClusterName(in);
|
||||||
clusterState = ClusterState.Builder.readFrom(in, null);
|
clusterState = ClusterState.readFrom(in, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class GetAliasesResponse extends ActionResponse {
|
||||||
int valueSize = in.readVInt();
|
int valueSize = in.readVInt();
|
||||||
List<AliasMetaData> value = new ArrayList<>(valueSize);
|
List<AliasMetaData> value = new ArrayList<>(valueSize);
|
||||||
for (int j = 0; j < valueSize; j++) {
|
for (int j = 0; j < valueSize; j++) {
|
||||||
value.add(AliasMetaData.Builder.readFrom(in));
|
value.add(new AliasMetaData(in));
|
||||||
}
|
}
|
||||||
aliasesBuilder.put(key, Collections.unmodifiableList(value));
|
aliasesBuilder.put(key, Collections.unmodifiableList(value));
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,7 @@ public class GetIndexResponse extends ActionResponse {
|
||||||
int valueSize = in.readVInt();
|
int valueSize = in.readVInt();
|
||||||
ImmutableOpenMap.Builder<String, MappingMetaData> mappingEntryBuilder = ImmutableOpenMap.builder();
|
ImmutableOpenMap.Builder<String, MappingMetaData> mappingEntryBuilder = ImmutableOpenMap.builder();
|
||||||
for (int j = 0; j < valueSize; j++) {
|
for (int j = 0; j < valueSize; j++) {
|
||||||
mappingEntryBuilder.put(in.readString(), MappingMetaData.PROTO.readFrom(in));
|
mappingEntryBuilder.put(in.readString(), new MappingMetaData(in));
|
||||||
}
|
}
|
||||||
mappingsMapBuilder.put(key, mappingEntryBuilder.build());
|
mappingsMapBuilder.put(key, mappingEntryBuilder.build());
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ public class GetIndexResponse extends ActionResponse {
|
||||||
int valueSize = in.readVInt();
|
int valueSize = in.readVInt();
|
||||||
List<AliasMetaData> aliasEntryBuilder = new ArrayList<>();
|
List<AliasMetaData> aliasEntryBuilder = new ArrayList<>();
|
||||||
for (int j = 0; j < valueSize; j++) {
|
for (int j = 0; j < valueSize; j++) {
|
||||||
aliasEntryBuilder.add(AliasMetaData.Builder.readFrom(in));
|
aliasEntryBuilder.add(new AliasMetaData(in));
|
||||||
}
|
}
|
||||||
aliasesMapBuilder.put(key, Collections.unmodifiableList(aliasEntryBuilder));
|
aliasesMapBuilder.put(key, Collections.unmodifiableList(aliasEntryBuilder));
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class GetMappingsResponse extends ActionResponse {
|
||||||
int valueSize = in.readVInt();
|
int valueSize = in.readVInt();
|
||||||
ImmutableOpenMap.Builder<String, MappingMetaData> typeMapBuilder = ImmutableOpenMap.builder();
|
ImmutableOpenMap.Builder<String, MappingMetaData> typeMapBuilder = ImmutableOpenMap.builder();
|
||||||
for (int j = 0; j < valueSize; j++) {
|
for (int j = 0; j < valueSize; j++) {
|
||||||
typeMapBuilder.put(in.readString(), MappingMetaData.PROTO.readFrom(in));
|
typeMapBuilder.put(in.readString(), new MappingMetaData(in));
|
||||||
}
|
}
|
||||||
indexMapBuilder.put(key, typeMapBuilder.build());
|
indexMapBuilder.put(key, typeMapBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class GetIndexTemplatesResponse extends ActionResponse implements ToXCont
|
||||||
int size = in.readVInt();
|
int size = in.readVInt();
|
||||||
indexTemplates = new ArrayList<>(size);
|
indexTemplates = new ArrayList<>(size);
|
||||||
for (int i = 0 ; i < size ; i++) {
|
for (int i = 0 ; i < size ; i++) {
|
||||||
indexTemplates.add(0, IndexTemplateMetaData.Builder.readFrom(in));
|
indexTemplates.add(0, IndexTemplateMetaData.readFrom(in));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -349,7 +349,7 @@ public class BulkRequest extends ActionRequest implements CompositeIndicesReques
|
||||||
} else if ("fields".equals(currentFieldName)) {
|
} else if ("fields".equals(currentFieldName)) {
|
||||||
throw new IllegalArgumentException("Action/metadata line [" + line + "] contains a simple value for parameter [fields] while a list is expected");
|
throw new IllegalArgumentException("Action/metadata line [" + line + "] contains a simple value for parameter [fields] while a list is expected");
|
||||||
} else if ("_source".equals(currentFieldName)) {
|
} else if ("_source".equals(currentFieldName)) {
|
||||||
fetchSourceContext = FetchSourceContext.parse(parser);
|
fetchSourceContext = FetchSourceContext.fromXContent(parser);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Action/metadata line [" + line + "] contains an unknown parameter [" + currentFieldName + "]");
|
throw new IllegalArgumentException("Action/metadata line [" + line + "] contains an unknown parameter [" + currentFieldName + "]");
|
||||||
}
|
}
|
||||||
|
@ -362,7 +362,7 @@ public class BulkRequest extends ActionRequest implements CompositeIndicesReques
|
||||||
throw new IllegalArgumentException("Malformed action/metadata line [" + line + "], expected a simple value for field [" + currentFieldName + "] but found [" + token + "]");
|
throw new IllegalArgumentException("Malformed action/metadata line [" + line + "], expected a simple value for field [" + currentFieldName + "] but found [" + token + "]");
|
||||||
}
|
}
|
||||||
} else if (token == XContentParser.Token.START_OBJECT && "_source".equals(currentFieldName)) {
|
} else if (token == XContentParser.Token.START_OBJECT && "_source".equals(currentFieldName)) {
|
||||||
fetchSourceContext = FetchSourceContext.parse(parser);
|
fetchSourceContext = FetchSourceContext.fromXContent(parser);
|
||||||
} else if (token != XContentParser.Token.VALUE_NULL) {
|
} else if (token != XContentParser.Token.VALUE_NULL) {
|
||||||
throw new IllegalArgumentException("Malformed action/metadata line [" + line + "], expected a simple value for field [" + currentFieldName + "] but found [" + token + "]");
|
throw new IllegalArgumentException("Malformed action/metadata line [" + line + "], expected a simple value for field [" + currentFieldName + "] but found [" + token + "]");
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ public class GetPipelineResponse extends ActionResponse implements StatusToXCont
|
||||||
int size = in.readVInt();
|
int size = in.readVInt();
|
||||||
pipelines = new ArrayList<>(size);
|
pipelines = new ArrayList<>(size);
|
||||||
for (int i = 0; i < size; i++) {
|
for (int i = 0; i < size; i++) {
|
||||||
pipelines.add(PipelineConfiguration.readPipelineConfiguration(in));
|
pipelines.add(PipelineConfiguration.readFrom(in));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ import org.elasticsearch.action.ActionRequestValidationException;
|
||||||
import org.elasticsearch.action.IndicesRequest;
|
import org.elasticsearch.action.IndicesRequest;
|
||||||
import org.elasticsearch.action.support.IndicesOptions;
|
import org.elasticsearch.action.support.IndicesOptions;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.ParseFieldMatcher;
|
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
|
@ -199,7 +198,7 @@ public final class SearchRequest extends ActionRequest implements IndicesRequest
|
||||||
* "query_then_fetch"/"queryThenFetch", and "query_and_fetch"/"queryAndFetch".
|
* "query_then_fetch"/"queryThenFetch", and "query_and_fetch"/"queryAndFetch".
|
||||||
*/
|
*/
|
||||||
public SearchRequest searchType(String searchType) {
|
public SearchRequest searchType(String searchType) {
|
||||||
return searchType(SearchType.fromString(searchType, ParseFieldMatcher.EMPTY));
|
return searchType(SearchType.fromString(searchType));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,8 +19,6 @@
|
||||||
|
|
||||||
package org.elasticsearch.action.search;
|
package org.elasticsearch.action.search;
|
||||||
|
|
||||||
import org.elasticsearch.common.ParseFieldMatcher;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search type represent the manner at which the search operation is executed.
|
* Search type represent the manner at which the search operation is executed.
|
||||||
*
|
*
|
||||||
|
@ -91,7 +89,7 @@ public enum SearchType {
|
||||||
* one of "dfs_query_then_fetch"/"dfsQueryThenFetch", "dfs_query_and_fetch"/"dfsQueryAndFetch",
|
* one of "dfs_query_then_fetch"/"dfsQueryThenFetch", "dfs_query_and_fetch"/"dfsQueryAndFetch",
|
||||||
* "query_then_fetch"/"queryThenFetch" and "query_and_fetch"/"queryAndFetch".
|
* "query_then_fetch"/"queryThenFetch" and "query_and_fetch"/"queryAndFetch".
|
||||||
*/
|
*/
|
||||||
public static SearchType fromString(String searchType, ParseFieldMatcher parseFieldMatcher) {
|
public static SearchType fromString(String searchType) {
|
||||||
if (searchType == null) {
|
if (searchType == null) {
|
||||||
return SearchType.DEFAULT;
|
return SearchType.DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ import org.elasticsearch.action.support.WriteRequest;
|
||||||
import org.elasticsearch.action.support.replication.ReplicationRequest;
|
import org.elasticsearch.action.support.replication.ReplicationRequest;
|
||||||
import org.elasticsearch.action.support.single.instance.InstanceShardOperationRequest;
|
import org.elasticsearch.action.support.single.instance.InstanceShardOperationRequest;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.ParseFieldMatcher;
|
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.common.logging.DeprecationLogger;
|
import org.elasticsearch.common.logging.DeprecationLogger;
|
||||||
|
@ -714,7 +713,7 @@ public class UpdateRequest extends InstanceShardOperationRequest<UpdateRequest>
|
||||||
if (token == XContentParser.Token.FIELD_NAME) {
|
if (token == XContentParser.Token.FIELD_NAME) {
|
||||||
currentFieldName = parser.currentName();
|
currentFieldName = parser.currentName();
|
||||||
} else if ("script".equals(currentFieldName)) {
|
} else if ("script".equals(currentFieldName)) {
|
||||||
script = Script.parse(parser, ParseFieldMatcher.EMPTY);
|
script = Script.parse(parser);
|
||||||
} else if ("scripted_upsert".equals(currentFieldName)) {
|
} else if ("scripted_upsert".equals(currentFieldName)) {
|
||||||
scriptedUpsert = parser.booleanValue();
|
scriptedUpsert = parser.booleanValue();
|
||||||
} else if ("upsert".equals(currentFieldName)) {
|
} else if ("upsert".equals(currentFieldName)) {
|
||||||
|
@ -740,7 +739,7 @@ public class UpdateRequest extends InstanceShardOperationRequest<UpdateRequest>
|
||||||
fields(fields.toArray(new String[fields.size()]));
|
fields(fields.toArray(new String[fields.size()]));
|
||||||
}
|
}
|
||||||
} else if ("_source".equals(currentFieldName)) {
|
} else if ("_source".equals(currentFieldName)) {
|
||||||
fetchSourceContext = FetchSourceContext.parse(parser);
|
fetchSourceContext = FetchSourceContext.fromXContent(parser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (script != null) {
|
if (script != null) {
|
||||||
|
|
|
@ -38,6 +38,7 @@ import org.elasticsearch.common.inject.CreationException;
|
||||||
import org.elasticsearch.common.logging.ESLoggerFactory;
|
import org.elasticsearch.common.logging.ESLoggerFactory;
|
||||||
import org.elasticsearch.common.logging.LogConfigurator;
|
import org.elasticsearch.common.logging.LogConfigurator;
|
||||||
import org.elasticsearch.common.logging.Loggers;
|
import org.elasticsearch.common.logging.Loggers;
|
||||||
|
import org.elasticsearch.common.network.IfConfig;
|
||||||
import org.elasticsearch.common.settings.KeyStoreWrapper;
|
import org.elasticsearch.common.settings.KeyStoreWrapper;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.transport.BoundTransportAddress;
|
import org.elasticsearch.common.transport.BoundTransportAddress;
|
||||||
|
@ -206,6 +207,9 @@ final class Bootstrap {
|
||||||
throw new BootstrapException(e);
|
throw new BootstrapException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Log ifconfig output before SecurityManager is installed
|
||||||
|
IfConfig.logIfNecessary();
|
||||||
|
|
||||||
// install SM after natives, shutdown hooks, etc.
|
// install SM after natives, shutdown hooks, etc.
|
||||||
try {
|
try {
|
||||||
Security.configure(environment, BootstrapSettings.SECURITY_FILTER_BAD_DEFAULTS_SETTING.get(settings));
|
Security.configure(environment, BootstrapSettings.SECURITY_FILTER_BAD_DEFAULTS_SETTING.get(settings));
|
||||||
|
|
|
@ -27,6 +27,7 @@ import org.elasticsearch.action.ActionRequest;
|
||||||
import org.elasticsearch.action.ActionRequestBuilder;
|
import org.elasticsearch.action.ActionRequestBuilder;
|
||||||
import org.elasticsearch.action.ActionResponse;
|
import org.elasticsearch.action.ActionResponse;
|
||||||
import org.elasticsearch.client.support.AbstractClient;
|
import org.elasticsearch.client.support.AbstractClient;
|
||||||
|
import org.elasticsearch.cluster.ClusterModule;
|
||||||
import org.elasticsearch.cluster.node.DiscoveryNode;
|
import org.elasticsearch.cluster.node.DiscoveryNode;
|
||||||
import org.elasticsearch.common.component.LifecycleComponent;
|
import org.elasticsearch.common.component.LifecycleComponent;
|
||||||
import org.elasticsearch.common.inject.Injector;
|
import org.elasticsearch.common.inject.Injector;
|
||||||
|
@ -140,6 +141,7 @@ public abstract class TransportClient extends AbstractClient {
|
||||||
List<NamedWriteableRegistry.Entry> entries = new ArrayList<>();
|
List<NamedWriteableRegistry.Entry> entries = new ArrayList<>();
|
||||||
entries.addAll(NetworkModule.getNamedWriteables());
|
entries.addAll(NetworkModule.getNamedWriteables());
|
||||||
entries.addAll(searchModule.getNamedWriteables());
|
entries.addAll(searchModule.getNamedWriteables());
|
||||||
|
entries.addAll(ClusterModule.getNamedWriteables());
|
||||||
entries.addAll(pluginsService.filterPlugins(Plugin.class).stream()
|
entries.addAll(pluginsService.filterPlugins(Plugin.class).stream()
|
||||||
.flatMap(p -> p.getNamedWriteables().stream())
|
.flatMap(p -> p.getNamedWriteables().stream())
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
|
|
|
@ -40,12 +40,7 @@ public abstract class AbstractDiffable<T extends Diffable<T>> implements Diffabl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static <T extends Diffable<T>> Diff<T> readDiffFrom(Reader<T> reader, StreamInput in) throws IOException {
|
||||||
public Diff<T> readDiffFrom(StreamInput in) throws IOException {
|
|
||||||
return new CompleteDiff<>(this, in);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T extends Diffable<T>> Diff<T> readDiffFrom(T reader, StreamInput in) throws IOException {
|
|
||||||
return new CompleteDiff<T>(reader, in);
|
return new CompleteDiff<T>(reader, in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,9 +66,9 @@ public abstract class AbstractDiffable<T extends Diffable<T>> implements Diffabl
|
||||||
/**
|
/**
|
||||||
* Read simple diff from the stream
|
* Read simple diff from the stream
|
||||||
*/
|
*/
|
||||||
public CompleteDiff(Diffable<T> reader, StreamInput in) throws IOException {
|
public CompleteDiff(Reader<T> reader, StreamInput in) throws IOException {
|
||||||
if (in.readBoolean()) {
|
if (in.readBoolean()) {
|
||||||
this.part = reader.readFrom(in);
|
this.part = reader.read(in);
|
||||||
} else {
|
} else {
|
||||||
this.part = null;
|
this.part = null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.cluster;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.common.Nullable;
|
||||||
|
import org.elasticsearch.common.io.stream.NamedWriteable;
|
||||||
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract diffable object with simple diffs implementation that sends the entire object if object has changed or
|
||||||
|
* nothing is object remained the same. Comparing to AbstractDiffable, this class also works with NamedWriteables
|
||||||
|
*/
|
||||||
|
public abstract class AbstractNamedDiffable<T extends NamedDiffable<T>> implements Diffable<T>, NamedWriteable {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Diff<T> diff(T previousState) {
|
||||||
|
if (this.get().equals(previousState)) {
|
||||||
|
return new CompleteNamedDiff<>(previousState.getWriteableName(), previousState.getMinimalSupportedVersion());
|
||||||
|
} else {
|
||||||
|
return new CompleteNamedDiff<>(get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends NamedDiffable<T>> NamedDiff<T> readDiffFrom(Class<? extends T> tClass, String name, StreamInput in)
|
||||||
|
throws IOException {
|
||||||
|
return new CompleteNamedDiff<>(tClass, name, in);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CompleteNamedDiff<T extends NamedDiffable<T>> implements NamedDiff<T> {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final T part;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A non-null value is only required for write operation, if the diff was just read from the stream the version
|
||||||
|
* is unnecessary.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
private final Version minimalSupportedVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates simple diff with changes
|
||||||
|
*/
|
||||||
|
public CompleteNamedDiff(T part) {
|
||||||
|
this.part = part;
|
||||||
|
this.name = part.getWriteableName();
|
||||||
|
this.minimalSupportedVersion = part.getMinimalSupportedVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates simple diff without changes
|
||||||
|
*/
|
||||||
|
public CompleteNamedDiff(String name, Version minimalSupportedVersion) {
|
||||||
|
this.part = null;
|
||||||
|
this.name = name;
|
||||||
|
this.minimalSupportedVersion = minimalSupportedVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read simple diff from the stream
|
||||||
|
*/
|
||||||
|
public CompleteNamedDiff(Class<? extends T> tClass, String name, StreamInput in) throws IOException {
|
||||||
|
if (in.readBoolean()) {
|
||||||
|
this.part = in.readNamedWriteable(tClass, name);
|
||||||
|
this.minimalSupportedVersion = part.getMinimalSupportedVersion();
|
||||||
|
} else {
|
||||||
|
this.part = null;
|
||||||
|
this.minimalSupportedVersion = null; // We just read this diff, so it's not going to be written
|
||||||
|
}
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
|
assert minimalSupportedVersion != null : "shouldn't be called on diff that was de-serialized from the stream";
|
||||||
|
if (part != null) {
|
||||||
|
out.writeBoolean(true);
|
||||||
|
part.writeTo(out);
|
||||||
|
} else {
|
||||||
|
out.writeBoolean(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T apply(T part) {
|
||||||
|
if (this.part != null) {
|
||||||
|
return this.part;
|
||||||
|
} else {
|
||||||
|
return part;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWriteableName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Version getMinimalSupportedVersion() {
|
||||||
|
assert minimalSupportedVersion != null : "shouldn't be called on the diff that was de-serialized from the stream";
|
||||||
|
return minimalSupportedVersion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public T get() {
|
||||||
|
return (T) this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -22,7 +22,9 @@ package org.elasticsearch.cluster;
|
||||||
import org.elasticsearch.cluster.action.index.MappingUpdatedAction;
|
import org.elasticsearch.cluster.action.index.MappingUpdatedAction;
|
||||||
import org.elasticsearch.cluster.action.index.NodeMappingRefreshAction;
|
import org.elasticsearch.cluster.action.index.NodeMappingRefreshAction;
|
||||||
import org.elasticsearch.cluster.action.shard.ShardStateAction;
|
import org.elasticsearch.cluster.action.shard.ShardStateAction;
|
||||||
|
import org.elasticsearch.cluster.metadata.IndexGraveyard;
|
||||||
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
|
||||||
|
import org.elasticsearch.cluster.metadata.MetaData;
|
||||||
import org.elasticsearch.cluster.metadata.MetaDataCreateIndexService;
|
import org.elasticsearch.cluster.metadata.MetaDataCreateIndexService;
|
||||||
import org.elasticsearch.cluster.metadata.MetaDataDeleteIndexService;
|
import org.elasticsearch.cluster.metadata.MetaDataDeleteIndexService;
|
||||||
import org.elasticsearch.cluster.metadata.MetaDataIndexAliasesService;
|
import org.elasticsearch.cluster.metadata.MetaDataIndexAliasesService;
|
||||||
|
@ -30,6 +32,7 @@ import org.elasticsearch.cluster.metadata.MetaDataIndexStateService;
|
||||||
import org.elasticsearch.cluster.metadata.MetaDataIndexTemplateService;
|
import org.elasticsearch.cluster.metadata.MetaDataIndexTemplateService;
|
||||||
import org.elasticsearch.cluster.metadata.MetaDataMappingService;
|
import org.elasticsearch.cluster.metadata.MetaDataMappingService;
|
||||||
import org.elasticsearch.cluster.metadata.MetaDataUpdateSettingsService;
|
import org.elasticsearch.cluster.metadata.MetaDataUpdateSettingsService;
|
||||||
|
import org.elasticsearch.cluster.metadata.RepositoriesMetaData;
|
||||||
import org.elasticsearch.cluster.routing.DelayedAllocationService;
|
import org.elasticsearch.cluster.routing.DelayedAllocationService;
|
||||||
import org.elasticsearch.cluster.routing.RoutingService;
|
import org.elasticsearch.cluster.routing.RoutingService;
|
||||||
import org.elasticsearch.cluster.routing.allocation.AllocationService;
|
import org.elasticsearch.cluster.routing.allocation.AllocationService;
|
||||||
|
@ -52,15 +55,25 @@ import org.elasticsearch.cluster.routing.allocation.decider.ShardsLimitAllocatio
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.SnapshotInProgressAllocationDecider;
|
import org.elasticsearch.cluster.routing.allocation.decider.SnapshotInProgressAllocationDecider;
|
||||||
import org.elasticsearch.cluster.routing.allocation.decider.ThrottlingAllocationDecider;
|
import org.elasticsearch.cluster.routing.allocation.decider.ThrottlingAllocationDecider;
|
||||||
import org.elasticsearch.cluster.service.ClusterService;
|
import org.elasticsearch.cluster.service.ClusterService;
|
||||||
|
import org.elasticsearch.common.ParseField;
|
||||||
import org.elasticsearch.common.inject.AbstractModule;
|
import org.elasticsearch.common.inject.AbstractModule;
|
||||||
|
import org.elasticsearch.common.io.stream.NamedWriteable;
|
||||||
|
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||||
|
import org.elasticsearch.common.io.stream.NamedWriteableRegistry.Entry;
|
||||||
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
|
import org.elasticsearch.common.io.stream.Writeable.Reader;
|
||||||
import org.elasticsearch.common.settings.ClusterSettings;
|
import org.elasticsearch.common.settings.ClusterSettings;
|
||||||
import org.elasticsearch.common.settings.Setting;
|
import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Setting.Property;
|
import org.elasticsearch.common.settings.Setting.Property;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
|
||||||
import org.elasticsearch.gateway.GatewayAllocator;
|
import org.elasticsearch.gateway.GatewayAllocator;
|
||||||
|
import org.elasticsearch.ingest.IngestMetadata;
|
||||||
import org.elasticsearch.plugins.ClusterPlugin;
|
import org.elasticsearch.plugins.ClusterPlugin;
|
||||||
|
import org.elasticsearch.script.ScriptMetaData;
|
||||||
import org.elasticsearch.tasks.TaskResultsService;
|
import org.elasticsearch.tasks.TaskResultsService;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
@ -94,6 +107,52 @@ public class ClusterModule extends AbstractModule {
|
||||||
indexNameExpressionResolver = new IndexNameExpressionResolver(settings);
|
indexNameExpressionResolver = new IndexNameExpressionResolver(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static List<Entry> getNamedWriteables() {
|
||||||
|
List<Entry> entries = new ArrayList<>();
|
||||||
|
// Cluster State
|
||||||
|
registerClusterCustom(entries, SnapshotsInProgress.TYPE, SnapshotsInProgress::new, SnapshotsInProgress::readDiffFrom);
|
||||||
|
registerClusterCustom(entries, RestoreInProgress.TYPE, RestoreInProgress::new, RestoreInProgress::readDiffFrom);
|
||||||
|
registerClusterCustom(entries, SnapshotDeletionsInProgress.TYPE, SnapshotDeletionsInProgress::new,
|
||||||
|
SnapshotDeletionsInProgress::readDiffFrom);
|
||||||
|
// Metadata
|
||||||
|
registerMetaDataCustom(entries, RepositoriesMetaData.TYPE, RepositoriesMetaData::new, RepositoriesMetaData::readDiffFrom);
|
||||||
|
registerMetaDataCustom(entries, IngestMetadata.TYPE, IngestMetadata::new, IngestMetadata::readDiffFrom);
|
||||||
|
registerMetaDataCustom(entries, ScriptMetaData.TYPE, ScriptMetaData::new, ScriptMetaData::readDiffFrom);
|
||||||
|
registerMetaDataCustom(entries, IndexGraveyard.TYPE, IndexGraveyard::new, IndexGraveyard::readDiffFrom);
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<NamedXContentRegistry.Entry> getNamedXWriteables() {
|
||||||
|
List<NamedXContentRegistry.Entry> entries = new ArrayList<>();
|
||||||
|
// Metadata
|
||||||
|
entries.add(new NamedXContentRegistry.Entry(MetaData.Custom.class, new ParseField(RepositoriesMetaData.TYPE),
|
||||||
|
RepositoriesMetaData::fromXContent));
|
||||||
|
entries.add(new NamedXContentRegistry.Entry(MetaData.Custom.class, new ParseField(IngestMetadata.TYPE),
|
||||||
|
IngestMetadata::fromXContent));
|
||||||
|
entries.add(new NamedXContentRegistry.Entry(MetaData.Custom.class, new ParseField(ScriptMetaData.TYPE),
|
||||||
|
ScriptMetaData::fromXContent));
|
||||||
|
entries.add(new NamedXContentRegistry.Entry(MetaData.Custom.class, new ParseField(IndexGraveyard.TYPE),
|
||||||
|
IndexGraveyard::fromXContent));
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T extends ClusterState.Custom> void registerClusterCustom(List<Entry> entries, String name, Reader<? extends T> reader,
|
||||||
|
Reader<NamedDiff> diffReader) {
|
||||||
|
registerCustom(entries, ClusterState.Custom.class, name, reader, diffReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T extends MetaData.Custom> void registerMetaDataCustom(List<Entry> entries, String name, Reader<? extends T> reader,
|
||||||
|
Reader<NamedDiff> diffReader) {
|
||||||
|
registerCustom(entries, MetaData.Custom.class, name, reader, diffReader);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T extends NamedWriteable> void registerCustom(List<Entry> entries, Class<T> category, String name,
|
||||||
|
Reader<? extends T> reader, Reader<NamedDiff> diffReader) {
|
||||||
|
entries.add(new Entry(category, name, reader));
|
||||||
|
entries.add(new Entry(NamedDiff.class, name, diffReader));
|
||||||
|
}
|
||||||
|
|
||||||
public IndexNameExpressionResolver getIndexNameExpressionResolver() {
|
public IndexNameExpressionResolver getIndexNameExpressionResolver() {
|
||||||
return indexNameExpressionResolver;
|
return indexNameExpressionResolver;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ package org.elasticsearch.cluster;
|
||||||
import com.carrotsearch.hppc.cursors.IntObjectCursor;
|
import com.carrotsearch.hppc.cursors.IntObjectCursor;
|
||||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||||
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.cluster.block.ClusterBlock;
|
import org.elasticsearch.cluster.block.ClusterBlock;
|
||||||
import org.elasticsearch.cluster.block.ClusterBlocks;
|
import org.elasticsearch.cluster.block.ClusterBlocks;
|
||||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||||
|
@ -38,7 +38,6 @@ import org.elasticsearch.cluster.routing.RoutingNodes;
|
||||||
import org.elasticsearch.cluster.routing.RoutingTable;
|
import org.elasticsearch.cluster.routing.RoutingTable;
|
||||||
import org.elasticsearch.cluster.routing.ShardRouting;
|
import org.elasticsearch.cluster.routing.ShardRouting;
|
||||||
import org.elasticsearch.cluster.service.ClusterService;
|
import org.elasticsearch.cluster.service.ClusterService;
|
||||||
import org.elasticsearch.common.Nullable;
|
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.UUIDs;
|
import org.elasticsearch.common.UUIDs;
|
||||||
import org.elasticsearch.common.bytes.BytesArray;
|
import org.elasticsearch.common.bytes.BytesArray;
|
||||||
|
@ -46,6 +45,8 @@ import org.elasticsearch.common.bytes.BytesReference;
|
||||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||||
import org.elasticsearch.common.compress.CompressedXContent;
|
import org.elasticsearch.common.compress.CompressedXContent;
|
||||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||||
|
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
|
||||||
|
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
|
@ -87,36 +88,12 @@ import java.util.Set;
|
||||||
*/
|
*/
|
||||||
public class ClusterState implements ToXContent, Diffable<ClusterState> {
|
public class ClusterState implements ToXContent, Diffable<ClusterState> {
|
||||||
|
|
||||||
public static final ClusterState PROTO = builder(ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY)).build();
|
public static final ClusterState EMPTY_STATE = builder(ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY)).build();
|
||||||
|
|
||||||
public interface Custom extends Diffable<Custom>, ToXContent {
|
public interface Custom extends NamedDiffable<Custom>, ToXContent {
|
||||||
|
|
||||||
String type();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Map<String, Custom> customPrototypes = new HashMap<>();
|
private static final NamedDiffableValueSerializer<Custom> CUSTOM_VALUE_SERIALIZER = new NamedDiffableValueSerializer<>(Custom.class);
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a custom index meta data factory. Make sure to call it from a static block.
|
|
||||||
*/
|
|
||||||
public static void registerPrototype(String type, Custom proto) {
|
|
||||||
customPrototypes.put(type, proto);
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
// register non plugin custom parts
|
|
||||||
registerPrototype(SnapshotsInProgress.TYPE, SnapshotsInProgress.PROTO);
|
|
||||||
registerPrototype(RestoreInProgress.TYPE, RestoreInProgress.PROTO);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static <T extends Custom> T lookupPrototype(String type) {
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
T proto = (T) customPrototypes.get(type);
|
|
||||||
if (proto == null) {
|
|
||||||
throw new IllegalArgumentException("No custom state prototype registered for type [" + type + "], node likely missing plugins");
|
|
||||||
}
|
|
||||||
return proto;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final String UNKNOWN_UUID = "_na_";
|
public static final String UNKNOWN_UUID = "_na_";
|
||||||
|
|
||||||
|
@ -659,53 +636,39 @@ public class ClusterState implements ToXContent, Diffable<ClusterState> {
|
||||||
* @param data input bytes
|
* @param data input bytes
|
||||||
* @param localNode used to set the local node in the cluster state.
|
* @param localNode used to set the local node in the cluster state.
|
||||||
*/
|
*/
|
||||||
public static ClusterState fromBytes(byte[] data, DiscoveryNode localNode) throws IOException {
|
public static ClusterState fromBytes(byte[] data, DiscoveryNode localNode, NamedWriteableRegistry registry) throws IOException {
|
||||||
return readFrom(StreamInput.wrap(data), localNode);
|
StreamInput in = new NamedWriteableAwareStreamInput(StreamInput.wrap(data), registry);
|
||||||
|
return readFrom(in, localNode);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param in input stream
|
|
||||||
* @param localNode used to set the local node in the cluster state. can be null.
|
|
||||||
*/
|
|
||||||
public static ClusterState readFrom(StreamInput in, @Nullable DiscoveryNode localNode) throws IOException {
|
|
||||||
return PROTO.readFrom(in, localNode);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Diff diff(ClusterState previousState) {
|
public Diff<ClusterState> diff(ClusterState previousState) {
|
||||||
return new ClusterStateDiff(previousState, this);
|
return new ClusterStateDiff(previousState, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static Diff<ClusterState> readDiffFrom(StreamInput in, DiscoveryNode localNode) throws IOException {
|
||||||
public Diff<ClusterState> readDiffFrom(StreamInput in) throws IOException {
|
return new ClusterStateDiff(in, localNode);
|
||||||
return new ClusterStateDiff(in, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClusterState readFrom(StreamInput in, DiscoveryNode localNode) throws IOException {
|
public static ClusterState readFrom(StreamInput in, DiscoveryNode localNode) throws IOException {
|
||||||
ClusterName clusterName = new ClusterName(in);
|
ClusterName clusterName = new ClusterName(in);
|
||||||
Builder builder = new Builder(clusterName);
|
Builder builder = new Builder(clusterName);
|
||||||
builder.version = in.readLong();
|
builder.version = in.readLong();
|
||||||
builder.uuid = in.readString();
|
builder.uuid = in.readString();
|
||||||
builder.metaData = MetaData.Builder.readFrom(in);
|
builder.metaData = MetaData.readFrom(in);
|
||||||
builder.routingTable = RoutingTable.Builder.readFrom(in);
|
builder.routingTable = RoutingTable.readFrom(in);
|
||||||
builder.nodes = DiscoveryNodes.Builder.readFrom(in, localNode);
|
builder.nodes = DiscoveryNodes.readFrom(in, localNode);
|
||||||
builder.blocks = ClusterBlocks.Builder.readClusterBlocks(in);
|
builder.blocks = new ClusterBlocks(in);
|
||||||
int customSize = in.readVInt();
|
int customSize = in.readVInt();
|
||||||
for (int i = 0; i < customSize; i++) {
|
for (int i = 0; i < customSize; i++) {
|
||||||
String type = in.readString();
|
Custom customIndexMetaData = in.readNamedWriteable(Custom.class);
|
||||||
Custom customIndexMetaData = lookupPrototype(type).readFrom(in);
|
builder.putCustom(customIndexMetaData.getWriteableName(), customIndexMetaData);
|
||||||
builder.putCustom(type, customIndexMetaData);
|
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ClusterState readFrom(StreamInput in) throws IOException {
|
|
||||||
return readFrom(in, nodes.getLocalNode());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeTo(StreamOutput out) throws IOException {
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
clusterName.writeTo(out);
|
clusterName.writeTo(out);
|
||||||
|
@ -715,10 +678,18 @@ public class ClusterState implements ToXContent, Diffable<ClusterState> {
|
||||||
routingTable.writeTo(out);
|
routingTable.writeTo(out);
|
||||||
nodes.writeTo(out);
|
nodes.writeTo(out);
|
||||||
blocks.writeTo(out);
|
blocks.writeTo(out);
|
||||||
out.writeVInt(customs.size());
|
// filter out custom states not supported by the other node
|
||||||
for (ObjectObjectCursor<String, Custom> cursor : customs) {
|
int numberOfCustoms = 0;
|
||||||
out.writeString(cursor.key);
|
for (ObjectCursor<Custom> cursor : customs.values()) {
|
||||||
cursor.value.writeTo(out);
|
if (out.getVersion().onOrAfter(cursor.value.getMinimalSupportedVersion())) {
|
||||||
|
numberOfCustoms++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.writeVInt(numberOfCustoms);
|
||||||
|
for (ObjectCursor<Custom> cursor : customs.values()) {
|
||||||
|
if (out.getVersion().onOrAfter(cursor.value.getMinimalSupportedVersion())) {
|
||||||
|
out.writeNamedWriteable(cursor.value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -751,30 +722,19 @@ public class ClusterState implements ToXContent, Diffable<ClusterState> {
|
||||||
nodes = after.nodes.diff(before.nodes);
|
nodes = after.nodes.diff(before.nodes);
|
||||||
metaData = after.metaData.diff(before.metaData);
|
metaData = after.metaData.diff(before.metaData);
|
||||||
blocks = after.blocks.diff(before.blocks);
|
blocks = after.blocks.diff(before.blocks);
|
||||||
customs = DiffableUtils.diff(before.customs, after.customs, DiffableUtils.getStringKeySerializer());
|
customs = DiffableUtils.diff(before.customs, after.customs, DiffableUtils.getStringKeySerializer(), CUSTOM_VALUE_SERIALIZER);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClusterStateDiff(StreamInput in, ClusterState proto) throws IOException {
|
public ClusterStateDiff(StreamInput in, DiscoveryNode localNode) throws IOException {
|
||||||
clusterName = new ClusterName(in);
|
clusterName = new ClusterName(in);
|
||||||
fromUuid = in.readString();
|
fromUuid = in.readString();
|
||||||
toUuid = in.readString();
|
toUuid = in.readString();
|
||||||
toVersion = in.readLong();
|
toVersion = in.readLong();
|
||||||
routingTable = proto.routingTable.readDiffFrom(in);
|
routingTable = RoutingTable.readDiffFrom(in);
|
||||||
nodes = proto.nodes.readDiffFrom(in);
|
nodes = DiscoveryNodes.readDiffFrom(in, localNode);
|
||||||
metaData = proto.metaData.readDiffFrom(in);
|
metaData = MetaData.readDiffFrom(in);
|
||||||
blocks = proto.blocks.readDiffFrom(in);
|
blocks = ClusterBlocks.readDiffFrom(in);
|
||||||
customs = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(),
|
customs = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), CUSTOM_VALUE_SERIALIZER);
|
||||||
new DiffableUtils.DiffableValueSerializer<String, Custom>() {
|
|
||||||
@Override
|
|
||||||
public Custom read(StreamInput in, String key) throws IOException {
|
|
||||||
return lookupPrototype(key).readFrom(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Diff<Custom> readDiff(StreamInput in, String key) throws IOException {
|
|
||||||
return lookupPrototype(key).readDiffFrom(in);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,6 +18,8 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.cluster;
|
package org.elasticsearch.cluster;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.Nullable;
|
||||||
|
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -27,10 +29,10 @@ public interface ClusterStateTaskExecutor<T> {
|
||||||
* Update the cluster state based on the current state and the given tasks. Return the *same instance* if no state
|
* Update the cluster state based on the current state and the given tasks. Return the *same instance* if no state
|
||||||
* should be changed.
|
* should be changed.
|
||||||
*/
|
*/
|
||||||
BatchResult<T> execute(ClusterState currentState, List<T> tasks) throws Exception;
|
ClusterTasksResult<T> execute(ClusterState currentState, List<T> tasks) throws Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* indicates whether this task should only run if current node is master
|
* indicates whether this executor should only run if the current node is master
|
||||||
*/
|
*/
|
||||||
default boolean runOnlyOnMaster() {
|
default boolean runOnlyOnMaster() {
|
||||||
return true;
|
return true;
|
||||||
|
@ -68,18 +70,22 @@ public interface ClusterStateTaskExecutor<T> {
|
||||||
* Represents the result of a batched execution of cluster state update tasks
|
* Represents the result of a batched execution of cluster state update tasks
|
||||||
* @param <T> the type of the cluster state update task
|
* @param <T> the type of the cluster state update task
|
||||||
*/
|
*/
|
||||||
class BatchResult<T> {
|
class ClusterTasksResult<T> {
|
||||||
|
public final boolean noMaster;
|
||||||
|
@Nullable
|
||||||
public final ClusterState resultingState;
|
public final ClusterState resultingState;
|
||||||
public final Map<T, TaskResult> executionResults;
|
public final Map<T, TaskResult> executionResults;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct an execution result instance with a correspondence between the tasks and their execution result
|
* Construct an execution result instance with a correspondence between the tasks and their execution result
|
||||||
|
* @param noMaster whether this node steps down as master or has lost connection to the master
|
||||||
* @param resultingState the resulting cluster state
|
* @param resultingState the resulting cluster state
|
||||||
* @param executionResults the correspondence between tasks and their outcome
|
* @param executionResults the correspondence between tasks and their outcome
|
||||||
*/
|
*/
|
||||||
BatchResult(ClusterState resultingState, Map<T, TaskResult> executionResults) {
|
ClusterTasksResult(boolean noMaster, ClusterState resultingState, Map<T, TaskResult> executionResults) {
|
||||||
this.resultingState = resultingState;
|
this.resultingState = resultingState;
|
||||||
this.executionResults = executionResults;
|
this.executionResults = executionResults;
|
||||||
|
this.noMaster = noMaster;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> Builder<T> builder() {
|
public static <T> Builder<T> builder() {
|
||||||
|
@ -117,8 +123,13 @@ public interface ClusterStateTaskExecutor<T> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BatchResult<T> build(ClusterState resultingState) {
|
public ClusterTasksResult<T> build(ClusterState resultingState) {
|
||||||
return new BatchResult<>(resultingState, executionResults);
|
return new ClusterTasksResult<>(false, resultingState, executionResults);
|
||||||
|
}
|
||||||
|
|
||||||
|
ClusterTasksResult<T> build(ClusterTasksResult<T> result, ClusterState previousState) {
|
||||||
|
return new ClusterTasksResult<>(result.noMaster, result.resultingState == null ? previousState : result.resultingState,
|
||||||
|
executionResults);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,9 +41,9 @@ public abstract class ClusterStateUpdateTask implements ClusterStateTaskConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final BatchResult<ClusterStateUpdateTask> execute(ClusterState currentState, List<ClusterStateUpdateTask> tasks) throws Exception {
|
public final ClusterTasksResult<ClusterStateUpdateTask> execute(ClusterState currentState, List<ClusterStateUpdateTask> tasks) throws Exception {
|
||||||
ClusterState result = execute(currentState);
|
ClusterState result = execute(currentState);
|
||||||
return BatchResult.<ClusterStateUpdateTask>builder().successes(tasks).build(result);
|
return ClusterTasksResult.<ClusterStateUpdateTask>builder().successes(tasks).build(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -75,4 +75,13 @@ public abstract class ClusterStateUpdateTask implements ClusterStateTaskConfig,
|
||||||
public Priority priority() {
|
public Priority priority() {
|
||||||
return priority;
|
return priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marked as final as cluster state update tasks should only run on master.
|
||||||
|
* For local requests, use {@link LocalClusterUpdateTask} instead.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final boolean runOnlyOnMaster() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,13 +34,4 @@ public interface Diffable<T> extends Writeable {
|
||||||
*/
|
*/
|
||||||
Diff<T> diff(T previousState);
|
Diff<T> diff(T previousState);
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads the {@link org.elasticsearch.cluster.Diff} from StreamInput
|
|
||||||
*/
|
|
||||||
Diff<T> readDiffFrom(StreamInput in) throws IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads an object of this type from the provided {@linkplain StreamInput}. The receiving instance remains unchanged.
|
|
||||||
*/
|
|
||||||
T readFrom(StreamInput in) throws IOException;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,12 @@ import com.carrotsearch.hppc.cursors.IntCursor;
|
||||||
import com.carrotsearch.hppc.cursors.IntObjectCursor;
|
import com.carrotsearch.hppc.cursors.IntObjectCursor;
|
||||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||||
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.common.collect.ImmutableOpenIntMap;
|
import org.elasticsearch.common.collect.ImmutableOpenIntMap;
|
||||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
|
import org.elasticsearch.common.io.stream.Writeable.Reader;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -74,7 +76,7 @@ public final class DiffableUtils {
|
||||||
/**
|
/**
|
||||||
* Calculates diff between two ImmutableOpenMaps of non-diffable objects
|
* Calculates diff between two ImmutableOpenMaps of non-diffable objects
|
||||||
*/
|
*/
|
||||||
public static <K, T> MapDiff<K, T, ImmutableOpenMap<K, T>> diff(ImmutableOpenMap<K, T> before, ImmutableOpenMap<K, T> after, KeySerializer<K> keySerializer, NonDiffableValueSerializer<K, T> valueSerializer) {
|
public static <K, T> MapDiff<K, T, ImmutableOpenMap<K, T>> diff(ImmutableOpenMap<K, T> before, ImmutableOpenMap<K, T> after, KeySerializer<K> keySerializer, ValueSerializer<K, T> valueSerializer) {
|
||||||
assert after != null && before != null;
|
assert after != null && before != null;
|
||||||
return new ImmutableOpenMapDiff<>(before, after, keySerializer, valueSerializer);
|
return new ImmutableOpenMapDiff<>(before, after, keySerializer, valueSerializer);
|
||||||
}
|
}
|
||||||
|
@ -90,7 +92,7 @@ public final class DiffableUtils {
|
||||||
/**
|
/**
|
||||||
* Calculates diff between two ImmutableOpenIntMaps of non-diffable objects
|
* Calculates diff between two ImmutableOpenIntMaps of non-diffable objects
|
||||||
*/
|
*/
|
||||||
public static <T> MapDiff<Integer, T, ImmutableOpenIntMap<T>> diff(ImmutableOpenIntMap<T> before, ImmutableOpenIntMap<T> after, KeySerializer<Integer> keySerializer, NonDiffableValueSerializer<Integer, T> valueSerializer) {
|
public static <T> MapDiff<Integer, T, ImmutableOpenIntMap<T>> diff(ImmutableOpenIntMap<T> before, ImmutableOpenIntMap<T> after, KeySerializer<Integer> keySerializer, ValueSerializer<Integer, T> valueSerializer) {
|
||||||
assert after != null && before != null;
|
assert after != null && before != null;
|
||||||
return new ImmutableOpenIntMapDiff<>(before, after, keySerializer, valueSerializer);
|
return new ImmutableOpenIntMapDiff<>(before, after, keySerializer, valueSerializer);
|
||||||
}
|
}
|
||||||
|
@ -106,7 +108,7 @@ public final class DiffableUtils {
|
||||||
/**
|
/**
|
||||||
* Calculates diff between two Maps of non-diffable objects
|
* Calculates diff between two Maps of non-diffable objects
|
||||||
*/
|
*/
|
||||||
public static <K, T> MapDiff<K, T, Map<K, T>> diff(Map<K, T> before, Map<K, T> after, KeySerializer<K> keySerializer, NonDiffableValueSerializer<K, T> valueSerializer) {
|
public static <K, T> MapDiff<K, T, Map<K, T>> diff(Map<K, T> before, Map<K, T> after, KeySerializer<K> keySerializer, ValueSerializer<K, T> valueSerializer) {
|
||||||
assert after != null && before != null;
|
assert after != null && before != null;
|
||||||
return new JdkMapDiff<>(before, after, keySerializer, valueSerializer);
|
return new JdkMapDiff<>(before, after, keySerializer, valueSerializer);
|
||||||
}
|
}
|
||||||
|
@ -135,22 +137,22 @@ public final class DiffableUtils {
|
||||||
/**
|
/**
|
||||||
* Loads an object that represents difference between two ImmutableOpenMaps of Diffable objects using Diffable proto object
|
* Loads an object that represents difference between two ImmutableOpenMaps of Diffable objects using Diffable proto object
|
||||||
*/
|
*/
|
||||||
public static <K, T extends Diffable<T>> MapDiff<K, T, ImmutableOpenMap<K, T>> readImmutableOpenMapDiff(StreamInput in, KeySerializer<K> keySerializer, T proto) throws IOException {
|
public static <K, T extends Diffable<T>> MapDiff<K, T, ImmutableOpenMap<K, T>> readImmutableOpenMapDiff(StreamInput in, KeySerializer<K> keySerializer, Reader<T> reader, Reader<Diff<T>> diffReader) throws IOException {
|
||||||
return new ImmutableOpenMapDiff<>(in, keySerializer, new DiffablePrototypeValueReader<>(proto));
|
return new ImmutableOpenMapDiff<>(in, keySerializer, new DiffableValueReader<>(reader, diffReader));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads an object that represents difference between two ImmutableOpenIntMaps of Diffable objects using Diffable proto object
|
* Loads an object that represents difference between two ImmutableOpenIntMaps of Diffable objects using Diffable proto object
|
||||||
*/
|
*/
|
||||||
public static <T extends Diffable<T>> MapDiff<Integer, T, ImmutableOpenIntMap<T>> readImmutableOpenIntMapDiff(StreamInput in, KeySerializer<Integer> keySerializer, T proto) throws IOException {
|
public static <T extends Diffable<T>> MapDiff<Integer, T, ImmutableOpenIntMap<T>> readImmutableOpenIntMapDiff(StreamInput in, KeySerializer<Integer> keySerializer, Reader<T> reader, Reader<Diff<T>> diffReader) throws IOException {
|
||||||
return new ImmutableOpenIntMapDiff<>(in, keySerializer, new DiffablePrototypeValueReader<>(proto));
|
return new ImmutableOpenIntMapDiff<>(in, keySerializer, new DiffableValueReader<>(reader, diffReader));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads an object that represents difference between two Maps of Diffable objects using Diffable proto object
|
* Loads an object that represents difference between two Maps of Diffable objects using Diffable proto object
|
||||||
*/
|
*/
|
||||||
public static <K, T extends Diffable<T>> MapDiff<K, T, Map<K, T>> readJdkMapDiff(StreamInput in, KeySerializer<K> keySerializer, T proto) throws IOException {
|
public static <K, T extends Diffable<T>> MapDiff<K, T, Map<K, T>> readJdkMapDiff(StreamInput in, KeySerializer<K> keySerializer, Reader<T> reader, Reader<Diff<T>> diffReader) throws IOException {
|
||||||
return new JdkMapDiff<>(in, keySerializer, new DiffablePrototypeValueReader<>(proto));
|
return new JdkMapDiff<>(in, keySerializer, new DiffableValueReader<>(reader, diffReader));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -214,12 +216,17 @@ public final class DiffableUtils {
|
||||||
*
|
*
|
||||||
* @param <T> the object type
|
* @param <T> the object type
|
||||||
*/
|
*/
|
||||||
private static class ImmutableOpenMapDiff<K, T> extends MapDiff<K, T, ImmutableOpenMap<K, T>> {
|
public static class ImmutableOpenMapDiff<K, T> extends MapDiff<K, T, ImmutableOpenMap<K, T>> {
|
||||||
|
|
||||||
protected ImmutableOpenMapDiff(StreamInput in, KeySerializer<K> keySerializer, ValueSerializer<K, T> valueSerializer) throws IOException {
|
protected ImmutableOpenMapDiff(StreamInput in, KeySerializer<K> keySerializer, ValueSerializer<K, T> valueSerializer) throws IOException {
|
||||||
super(in, keySerializer, valueSerializer);
|
super(in, keySerializer, valueSerializer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ImmutableOpenMapDiff(KeySerializer<K> keySerializer, ValueSerializer<K, T> valueSerializer,
|
||||||
|
List<K> deletes, Map<K, Diff<T>> diffs, Map<K, T> upserts) {
|
||||||
|
super(keySerializer, valueSerializer, deletes, diffs, upserts);
|
||||||
|
}
|
||||||
|
|
||||||
public ImmutableOpenMapDiff(ImmutableOpenMap<K, T> before, ImmutableOpenMap<K, T> after,
|
public ImmutableOpenMapDiff(ImmutableOpenMap<K, T> before, ImmutableOpenMap<K, T> after,
|
||||||
KeySerializer<K> keySerializer, ValueSerializer<K, T> valueSerializer) {
|
KeySerializer<K> keySerializer, ValueSerializer<K, T> valueSerializer) {
|
||||||
super(keySerializer, valueSerializer);
|
super(keySerializer, valueSerializer);
|
||||||
|
@ -245,6 +252,21 @@ public final class DiffableUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new diff map with the given key removed, does not modify the invoking instance.
|
||||||
|
* If the key does not exist in the diff map, the same instance is returned.
|
||||||
|
*/
|
||||||
|
public ImmutableOpenMapDiff<K, T> withKeyRemoved(K key) {
|
||||||
|
if (this.diffs.containsKey(key) == false && this.upserts.containsKey(key) == false) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
Map<K, Diff<T>> newDiffs = new HashMap<>(this.diffs);
|
||||||
|
newDiffs.remove(key);
|
||||||
|
Map<K, T> newUpserts = new HashMap<>(this.upserts);
|
||||||
|
newUpserts.remove(key);
|
||||||
|
return new ImmutableOpenMapDiff<>(this.keySerializer, this.valueSerializer, this.deletes, newDiffs, newUpserts);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ImmutableOpenMap<K, T> apply(ImmutableOpenMap<K, T> map) {
|
public ImmutableOpenMap<K, T> apply(ImmutableOpenMap<K, T> map) {
|
||||||
ImmutableOpenMap.Builder<K, T> builder = ImmutableOpenMap.builder();
|
ImmutableOpenMap.Builder<K, T> builder = ImmutableOpenMap.builder();
|
||||||
|
@ -346,6 +368,15 @@ public final class DiffableUtils {
|
||||||
upserts = new HashMap<>();
|
upserts = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected MapDiff(KeySerializer<K> keySerializer, ValueSerializer<K, T> valueSerializer,
|
||||||
|
List<K> deletes, Map<K, Diff<T>> diffs, Map<K, T> upserts) {
|
||||||
|
this.keySerializer = keySerializer;
|
||||||
|
this.valueSerializer = valueSerializer;
|
||||||
|
this.deletes = deletes;
|
||||||
|
this.diffs = diffs;
|
||||||
|
this.upserts = upserts;
|
||||||
|
}
|
||||||
|
|
||||||
protected MapDiff(StreamInput in, KeySerializer<K> keySerializer, ValueSerializer<K, T> valueSerializer) throws IOException {
|
protected MapDiff(StreamInput in, KeySerializer<K> keySerializer, ValueSerializer<K, T> valueSerializer) throws IOException {
|
||||||
this.keySerializer = keySerializer;
|
this.keySerializer = keySerializer;
|
||||||
this.valueSerializer = valueSerializer;
|
this.valueSerializer = valueSerializer;
|
||||||
|
@ -406,12 +437,29 @@ public final class DiffableUtils {
|
||||||
for (K delete : deletes) {
|
for (K delete : deletes) {
|
||||||
keySerializer.writeKey(delete, out);
|
keySerializer.writeKey(delete, out);
|
||||||
}
|
}
|
||||||
out.writeVInt(diffs.size());
|
Version version = out.getVersion();
|
||||||
|
// filter out custom states not supported by the other node
|
||||||
|
int diffCount = 0;
|
||||||
|
for (Diff<T> diff : diffs.values()) {
|
||||||
|
if(valueSerializer.supportsVersion(diff, version)) {
|
||||||
|
diffCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.writeVInt(diffCount);
|
||||||
for (Map.Entry<K, Diff<T>> entry : diffs.entrySet()) {
|
for (Map.Entry<K, Diff<T>> entry : diffs.entrySet()) {
|
||||||
|
if(valueSerializer.supportsVersion(entry.getValue(), version)) {
|
||||||
keySerializer.writeKey(entry.getKey(), out);
|
keySerializer.writeKey(entry.getKey(), out);
|
||||||
valueSerializer.writeDiff(entry.getValue(), out);
|
valueSerializer.writeDiff(entry.getValue(), out);
|
||||||
}
|
}
|
||||||
out.writeVInt(upserts.size());
|
}
|
||||||
|
// filter out custom states not supported by the other node
|
||||||
|
int upsertsCount = 0;
|
||||||
|
for (T upsert : upserts.values()) {
|
||||||
|
if(valueSerializer.supportsVersion(upsert, version)) {
|
||||||
|
upsertsCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.writeVInt(upsertsCount);
|
||||||
for (Map.Entry<K, T> entry : upserts.entrySet()) {
|
for (Map.Entry<K, T> entry : upserts.entrySet()) {
|
||||||
keySerializer.writeKey(entry.getKey(), out);
|
keySerializer.writeKey(entry.getKey(), out);
|
||||||
valueSerializer.write(entry.getValue(), out);
|
valueSerializer.write(entry.getValue(), out);
|
||||||
|
@ -511,6 +559,20 @@ public final class DiffableUtils {
|
||||||
*/
|
*/
|
||||||
boolean supportsDiffableValues();
|
boolean supportsDiffableValues();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this serializer supports the version of the output stream
|
||||||
|
*/
|
||||||
|
default boolean supportsVersion(Diff<V> value, Version version) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this serializer supports the version of the output stream
|
||||||
|
*/
|
||||||
|
default boolean supportsVersion(V value, Version version) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes diff if this serializer supports diffable values
|
* Computes diff if this serializer supports diffable values
|
||||||
*/
|
*/
|
||||||
|
@ -600,25 +662,27 @@ public final class DiffableUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implementation of the ValueSerializer that uses a prototype object for reading operations
|
* Implementation of the ValueSerializer that wraps value and diff readers.
|
||||||
*
|
*
|
||||||
* Note: this implementation is ignoring the key.
|
* Note: this implementation is ignoring the key.
|
||||||
*/
|
*/
|
||||||
public static class DiffablePrototypeValueReader<K, V extends Diffable<V>> extends DiffableValueSerializer<K, V> {
|
public static class DiffableValueReader<K, V extends Diffable<V>> extends DiffableValueSerializer<K, V> {
|
||||||
private final V proto;
|
private final Reader<V> reader;
|
||||||
|
private final Reader<Diff<V>> diffReader;
|
||||||
|
|
||||||
public DiffablePrototypeValueReader(V proto) {
|
public DiffableValueReader(Reader<V> reader, Reader<Diff<V>> diffReader) {
|
||||||
this.proto = proto;
|
this.reader = reader;
|
||||||
|
this.diffReader = diffReader;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public V read(StreamInput in, K key) throws IOException {
|
public V read(StreamInput in, K key) throws IOException {
|
||||||
return proto.readFrom(in);
|
return reader.read(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Diff<V> readDiff(StreamInput in, K key) throws IOException {
|
public Diff<V> readDiff(StreamInput in, K key) throws IOException {
|
||||||
return proto.readDiffFrom(in);
|
return diffReader.read(in);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,93 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
package org.elasticsearch.cluster;
|
||||||
|
|
||||||
|
import org.elasticsearch.common.Nullable;
|
||||||
|
import org.elasticsearch.common.Priority;
|
||||||
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to apply state updates on nodes that are not necessarily master
|
||||||
|
*/
|
||||||
|
public abstract class LocalClusterUpdateTask implements ClusterStateTaskConfig, ClusterStateTaskExecutor<LocalClusterUpdateTask>,
|
||||||
|
ClusterStateTaskListener {
|
||||||
|
|
||||||
|
private final Priority priority;
|
||||||
|
|
||||||
|
public LocalClusterUpdateTask() {
|
||||||
|
this(Priority.NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LocalClusterUpdateTask(Priority priority) {
|
||||||
|
this.priority = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract ClusterTasksResult<LocalClusterUpdateTask> execute(ClusterState currentState) throws Exception;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final ClusterTasksResult<LocalClusterUpdateTask> execute(ClusterState currentState,
|
||||||
|
List<LocalClusterUpdateTask> tasks) throws Exception {
|
||||||
|
assert tasks.size() == 1 && tasks.get(0) == this : "expected one-element task list containing current object but was " + tasks;
|
||||||
|
ClusterTasksResult<LocalClusterUpdateTask> result = execute(currentState);
|
||||||
|
return ClusterTasksResult.<LocalClusterUpdateTask>builder().successes(tasks).build(result, currentState);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* node stepped down as master or has lost connection to the master
|
||||||
|
*/
|
||||||
|
public static ClusterTasksResult<LocalClusterUpdateTask> noMaster() {
|
||||||
|
return new ClusterTasksResult(true, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* no changes were made to the cluster state. Useful to execute a runnable on the cluster state applier thread
|
||||||
|
*/
|
||||||
|
public static ClusterTasksResult<LocalClusterUpdateTask> unchanged() {
|
||||||
|
return new ClusterTasksResult(false, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* locally apply cluster state received from a master
|
||||||
|
*/
|
||||||
|
public static ClusterTasksResult<LocalClusterUpdateTask> newState(ClusterState clusterState) {
|
||||||
|
return new ClusterTasksResult(false, clusterState, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String describeTasks(List<LocalClusterUpdateTask> tasks) {
|
||||||
|
return ""; // one of task, source is enough
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public TimeValue timeout() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Priority priority() {
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final boolean runOnlyOnMaster() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.cluster;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.common.io.stream.NamedWriteable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Diff that also support NamedWriteable interface
|
||||||
|
*/
|
||||||
|
public interface NamedDiff<T extends Diffable<T>> extends Diff<T>, NamedWriteable {
|
||||||
|
/**
|
||||||
|
* The minimal version of the recipient this custom object can be sent to
|
||||||
|
*/
|
||||||
|
default Version getMinimalSupportedVersion() {
|
||||||
|
return Version.CURRENT.minimumCompatibilityVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.cluster;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.common.io.stream.NamedWriteable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Diff that also support NamedWriteable interface
|
||||||
|
*/
|
||||||
|
public interface NamedDiffable<T> extends Diffable<T>, NamedWriteable {
|
||||||
|
/**
|
||||||
|
* The minimal version of the recipient this custom object can be sent to
|
||||||
|
*/
|
||||||
|
default Version getMinimalSupportedVersion() {
|
||||||
|
return Version.CURRENT.minimumCompatibilityVersion();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.cluster;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value Serializer for named diffables
|
||||||
|
*/
|
||||||
|
public class NamedDiffableValueSerializer<T extends NamedDiffable<T>> extends DiffableUtils.DiffableValueSerializer<String, T> {
|
||||||
|
|
||||||
|
private final Class<T> tClass;
|
||||||
|
|
||||||
|
public NamedDiffableValueSerializer(Class<T> tClass) {
|
||||||
|
this.tClass = tClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T read(StreamInput in, String key) throws IOException {
|
||||||
|
return in.readNamedWriteable(tClass, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsVersion(Diff<T> value, Version version) {
|
||||||
|
return version.onOrAfter(((NamedDiff<?>)value).getMinimalSupportedVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsVersion(T value, Version version) {
|
||||||
|
return version.onOrAfter(value.getMinimalSupportedVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public Diff<T> readDiff(StreamInput in, String key) throws IOException {
|
||||||
|
return in.readNamedWriteable(NamedDiff.class, key);
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,12 +39,10 @@ import java.util.Objects;
|
||||||
/**
|
/**
|
||||||
* Meta data about restore processes that are currently executing
|
* Meta data about restore processes that are currently executing
|
||||||
*/
|
*/
|
||||||
public class RestoreInProgress extends AbstractDiffable<Custom> implements Custom {
|
public class RestoreInProgress extends AbstractNamedDiffable<Custom> implements Custom {
|
||||||
|
|
||||||
public static final String TYPE = "restore";
|
public static final String TYPE = "restore";
|
||||||
|
|
||||||
public static final RestoreInProgress PROTO = new RestoreInProgress();
|
|
||||||
|
|
||||||
private final List<Entry> entries;
|
private final List<Entry> entries;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -377,15 +375,15 @@ public class RestoreInProgress extends AbstractDiffable<Custom> implements Custo
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String type() {
|
public String getWriteableName() {
|
||||||
return TYPE;
|
return TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public static NamedDiff<Custom> readDiffFrom(StreamInput in) throws IOException {
|
||||||
* {@inheritDoc}
|
return readDiffFrom(Custom.class, TYPE, in);
|
||||||
*/
|
}
|
||||||
@Override
|
|
||||||
public RestoreInProgress readFrom(StreamInput in) throws IOException {
|
public RestoreInProgress(StreamInput in) throws IOException {
|
||||||
Entry[] entries = new Entry[in.readVInt()];
|
Entry[] entries = new Entry[in.readVInt()];
|
||||||
for (int i = 0; i < entries.length; i++) {
|
for (int i = 0; i < entries.length; i++) {
|
||||||
Snapshot snapshot = new Snapshot(in);
|
Snapshot snapshot = new Snapshot(in);
|
||||||
|
@ -404,7 +402,7 @@ public class RestoreInProgress extends AbstractDiffable<Custom> implements Custo
|
||||||
}
|
}
|
||||||
entries[i] = new Entry(snapshot, state, Collections.unmodifiableList(indexBuilder), builder.build());
|
entries[i] = new Entry(snapshot, state, Collections.unmodifiableList(indexBuilder), builder.build());
|
||||||
}
|
}
|
||||||
return new RestoreInProgress(entries);
|
this.entries = Arrays.asList(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,220 @@
|
||||||
|
/*
|
||||||
|
* Licensed to Elasticsearch under one or more contributor
|
||||||
|
* license agreements. See the NOTICE file distributed with
|
||||||
|
* this work for additional information regarding copyright
|
||||||
|
* ownership. Elasticsearch licenses this file to you under
|
||||||
|
* the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
* not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing,
|
||||||
|
* software distributed under the License is distributed on an
|
||||||
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
|
* KIND, either express or implied. See the License for the
|
||||||
|
* specific language governing permissions and limitations
|
||||||
|
* under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.cluster;
|
||||||
|
|
||||||
|
import org.elasticsearch.Version;
|
||||||
|
import org.elasticsearch.cluster.ClusterState.Custom;
|
||||||
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.snapshots.Snapshot;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class that represents the snapshot deletions that are in progress in the cluster.
|
||||||
|
*/
|
||||||
|
public class SnapshotDeletionsInProgress extends AbstractNamedDiffable<Custom> implements Custom {
|
||||||
|
|
||||||
|
public static final String TYPE = "snapshot_deletions";
|
||||||
|
// the version where SnapshotDeletionsInProgress was introduced
|
||||||
|
public static final Version VERSION_INTRODUCED = Version.V_5_2_0_UNRELEASED;
|
||||||
|
|
||||||
|
// the list of snapshot deletion request entries
|
||||||
|
private final List<Entry> entries;
|
||||||
|
|
||||||
|
private SnapshotDeletionsInProgress(List<Entry> entries) {
|
||||||
|
this.entries = Collections.unmodifiableList(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SnapshotDeletionsInProgress(StreamInput in) throws IOException {
|
||||||
|
this.entries = Collections.unmodifiableList(in.readList(Entry::new));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new instance of {@link SnapshotDeletionsInProgress} with the given
|
||||||
|
* {@link Entry} added.
|
||||||
|
*/
|
||||||
|
public static SnapshotDeletionsInProgress newInstance(Entry entry) {
|
||||||
|
return new SnapshotDeletionsInProgress(Collections.singletonList(entry));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new instance of {@link SnapshotDeletionsInProgress} which adds
|
||||||
|
* the given {@link Entry} to the invoking instance.
|
||||||
|
*/
|
||||||
|
public SnapshotDeletionsInProgress withAddedEntry(Entry entry) {
|
||||||
|
List<Entry> entries = new ArrayList<>(getEntries());
|
||||||
|
entries.add(entry);
|
||||||
|
return new SnapshotDeletionsInProgress(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new instance of {@link SnapshotDeletionsInProgress} which removes
|
||||||
|
* the given entry from the invoking instance.
|
||||||
|
*/
|
||||||
|
public SnapshotDeletionsInProgress withRemovedEntry(Entry entry) {
|
||||||
|
List<Entry> entries = new ArrayList<>(getEntries());
|
||||||
|
entries.remove(entry);
|
||||||
|
return new SnapshotDeletionsInProgress(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an unmodifiable list of snapshot deletion entries.
|
||||||
|
*/
|
||||||
|
public List<Entry> getEntries() {
|
||||||
|
return entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns {@code true} if there are snapshot deletions in progress in the cluster,
|
||||||
|
* returns {@code false} otherwise.
|
||||||
|
*/
|
||||||
|
public boolean hasDeletionsInProgress() {
|
||||||
|
return entries.isEmpty() == false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWriteableName() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SnapshotDeletionsInProgress that = (SnapshotDeletionsInProgress) o;
|
||||||
|
return entries.equals(that.entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 31 + entries.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
|
out.writeList(entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NamedDiff<Custom> readDiffFrom(StreamInput in) throws IOException {
|
||||||
|
return readDiffFrom(Custom.class, TYPE, in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Version getMinimalSupportedVersion() {
|
||||||
|
return VERSION_INTRODUCED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
|
||||||
|
builder.startArray(TYPE);
|
||||||
|
for (Entry entry : entries) {
|
||||||
|
builder.startObject();
|
||||||
|
{
|
||||||
|
builder.field("repository", entry.snapshot.getRepository());
|
||||||
|
builder.field("snapshot", entry.snapshot.getSnapshotId().getName());
|
||||||
|
builder.timeValueField("start_time_millis", "start_time", entry.startTime);
|
||||||
|
builder.field("repository_state_id", entry.repositoryStateId);
|
||||||
|
}
|
||||||
|
builder.endObject();
|
||||||
|
}
|
||||||
|
builder.endArray();
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class representing a snapshot deletion request entry in the cluster state.
|
||||||
|
*/
|
||||||
|
public static final class Entry implements Writeable {
|
||||||
|
private final Snapshot snapshot;
|
||||||
|
private final long startTime;
|
||||||
|
private final long repositoryStateId;
|
||||||
|
|
||||||
|
public Entry(Snapshot snapshot, long startTime, long repositoryStateId) {
|
||||||
|
this.snapshot = snapshot;
|
||||||
|
this.startTime = startTime;
|
||||||
|
this.repositoryStateId = repositoryStateId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Entry(StreamInput in) throws IOException {
|
||||||
|
this.snapshot = new Snapshot(in);
|
||||||
|
this.startTime = in.readVLong();
|
||||||
|
this.repositoryStateId = in.readLong();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The snapshot to delete.
|
||||||
|
*/
|
||||||
|
public Snapshot getSnapshot() {
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The start time in milliseconds for deleting the snapshots.
|
||||||
|
*/
|
||||||
|
public long getStartTime() {
|
||||||
|
return startTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The repository state id at the time the snapshot deletion began.
|
||||||
|
*/
|
||||||
|
public long getRepositoryStateId() {
|
||||||
|
return repositoryStateId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Entry that = (Entry) o;
|
||||||
|
return snapshot.equals(that.snapshot)
|
||||||
|
&& startTime == that.startTime
|
||||||
|
&& repositoryStateId == that.repositoryStateId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(snapshot, startTime, repositoryStateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeTo(StreamOutput out) throws IOException {
|
||||||
|
snapshot.writeTo(out);
|
||||||
|
out.writeVLong(startTime);
|
||||||
|
out.writeLong(repositoryStateId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,7 @@ package org.elasticsearch.cluster;
|
||||||
import com.carrotsearch.hppc.ObjectContainer;
|
import com.carrotsearch.hppc.ObjectContainer;
|
||||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||||
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
||||||
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.cluster.ClusterState.Custom;
|
import org.elasticsearch.cluster.ClusterState.Custom;
|
||||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
|
@ -43,10 +44,14 @@ import java.util.Map;
|
||||||
/**
|
/**
|
||||||
* Meta data about snapshots that are currently executing
|
* Meta data about snapshots that are currently executing
|
||||||
*/
|
*/
|
||||||
public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Custom {
|
public class SnapshotsInProgress extends AbstractNamedDiffable<Custom> implements Custom {
|
||||||
public static final String TYPE = "snapshots";
|
public static final String TYPE = "snapshots";
|
||||||
|
|
||||||
public static final SnapshotsInProgress PROTO = new SnapshotsInProgress();
|
// denotes an undefined repository state id, which will happen when receiving a cluster state with
|
||||||
|
// a snapshot in progress from a pre 5.2.x node
|
||||||
|
public static final long UNDEFINED_REPOSITORY_STATE_ID = -2L;
|
||||||
|
// the version where repository state ids were introduced
|
||||||
|
private static final Version REPOSITORY_ID_INTRODUCED_VERSION = Version.V_5_2_0_UNRELEASED;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
|
@ -74,9 +79,10 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
||||||
private final List<IndexId> indices;
|
private final List<IndexId> indices;
|
||||||
private final ImmutableOpenMap<String, List<ShardId>> waitingIndices;
|
private final ImmutableOpenMap<String, List<ShardId>> waitingIndices;
|
||||||
private final long startTime;
|
private final long startTime;
|
||||||
|
private final long repositoryStateId;
|
||||||
|
|
||||||
public Entry(Snapshot snapshot, boolean includeGlobalState, boolean partial, State state, List<IndexId> indices,
|
public Entry(Snapshot snapshot, boolean includeGlobalState, boolean partial, State state, List<IndexId> indices,
|
||||||
long startTime, ImmutableOpenMap<ShardId, ShardSnapshotStatus> shards) {
|
long startTime, long repositoryStateId, ImmutableOpenMap<ShardId, ShardSnapshotStatus> shards) {
|
||||||
this.state = state;
|
this.state = state;
|
||||||
this.snapshot = snapshot;
|
this.snapshot = snapshot;
|
||||||
this.includeGlobalState = includeGlobalState;
|
this.includeGlobalState = includeGlobalState;
|
||||||
|
@ -90,10 +96,12 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
||||||
this.shards = shards;
|
this.shards = shards;
|
||||||
this.waitingIndices = findWaitingIndices(shards);
|
this.waitingIndices = findWaitingIndices(shards);
|
||||||
}
|
}
|
||||||
|
this.repositoryStateId = repositoryStateId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entry(Entry entry, State state, ImmutableOpenMap<ShardId, ShardSnapshotStatus> shards) {
|
public Entry(Entry entry, State state, ImmutableOpenMap<ShardId, ShardSnapshotStatus> shards) {
|
||||||
this(entry.snapshot, entry.includeGlobalState, entry.partial, state, entry.indices, entry.startTime, shards);
|
this(entry.snapshot, entry.includeGlobalState, entry.partial, state, entry.indices, entry.startTime,
|
||||||
|
entry.repositoryStateId, shards);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Entry(Entry entry, ImmutableOpenMap<ShardId, ShardSnapshotStatus> shards) {
|
public Entry(Entry entry, ImmutableOpenMap<ShardId, ShardSnapshotStatus> shards) {
|
||||||
|
@ -132,6 +140,10 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
||||||
return startTime;
|
return startTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getRepositoryStateId() {
|
||||||
|
return repositoryStateId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
@ -147,6 +159,7 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
||||||
if (!snapshot.equals(entry.snapshot)) return false;
|
if (!snapshot.equals(entry.snapshot)) return false;
|
||||||
if (state != entry.state) return false;
|
if (state != entry.state) return false;
|
||||||
if (!waitingIndices.equals(entry.waitingIndices)) return false;
|
if (!waitingIndices.equals(entry.waitingIndices)) return false;
|
||||||
|
if (repositoryStateId != entry.repositoryStateId) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -161,6 +174,7 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
||||||
result = 31 * result + indices.hashCode();
|
result = 31 * result + indices.hashCode();
|
||||||
result = 31 * result + waitingIndices.hashCode();
|
result = 31 * result + waitingIndices.hashCode();
|
||||||
result = 31 * result + Long.hashCode(startTime);
|
result = 31 * result + Long.hashCode(startTime);
|
||||||
|
result = 31 * result + Long.hashCode(repositoryStateId);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -361,12 +375,15 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String type() {
|
public String getWriteableName() {
|
||||||
return TYPE;
|
return TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static NamedDiff<Custom> readDiffFrom(StreamInput in) throws IOException {
|
||||||
public SnapshotsInProgress readFrom(StreamInput in) throws IOException {
|
return readDiffFrom(Custom.class, TYPE, in);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SnapshotsInProgress(StreamInput in) throws IOException {
|
||||||
Entry[] entries = new Entry[in.readVInt()];
|
Entry[] entries = new Entry[in.readVInt()];
|
||||||
for (int i = 0; i < entries.length; i++) {
|
for (int i = 0; i < entries.length; i++) {
|
||||||
Snapshot snapshot = new Snapshot(in);
|
Snapshot snapshot = new Snapshot(in);
|
||||||
|
@ -387,15 +404,20 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
||||||
State shardState = State.fromValue(in.readByte());
|
State shardState = State.fromValue(in.readByte());
|
||||||
builder.put(shardId, new ShardSnapshotStatus(nodeId, shardState));
|
builder.put(shardId, new ShardSnapshotStatus(nodeId, shardState));
|
||||||
}
|
}
|
||||||
|
long repositoryStateId = UNDEFINED_REPOSITORY_STATE_ID;
|
||||||
|
if (in.getVersion().onOrAfter(REPOSITORY_ID_INTRODUCED_VERSION)) {
|
||||||
|
repositoryStateId = in.readLong();
|
||||||
|
}
|
||||||
entries[i] = new Entry(snapshot,
|
entries[i] = new Entry(snapshot,
|
||||||
includeGlobalState,
|
includeGlobalState,
|
||||||
partial,
|
partial,
|
||||||
state,
|
state,
|
||||||
Collections.unmodifiableList(indexBuilder),
|
Collections.unmodifiableList(indexBuilder),
|
||||||
startTime,
|
startTime,
|
||||||
|
repositoryStateId,
|
||||||
builder.build());
|
builder.build());
|
||||||
}
|
}
|
||||||
return new SnapshotsInProgress(entries);
|
this.entries = Arrays.asList(entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -417,6 +439,9 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
||||||
out.writeOptionalString(shardEntry.value.nodeId());
|
out.writeOptionalString(shardEntry.value.nodeId());
|
||||||
out.writeByte(shardEntry.value.state().value());
|
out.writeByte(shardEntry.value.state().value());
|
||||||
}
|
}
|
||||||
|
if (out.getVersion().onOrAfter(REPOSITORY_ID_INTRODUCED_VERSION)) {
|
||||||
|
out.writeLong(entry.repositoryStateId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,6 +455,7 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
||||||
private static final String INDICES = "indices";
|
private static final String INDICES = "indices";
|
||||||
private static final String START_TIME_MILLIS = "start_time_millis";
|
private static final String START_TIME_MILLIS = "start_time_millis";
|
||||||
private static final String START_TIME = "start_time";
|
private static final String START_TIME = "start_time";
|
||||||
|
private static final String REPOSITORY_STATE_ID = "repository_state_id";
|
||||||
private static final String SHARDS = "shards";
|
private static final String SHARDS = "shards";
|
||||||
private static final String INDEX = "index";
|
private static final String INDEX = "index";
|
||||||
private static final String SHARD = "shard";
|
private static final String SHARD = "shard";
|
||||||
|
@ -461,6 +487,7 @@ public class SnapshotsInProgress extends AbstractDiffable<Custom> implements Cus
|
||||||
}
|
}
|
||||||
builder.endArray();
|
builder.endArray();
|
||||||
builder.timeValueField(START_TIME_MILLIS, START_TIME, entry.startTime());
|
builder.timeValueField(START_TIME_MILLIS, START_TIME, entry.startTime());
|
||||||
|
builder.field(REPOSITORY_STATE_ID, entry.getRepositoryStateId());
|
||||||
builder.startArray(SHARDS);
|
builder.startArray(SHARDS);
|
||||||
{
|
{
|
||||||
for (ObjectObjectCursor<ShardId, ShardSnapshotStatus> shardEntry : entry.shards) {
|
for (ObjectObjectCursor<ShardId, ShardSnapshotStatus> shardEntry : entry.shards) {
|
||||||
|
|
|
@ -25,10 +25,10 @@ import org.apache.logging.log4j.util.Supplier;
|
||||||
import org.elasticsearch.ElasticsearchException;
|
import org.elasticsearch.ElasticsearchException;
|
||||||
import org.elasticsearch.ExceptionsHelper;
|
import org.elasticsearch.ExceptionsHelper;
|
||||||
import org.elasticsearch.cluster.ClusterChangedEvent;
|
import org.elasticsearch.cluster.ClusterChangedEvent;
|
||||||
|
import org.elasticsearch.cluster.ClusterStateTaskExecutor;
|
||||||
import org.elasticsearch.cluster.ClusterState;
|
import org.elasticsearch.cluster.ClusterState;
|
||||||
import org.elasticsearch.cluster.ClusterStateObserver;
|
import org.elasticsearch.cluster.ClusterStateObserver;
|
||||||
import org.elasticsearch.cluster.ClusterStateTaskConfig;
|
import org.elasticsearch.cluster.ClusterStateTaskConfig;
|
||||||
import org.elasticsearch.cluster.ClusterStateTaskExecutor;
|
|
||||||
import org.elasticsearch.cluster.ClusterStateTaskListener;
|
import org.elasticsearch.cluster.ClusterStateTaskListener;
|
||||||
import org.elasticsearch.cluster.MasterNodeChangePredicate;
|
import org.elasticsearch.cluster.MasterNodeChangePredicate;
|
||||||
import org.elasticsearch.cluster.NotMasterException;
|
import org.elasticsearch.cluster.NotMasterException;
|
||||||
|
@ -260,8 +260,8 @@ public class ShardStateAction extends AbstractComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BatchResult<ShardEntry> execute(ClusterState currentState, List<ShardEntry> tasks) throws Exception {
|
public ClusterTasksResult<ShardEntry> execute(ClusterState currentState, List<ShardEntry> tasks) throws Exception {
|
||||||
BatchResult.Builder<ShardEntry> batchResultBuilder = BatchResult.builder();
|
ClusterTasksResult.Builder<ShardEntry> batchResultBuilder = ClusterTasksResult.builder();
|
||||||
List<ShardEntry> tasksToBeApplied = new ArrayList<>();
|
List<ShardEntry> tasksToBeApplied = new ArrayList<>();
|
||||||
List<FailedShard> failedShardsToBeApplied = new ArrayList<>();
|
List<FailedShard> failedShardsToBeApplied = new ArrayList<>();
|
||||||
List<StaleShard> staleShardsToBeApplied = new ArrayList<>();
|
List<StaleShard> staleShardsToBeApplied = new ArrayList<>();
|
||||||
|
@ -394,8 +394,8 @@ public class ShardStateAction extends AbstractComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BatchResult<ShardEntry> execute(ClusterState currentState, List<ShardEntry> tasks) throws Exception {
|
public ClusterTasksResult<ShardEntry> execute(ClusterState currentState, List<ShardEntry> tasks) throws Exception {
|
||||||
BatchResult.Builder<ShardEntry> builder = BatchResult.builder();
|
ClusterTasksResult.Builder<ShardEntry> builder = ClusterTasksResult.builder();
|
||||||
List<ShardEntry> tasksToBeApplied = new ArrayList<>();
|
List<ShardEntry> tasksToBeApplied = new ArrayList<>();
|
||||||
List<ShardRouting> shardRoutingsToBeApplied = new ArrayList<>(tasks.size());
|
List<ShardRouting> shardRoutingsToBeApplied = new ArrayList<>(tasks.size());
|
||||||
Set<ShardRouting> seenShardRoutings = new HashSet<>(); // to prevent duplicates
|
Set<ShardRouting> seenShardRoutings = new HashSet<>(); // to prevent duplicates
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.cluster.block;
|
||||||
|
|
||||||
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
||||||
import org.elasticsearch.cluster.AbstractDiffable;
|
import org.elasticsearch.cluster.AbstractDiffable;
|
||||||
|
import org.elasticsearch.cluster.Diff;
|
||||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||||
import org.elasticsearch.cluster.metadata.MetaDataIndexStateService;
|
import org.elasticsearch.cluster.metadata.MetaDataIndexStateService;
|
||||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||||
|
@ -48,8 +49,6 @@ import static java.util.stream.Stream.concat;
|
||||||
public class ClusterBlocks extends AbstractDiffable<ClusterBlocks> {
|
public class ClusterBlocks extends AbstractDiffable<ClusterBlocks> {
|
||||||
public static final ClusterBlocks EMPTY_CLUSTER_BLOCK = new ClusterBlocks(emptySet(), ImmutableOpenMap.of());
|
public static final ClusterBlocks EMPTY_CLUSTER_BLOCK = new ClusterBlocks(emptySet(), ImmutableOpenMap.of());
|
||||||
|
|
||||||
public static final ClusterBlocks PROTO = EMPTY_CLUSTER_BLOCK;
|
|
||||||
|
|
||||||
private final Set<ClusterBlock> global;
|
private final Set<ClusterBlock> global;
|
||||||
|
|
||||||
private final ImmutableOpenMap<String, Set<ClusterBlock>> indicesBlocks;
|
private final ImmutableOpenMap<String, Set<ClusterBlock>> indicesBlocks;
|
||||||
|
@ -59,23 +58,7 @@ public class ClusterBlocks extends AbstractDiffable<ClusterBlocks> {
|
||||||
ClusterBlocks(Set<ClusterBlock> global, ImmutableOpenMap<String, Set<ClusterBlock>> indicesBlocks) {
|
ClusterBlocks(Set<ClusterBlock> global, ImmutableOpenMap<String, Set<ClusterBlock>> indicesBlocks) {
|
||||||
this.global = global;
|
this.global = global;
|
||||||
this.indicesBlocks = indicesBlocks;
|
this.indicesBlocks = indicesBlocks;
|
||||||
|
levelHolders = generateLevelHolders(global, indicesBlocks);
|
||||||
levelHolders = new ImmutableLevelHolder[ClusterBlockLevel.values().length];
|
|
||||||
for (final ClusterBlockLevel level : ClusterBlockLevel.values()) {
|
|
||||||
Predicate<ClusterBlock> containsLevel = block -> block.contains(level);
|
|
||||||
Set<ClusterBlock> newGlobal = unmodifiableSet(global.stream()
|
|
||||||
.filter(containsLevel)
|
|
||||||
.collect(toSet()));
|
|
||||||
|
|
||||||
ImmutableOpenMap.Builder<String, Set<ClusterBlock>> indicesBuilder = ImmutableOpenMap.builder();
|
|
||||||
for (ObjectObjectCursor<String, Set<ClusterBlock>> entry : indicesBlocks) {
|
|
||||||
indicesBuilder.put(entry.key, unmodifiableSet(entry.value.stream()
|
|
||||||
.filter(containsLevel)
|
|
||||||
.collect(toSet())));
|
|
||||||
}
|
|
||||||
|
|
||||||
levelHolders[level.id()] = new ImmutableLevelHolder(newGlobal, indicesBuilder.build());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<ClusterBlock> global() {
|
public Set<ClusterBlock> global() {
|
||||||
|
@ -98,6 +81,27 @@ public class ClusterBlocks extends AbstractDiffable<ClusterBlocks> {
|
||||||
return indices(level).getOrDefault(index, emptySet());
|
return indices(level).getOrDefault(index, emptySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ImmutableLevelHolder[] generateLevelHolders(Set<ClusterBlock> global,
|
||||||
|
ImmutableOpenMap<String, Set<ClusterBlock>> indicesBlocks) {
|
||||||
|
ImmutableLevelHolder[] levelHolders = new ImmutableLevelHolder[ClusterBlockLevel.values().length];
|
||||||
|
for (final ClusterBlockLevel level : ClusterBlockLevel.values()) {
|
||||||
|
Predicate<ClusterBlock> containsLevel = block -> block.contains(level);
|
||||||
|
Set<ClusterBlock> newGlobal = unmodifiableSet(global.stream()
|
||||||
|
.filter(containsLevel)
|
||||||
|
.collect(toSet()));
|
||||||
|
|
||||||
|
ImmutableOpenMap.Builder<String, Set<ClusterBlock>> indicesBuilder = ImmutableOpenMap.builder();
|
||||||
|
for (ObjectObjectCursor<String, Set<ClusterBlock>> entry : indicesBlocks) {
|
||||||
|
indicesBuilder.put(entry.key, unmodifiableSet(entry.value.stream()
|
||||||
|
.filter(containsLevel)
|
||||||
|
.collect(toSet())));
|
||||||
|
}
|
||||||
|
|
||||||
|
levelHolders[level.id()] = new ImmutableLevelHolder(newGlobal, indicesBuilder.build());
|
||||||
|
}
|
||||||
|
return levelHolders;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns <tt>true</tt> if one of the global blocks as its disable state persistence flag set.
|
* Returns <tt>true</tt> if one of the global blocks as its disable state persistence flag set.
|
||||||
*/
|
*/
|
||||||
|
@ -239,15 +243,16 @@ public class ClusterBlocks extends AbstractDiffable<ClusterBlocks> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public ClusterBlocks(StreamInput in) throws IOException {
|
||||||
public ClusterBlocks readFrom(StreamInput in) throws IOException {
|
|
||||||
Set<ClusterBlock> global = readBlockSet(in);
|
Set<ClusterBlock> global = readBlockSet(in);
|
||||||
int size = in.readVInt();
|
int size = in.readVInt();
|
||||||
ImmutableOpenMap.Builder<String, Set<ClusterBlock>> indicesBuilder = ImmutableOpenMap.builder(size);
|
ImmutableOpenMap.Builder<String, Set<ClusterBlock>> indicesBuilder = ImmutableOpenMap.builder(size);
|
||||||
for (int j = 0; j < size; j++) {
|
for (int j = 0; j < size; j++) {
|
||||||
indicesBuilder.put(in.readString().intern(), readBlockSet(in));
|
indicesBuilder.put(in.readString().intern(), readBlockSet(in));
|
||||||
}
|
}
|
||||||
return new ClusterBlocks(global, indicesBuilder.build());
|
this.global = global;
|
||||||
|
this.indicesBlocks = indicesBuilder.build();
|
||||||
|
levelHolders = generateLevelHolders(global, indicesBlocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<ClusterBlock> readBlockSet(StreamInput in) throws IOException {
|
private static Set<ClusterBlock> readBlockSet(StreamInput in) throws IOException {
|
||||||
|
@ -259,6 +264,10 @@ public class ClusterBlocks extends AbstractDiffable<ClusterBlocks> {
|
||||||
return unmodifiableSet(blocks);
|
return unmodifiableSet(blocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Diff<ClusterBlocks> readDiffFrom(StreamInput in) throws IOException {
|
||||||
|
return AbstractDiffable.readDiffFrom(ClusterBlocks::new, in);
|
||||||
|
}
|
||||||
|
|
||||||
static class ImmutableLevelHolder {
|
static class ImmutableLevelHolder {
|
||||||
|
|
||||||
static final ImmutableLevelHolder EMPTY = new ImmutableLevelHolder(emptySet(), ImmutableOpenMap.of());
|
static final ImmutableLevelHolder EMPTY = new ImmutableLevelHolder(emptySet(), ImmutableOpenMap.of());
|
||||||
|
@ -383,9 +392,5 @@ public class ClusterBlocks extends AbstractDiffable<ClusterBlocks> {
|
||||||
}
|
}
|
||||||
return new ClusterBlocks(unmodifiableSet(new HashSet<>(global)), indicesBuilder.build());
|
return new ClusterBlocks(unmodifiableSet(new HashSet<>(global)), indicesBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ClusterBlocks readClusterBlocks(StreamInput in) throws IOException {
|
|
||||||
return PROTO.readFrom(in);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.cluster.metadata;
|
||||||
|
|
||||||
import org.elasticsearch.ElasticsearchGenerationException;
|
import org.elasticsearch.ElasticsearchGenerationException;
|
||||||
import org.elasticsearch.cluster.AbstractDiffable;
|
import org.elasticsearch.cluster.AbstractDiffable;
|
||||||
|
import org.elasticsearch.cluster.Diff;
|
||||||
import org.elasticsearch.common.Strings;
|
import org.elasticsearch.common.Strings;
|
||||||
import org.elasticsearch.common.bytes.BytesArray;
|
import org.elasticsearch.common.bytes.BytesArray;
|
||||||
import org.elasticsearch.common.compress.CompressedXContent;
|
import org.elasticsearch.common.compress.CompressedXContent;
|
||||||
|
@ -41,8 +42,6 @@ import static java.util.Collections.emptySet;
|
||||||
|
|
||||||
public class AliasMetaData extends AbstractDiffable<AliasMetaData> {
|
public class AliasMetaData extends AbstractDiffable<AliasMetaData> {
|
||||||
|
|
||||||
public static final AliasMetaData PROTO = new AliasMetaData("", null, null, null);
|
|
||||||
|
|
||||||
private final String alias;
|
private final String alias;
|
||||||
|
|
||||||
private final CompressedXContent filter;
|
private final CompressedXContent filter;
|
||||||
|
@ -173,22 +172,29 @@ public class AliasMetaData extends AbstractDiffable<AliasMetaData> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public AliasMetaData(StreamInput in) throws IOException {
|
||||||
public AliasMetaData readFrom(StreamInput in) throws IOException {
|
alias = in.readString();
|
||||||
String alias = in.readString();
|
|
||||||
CompressedXContent filter = null;
|
|
||||||
if (in.readBoolean()) {
|
if (in.readBoolean()) {
|
||||||
filter = CompressedXContent.readCompressedString(in);
|
filter = CompressedXContent.readCompressedString(in);
|
||||||
|
} else {
|
||||||
|
filter = null;
|
||||||
}
|
}
|
||||||
String indexRouting = null;
|
|
||||||
if (in.readBoolean()) {
|
if (in.readBoolean()) {
|
||||||
indexRouting = in.readString();
|
indexRouting = in.readString();
|
||||||
|
} else {
|
||||||
|
indexRouting = null;
|
||||||
}
|
}
|
||||||
String searchRouting = null;
|
|
||||||
if (in.readBoolean()) {
|
if (in.readBoolean()) {
|
||||||
searchRouting = in.readString();
|
searchRouting = in.readString();
|
||||||
|
searchRoutingValues = Collections.unmodifiableSet(Strings.splitStringByCommaToSet(searchRouting));
|
||||||
|
} else {
|
||||||
|
searchRouting = null;
|
||||||
|
searchRoutingValues = emptySet();
|
||||||
}
|
}
|
||||||
return new AliasMetaData(alias, filter, indexRouting, searchRouting);
|
}
|
||||||
|
|
||||||
|
public static Diff<AliasMetaData> readDiffFrom(StreamInput in) throws IOException {
|
||||||
|
return readDiffFrom(AliasMetaData::new, in);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
@ -327,14 +333,6 @@ public class AliasMetaData extends AbstractDiffable<AliasMetaData> {
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeTo(AliasMetaData aliasMetaData, StreamOutput out) throws IOException {
|
|
||||||
aliasMetaData.writeTo(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AliasMetaData readFrom(StreamInput in) throws IOException {
|
|
||||||
return PROTO.readFrom(in);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,8 @@
|
||||||
package org.elasticsearch.cluster.metadata;
|
package org.elasticsearch.cluster.metadata;
|
||||||
|
|
||||||
import org.elasticsearch.cluster.Diff;
|
import org.elasticsearch.cluster.Diff;
|
||||||
|
import org.elasticsearch.cluster.NamedDiff;
|
||||||
import org.elasticsearch.common.ParseField;
|
import org.elasticsearch.common.ParseField;
|
||||||
import org.elasticsearch.common.ParseFieldMatcher;
|
|
||||||
import org.elasticsearch.common.ParseFieldMatcherSupplier;
|
|
||||||
import org.elasticsearch.common.io.stream.StreamInput;
|
import org.elasticsearch.common.io.stream.StreamInput;
|
||||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||||
import org.elasticsearch.common.io.stream.Writeable;
|
import org.elasticsearch.common.io.stream.Writeable;
|
||||||
|
@ -67,10 +66,9 @@ public final class IndexGraveyard implements MetaData.Custom {
|
||||||
500, // the default maximum number of tombstones
|
500, // the default maximum number of tombstones
|
||||||
Setting.Property.NodeScope);
|
Setting.Property.NodeScope);
|
||||||
|
|
||||||
public static final IndexGraveyard PROTO = new IndexGraveyard(new ArrayList<>());
|
|
||||||
public static final String TYPE = "index-graveyard";
|
public static final String TYPE = "index-graveyard";
|
||||||
private static final ParseField TOMBSTONES_FIELD = new ParseField("tombstones");
|
private static final ParseField TOMBSTONES_FIELD = new ParseField("tombstones");
|
||||||
private static final ObjectParser<List<Tombstone>, ParseFieldMatcherSupplier> GRAVEYARD_PARSER;
|
private static final ObjectParser<List<Tombstone>, Void> GRAVEYARD_PARSER;
|
||||||
static {
|
static {
|
||||||
GRAVEYARD_PARSER = new ObjectParser<>("index_graveyard", ArrayList::new);
|
GRAVEYARD_PARSER = new ObjectParser<>("index_graveyard", ArrayList::new);
|
||||||
GRAVEYARD_PARSER.declareObjectArray(List::addAll, Tombstone.getParser(), TOMBSTONES_FIELD);
|
GRAVEYARD_PARSER.declareObjectArray(List::addAll, Tombstone.getParser(), TOMBSTONES_FIELD);
|
||||||
|
@ -83,7 +81,7 @@ public final class IndexGraveyard implements MetaData.Custom {
|
||||||
tombstones = Collections.unmodifiableList(list);
|
tombstones = Collections.unmodifiableList(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IndexGraveyard(final StreamInput in) throws IOException {
|
public IndexGraveyard(final StreamInput in) throws IOException {
|
||||||
final int queueSize = in.readVInt();
|
final int queueSize = in.readVInt();
|
||||||
List<Tombstone> tombstones = new ArrayList<>(queueSize);
|
List<Tombstone> tombstones = new ArrayList<>(queueSize);
|
||||||
for (int i = 0; i < queueSize; i++) {
|
for (int i = 0; i < queueSize; i++) {
|
||||||
|
@ -92,12 +90,8 @@ public final class IndexGraveyard implements MetaData.Custom {
|
||||||
this.tombstones = Collections.unmodifiableList(tombstones);
|
this.tombstones = Collections.unmodifiableList(tombstones);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IndexGraveyard fromStream(final StreamInput in) throws IOException {
|
|
||||||
return new IndexGraveyard(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String type() {
|
public String getWriteableName() {
|
||||||
return TYPE;
|
return TYPE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,8 +138,8 @@ public final class IndexGraveyard implements MetaData.Custom {
|
||||||
return builder.endArray();
|
return builder.endArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IndexGraveyard fromXContent(final XContentParser parser) throws IOException {
|
public static IndexGraveyard fromXContent(final XContentParser parser) throws IOException {
|
||||||
return new IndexGraveyard(GRAVEYARD_PARSER.parse(parser, () -> ParseFieldMatcher.STRICT));
|
return new IndexGraveyard(GRAVEYARD_PARSER.parse(parser, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -161,19 +155,13 @@ public final class IndexGraveyard implements MetaData.Custom {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public IndexGraveyard readFrom(final StreamInput in) throws IOException {
|
|
||||||
return new IndexGraveyard(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Diff<MetaData.Custom> diff(final MetaData.Custom previous) {
|
public Diff<MetaData.Custom> diff(final MetaData.Custom previous) {
|
||||||
return new IndexGraveyardDiff((IndexGraveyard) previous, this);
|
return new IndexGraveyardDiff((IndexGraveyard) previous, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static NamedDiff<MetaData.Custom> readDiffFrom(final StreamInput in) throws IOException {
|
||||||
public Diff<MetaData.Custom> readDiffFrom(final StreamInput in) throws IOException {
|
|
||||||
return new IndexGraveyardDiff(in);
|
return new IndexGraveyardDiff(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,7 +261,7 @@ public final class IndexGraveyard implements MetaData.Custom {
|
||||||
/**
|
/**
|
||||||
* A class representing a diff of two IndexGraveyard objects.
|
* A class representing a diff of two IndexGraveyard objects.
|
||||||
*/
|
*/
|
||||||
public static final class IndexGraveyardDiff implements Diff<MetaData.Custom> {
|
public static final class IndexGraveyardDiff implements NamedDiff<MetaData.Custom> {
|
||||||
|
|
||||||
private final List<Tombstone> added;
|
private final List<Tombstone> added;
|
||||||
private final int removedCount;
|
private final int removedCount;
|
||||||
|
@ -349,6 +337,11 @@ public final class IndexGraveyard implements MetaData.Custom {
|
||||||
public int getRemovedCount() {
|
public int getRemovedCount() {
|
||||||
return removedCount;
|
return removedCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWriteableName() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -359,16 +352,17 @@ public final class IndexGraveyard implements MetaData.Custom {
|
||||||
private static final String INDEX_KEY = "index";
|
private static final String INDEX_KEY = "index";
|
||||||
private static final String DELETE_DATE_IN_MILLIS_KEY = "delete_date_in_millis";
|
private static final String DELETE_DATE_IN_MILLIS_KEY = "delete_date_in_millis";
|
||||||
private static final String DELETE_DATE_KEY = "delete_date";
|
private static final String DELETE_DATE_KEY = "delete_date";
|
||||||
private static final ObjectParser<Tombstone.Builder, ParseFieldMatcherSupplier> TOMBSTONE_PARSER;
|
private static final ObjectParser<Tombstone.Builder, Void> TOMBSTONE_PARSER;
|
||||||
static {
|
static {
|
||||||
TOMBSTONE_PARSER = new ObjectParser<>("tombstoneEntry", Tombstone.Builder::new);
|
TOMBSTONE_PARSER = new ObjectParser<>("tombstoneEntry", Tombstone.Builder::new);
|
||||||
TOMBSTONE_PARSER.declareObject(Tombstone.Builder::index, Index::parseIndex, new ParseField(INDEX_KEY));
|
TOMBSTONE_PARSER.declareObject(Tombstone.Builder::index, (parser, context) -> Index.fromXContent(parser),
|
||||||
|
new ParseField(INDEX_KEY));
|
||||||
TOMBSTONE_PARSER.declareLong(Tombstone.Builder::deleteDateInMillis, new ParseField(DELETE_DATE_IN_MILLIS_KEY));
|
TOMBSTONE_PARSER.declareLong(Tombstone.Builder::deleteDateInMillis, new ParseField(DELETE_DATE_IN_MILLIS_KEY));
|
||||||
TOMBSTONE_PARSER.declareString((b, s) -> {}, new ParseField(DELETE_DATE_KEY));
|
TOMBSTONE_PARSER.declareString((b, s) -> {}, new ParseField(DELETE_DATE_KEY));
|
||||||
}
|
}
|
||||||
|
|
||||||
static ContextParser<ParseFieldMatcherSupplier, Tombstone> getParser() {
|
static ContextParser<Void, Tombstone> getParser() {
|
||||||
return (p, c) -> TOMBSTONE_PARSER.apply(p, c).build();
|
return (parser, context) -> TOMBSTONE_PARSER.apply(parser, null).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Index index;
|
private final Index index;
|
||||||
|
@ -443,7 +437,7 @@ public final class IndexGraveyard implements MetaData.Custom {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Tombstone fromXContent(final XContentParser parser) throws IOException {
|
public static Tombstone fromXContent(final XContentParser parser) throws IOException {
|
||||||
return TOMBSTONE_PARSER.parse(parser, () -> ParseFieldMatcher.STRICT).build();
|
return TOMBSTONE_PARSER.parse(parser, null).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,7 +23,6 @@ import com.carrotsearch.hppc.LongArrayList;
|
||||||
import com.carrotsearch.hppc.cursors.IntObjectCursor;
|
import com.carrotsearch.hppc.cursors.IntObjectCursor;
|
||||||
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
import com.carrotsearch.hppc.cursors.ObjectCursor;
|
||||||
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
|
||||||
|
|
||||||
import org.elasticsearch.Version;
|
import org.elasticsearch.Version;
|
||||||
import org.elasticsearch.action.support.ActiveShardCount;
|
import org.elasticsearch.action.support.ActiveShardCount;
|
||||||
import org.elasticsearch.cluster.Diff;
|
import org.elasticsearch.cluster.Diff;
|
||||||
|
@ -34,7 +33,6 @@ import org.elasticsearch.cluster.block.ClusterBlockLevel;
|
||||||
import org.elasticsearch.cluster.node.DiscoveryNodeFilters;
|
import org.elasticsearch.cluster.node.DiscoveryNodeFilters;
|
||||||
import org.elasticsearch.cluster.routing.allocation.IndexMetaDataUpdater;
|
import org.elasticsearch.cluster.routing.allocation.IndexMetaDataUpdater;
|
||||||
import org.elasticsearch.common.Nullable;
|
import org.elasticsearch.common.Nullable;
|
||||||
import org.elasticsearch.common.ParseFieldMatcher;
|
|
||||||
import org.elasticsearch.common.bytes.BytesArray;
|
import org.elasticsearch.common.bytes.BytesArray;
|
||||||
import org.elasticsearch.common.collect.ImmutableOpenIntMap;
|
import org.elasticsearch.common.collect.ImmutableOpenIntMap;
|
||||||
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
import org.elasticsearch.common.collect.ImmutableOpenMap;
|
||||||
|
@ -46,7 +44,6 @@ import org.elasticsearch.common.settings.Setting;
|
||||||
import org.elasticsearch.common.settings.Setting.Property;
|
import org.elasticsearch.common.settings.Setting.Property;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.settings.loader.SettingsLoader;
|
import org.elasticsearch.common.settings.loader.SettingsLoader;
|
||||||
import org.elasticsearch.common.xcontent.FromXContentBuilder;
|
|
||||||
import org.elasticsearch.common.xcontent.ToXContent;
|
import org.elasticsearch.common.xcontent.ToXContent;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
import org.elasticsearch.common.xcontent.XContentFactory;
|
import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
|
@ -78,8 +75,12 @@ import static org.elasticsearch.cluster.node.DiscoveryNodeFilters.OpType.OR;
|
||||||
import static org.elasticsearch.common.settings.Settings.readSettingsFromStream;
|
import static org.elasticsearch.common.settings.Settings.readSettingsFromStream;
|
||||||
import static org.elasticsearch.common.settings.Settings.writeSettingsToStream;
|
import static org.elasticsearch.common.settings.Settings.writeSettingsToStream;
|
||||||
|
|
||||||
public class IndexMetaData implements Diffable<IndexMetaData>, FromXContentBuilder<IndexMetaData>, ToXContent {
|
public class IndexMetaData implements Diffable<IndexMetaData>, ToXContent {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class will be removed in v7.0
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public interface Custom extends Diffable<Custom>, ToXContent {
|
public interface Custom extends Diffable<Custom>, ToXContent {
|
||||||
|
|
||||||
String type();
|
String type();
|
||||||
|
@ -88,6 +89,16 @@ public class IndexMetaData implements Diffable<IndexMetaData>, FromXContentBuild
|
||||||
|
|
||||||
Custom fromXContent(XContentParser parser) throws IOException;
|
Custom fromXContent(XContentParser parser) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the {@link org.elasticsearch.cluster.Diff} from StreamInput
|
||||||
|
*/
|
||||||
|
Diff<Custom> readDiffFrom(StreamInput in) throws IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads an object of this type from the provided {@linkplain StreamInput}. The receiving instance remains unchanged.
|
||||||
|
*/
|
||||||
|
Custom readFrom(StreamInput in) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges from this to another, with this being more important, i.e., if something exists in this and another,
|
* Merges from this to another, with this being more important, i.e., if something exists in this and another,
|
||||||
* this will prevail.
|
* this will prevail.
|
||||||
|
@ -249,10 +260,6 @@ public class IndexMetaData implements Diffable<IndexMetaData>, FromXContentBuild
|
||||||
Setting.Property.Dynamic,
|
Setting.Property.Dynamic,
|
||||||
Setting.Property.IndexScope);
|
Setting.Property.IndexScope);
|
||||||
|
|
||||||
public static final IndexMetaData PROTO = IndexMetaData.builder("")
|
|
||||||
.settings(Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT))
|
|
||||||
.numberOfShards(1).numberOfReplicas(0).build();
|
|
||||||
|
|
||||||
public static final String KEY_IN_SYNC_ALLOCATIONS = "in_sync_allocations";
|
public static final String KEY_IN_SYNC_ALLOCATIONS = "in_sync_allocations";
|
||||||
static final String KEY_VERSION = "version";
|
static final String KEY_VERSION = "version";
|
||||||
static final String KEY_ROUTING_NUM_SHARDS = "routing_num_shards";
|
static final String KEY_ROUTING_NUM_SHARDS = "routing_num_shards";
|
||||||
|
@ -567,13 +574,11 @@ public class IndexMetaData implements Diffable<IndexMetaData>, FromXContentBuild
|
||||||
return new IndexMetaDataDiff(previousState, this);
|
return new IndexMetaDataDiff(previousState, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static Diff<IndexMetaData> readDiffFrom(StreamInput in) throws IOException {
|
||||||
public Diff<IndexMetaData> readDiffFrom(StreamInput in) throws IOException {
|
|
||||||
return new IndexMetaDataDiff(in);
|
return new IndexMetaDataDiff(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static IndexMetaData fromXContent(XContentParser parser) throws IOException {
|
||||||
public IndexMetaData fromXContent(XContentParser parser, ParseFieldMatcher parseFieldMatcher) throws IOException {
|
|
||||||
return Builder.fromXContent(parser);
|
return Builder.fromXContent(parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -617,8 +622,10 @@ public class IndexMetaData implements Diffable<IndexMetaData>, FromXContentBuild
|
||||||
state = State.fromId(in.readByte());
|
state = State.fromId(in.readByte());
|
||||||
settings = Settings.readSettingsFromStream(in);
|
settings = Settings.readSettingsFromStream(in);
|
||||||
primaryTerms = in.readVLongArray();
|
primaryTerms = in.readVLongArray();
|
||||||
mappings = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), MappingMetaData.PROTO);
|
mappings = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), MappingMetaData::new,
|
||||||
aliases = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), AliasMetaData.PROTO);
|
MappingMetaData::readDiffFrom);
|
||||||
|
aliases = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(), AliasMetaData::new,
|
||||||
|
AliasMetaData::readDiffFrom);
|
||||||
customs = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(),
|
customs = DiffableUtils.readImmutableOpenMapDiff(in, DiffableUtils.getStringKeySerializer(),
|
||||||
new DiffableUtils.DiffableValueSerializer<String, Custom>() {
|
new DiffableUtils.DiffableValueSerializer<String, Custom>() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -626,6 +633,7 @@ public class IndexMetaData implements Diffable<IndexMetaData>, FromXContentBuild
|
||||||
return lookupPrototypeSafe(key).readFrom(in);
|
return lookupPrototypeSafe(key).readFrom(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Override
|
@Override
|
||||||
public Diff<Custom> readDiff(StreamInput in, String key) throws IOException {
|
public Diff<Custom> readDiff(StreamInput in, String key) throws IOException {
|
||||||
return lookupPrototypeSafe(key).readDiffFrom(in);
|
return lookupPrototypeSafe(key).readDiffFrom(in);
|
||||||
|
@ -665,8 +673,7 @@ public class IndexMetaData implements Diffable<IndexMetaData>, FromXContentBuild
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static IndexMetaData readFrom(StreamInput in) throws IOException {
|
||||||
public IndexMetaData readFrom(StreamInput in) throws IOException {
|
|
||||||
Builder builder = new Builder(in.readString());
|
Builder builder = new Builder(in.readString());
|
||||||
builder.version(in.readLong());
|
builder.version(in.readLong());
|
||||||
builder.setRoutingNumShards(in.readInt());
|
builder.setRoutingNumShards(in.readInt());
|
||||||
|
@ -675,12 +682,12 @@ public class IndexMetaData implements Diffable<IndexMetaData>, FromXContentBuild
|
||||||
builder.primaryTerms(in.readVLongArray());
|
builder.primaryTerms(in.readVLongArray());
|
||||||
int mappingsSize = in.readVInt();
|
int mappingsSize = in.readVInt();
|
||||||
for (int i = 0; i < mappingsSize; i++) {
|
for (int i = 0; i < mappingsSize; i++) {
|
||||||
MappingMetaData mappingMd = MappingMetaData.PROTO.readFrom(in);
|
MappingMetaData mappingMd = new MappingMetaData(in);
|
||||||
builder.putMapping(mappingMd);
|
builder.putMapping(mappingMd);
|
||||||
}
|
}
|
||||||
int aliasesSize = in.readVInt();
|
int aliasesSize = in.readVInt();
|
||||||
for (int i = 0; i < aliasesSize; i++) {
|
for (int i = 0; i < aliasesSize; i++) {
|
||||||
AliasMetaData aliasMd = AliasMetaData.Builder.readFrom(in);
|
AliasMetaData aliasMd = new AliasMetaData(in);
|
||||||
builder.putAlias(aliasMd);
|
builder.putAlias(aliasMd);
|
||||||
}
|
}
|
||||||
int customSize = in.readVInt();
|
int customSize = in.readVInt();
|
||||||
|
@ -1200,10 +1207,6 @@ public class IndexMetaData implements Diffable<IndexMetaData>, FromXContentBuild
|
||||||
}
|
}
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IndexMetaData readFrom(StreamInput in) throws IOException {
|
|
||||||
return PROTO.readFrom(in);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue