add unittests & javadocs
This commit is contained in:
parent
66d5d0c4f2
commit
2245f480c9
|
@ -54,6 +54,9 @@ public class IndexModule extends AbstractModule {
|
|||
this.indexSettings = indexSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a settings consumer for this index
|
||||
*/
|
||||
public void addIndexSettingsListener(Consumer<Settings> listener) {
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("listener must not be null");
|
||||
|
@ -65,10 +68,31 @@ public class IndexModule extends AbstractModule {
|
|||
settingsConsumers.add(listener);
|
||||
}
|
||||
|
||||
public Settings getIndexSettings() {
|
||||
/**
|
||||
* Returns the index {@link Settings} for this index
|
||||
*/
|
||||
public Settings getSettings() {
|
||||
return indexSettings.getSettings();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index this module is associated with
|
||||
*/
|
||||
public Index getIndex() {
|
||||
return indexSettings.getIndex();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an {@link IndexEventListener} for this index. All listeners added here
|
||||
* are maintained for the entire index lifecycle on this node. Once an index is closed or deleted these
|
||||
* listeners go out of scope.
|
||||
* <p>
|
||||
* Note: an index might be created on a node multiple times. For instance if the last shard from an index is
|
||||
* relocated to another node the internal representation will be destroyed which includes the registered listeners.
|
||||
* Once the node holds at least one shard of an index all modules are reloaded and listeners are registered again.
|
||||
* Listeners can't be unregistered the will stay alive for the entire time the index is allocated on a node.
|
||||
* </p>
|
||||
*/
|
||||
public void addIndexEventListener(IndexEventListener listener) {
|
||||
if (this.listener != null) {
|
||||
throw new IllegalStateException("can't add listener after listeners are frozen");
|
||||
|
|
|
@ -31,53 +31,94 @@ import java.util.Collections;
|
|||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
// TODO add javadocs - this also needs a dedicated unit test
|
||||
/**
|
||||
* This class encapsulates all index level settings and handles settings updates.
|
||||
* It's created per index and available to all index level classes and allows them to retrieve
|
||||
* the latest updated settings instance. Classes that need to listen to settings updates can register
|
||||
* a settings consumer at index creation via {@link IndexModule#addIndexSettingsListener(Consumer)} that will
|
||||
* be called for each settings update.
|
||||
*/
|
||||
public final class IndexSettings {
|
||||
private final String uuid;
|
||||
private volatile Settings settings;
|
||||
private final List<Consumer<Settings>> updateListeners;
|
||||
private final Index index;
|
||||
private final Version version;
|
||||
private final ESLogger logger;
|
||||
private final String nodeName;
|
||||
|
||||
public IndexSettings(Index index) {
|
||||
this(index, Settings.settingsBuilder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).build(), Collections.EMPTY_LIST);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link IndexSettings} instance
|
||||
* @param index the index this settings object is associated with
|
||||
* @param settings the actual settings including the node level settings
|
||||
* @param updateListeners a collection of listeners / consumers that should be notified if one or more settings are updated
|
||||
*/
|
||||
public IndexSettings(Index index, Settings settings, Collection<Consumer<Settings>> updateListeners) {
|
||||
this.settings = settings;
|
||||
this.updateListeners = Collections.unmodifiableList(new ArrayList<>(updateListeners));
|
||||
this.index = index;
|
||||
version = Version.indexCreated(settings);
|
||||
uuid = settings.get(IndexMetaData.SETTING_INDEX_UUID, IndexMetaData.INDEX_UUID_NA_VALUE);
|
||||
logger = Loggers.getLogger(getClass(), settings, index);
|
||||
nodeName = settings.get("name", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the settings for this index. These settings contain the node and index level settings where
|
||||
* settings that are specified on both index and node level are overwritten by the index settings.
|
||||
*/
|
||||
public Settings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index this settings object belongs to
|
||||
*/
|
||||
public Index getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the indexes UUID
|
||||
*/
|
||||
public String getUUID() {
|
||||
return settings.get(IndexMetaData.SETTING_INDEX_UUID, IndexMetaData.INDEX_UUID_NA_VALUE);
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the index has a custom data path
|
||||
*/
|
||||
public boolean hasCustomDataPath() {
|
||||
return NodeEnvironment.hasCustomDataPath(settings);
|
||||
}
|
||||
|
||||
public Version getVersion() {
|
||||
/**
|
||||
* Returns the version the index was created on.
|
||||
* @see Version#indexCreated(Settings)
|
||||
*/
|
||||
public Version getIndexVersionCreated() {
|
||||
return version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current node name
|
||||
*/
|
||||
public String getNodeName() {
|
||||
return settings.get("name", "");
|
||||
return nodeName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies all registered settings consumers with the new settings iff at least one setting has changed.
|
||||
*
|
||||
* @return <code>true</code> iff any setting has been updated otherwise <code>false</code>.
|
||||
*/
|
||||
synchronized boolean updateSettings(Settings settings) {
|
||||
if (Version.indexCreated(settings) != version) {
|
||||
throw new IllegalStateException("version mismatch on settings update");
|
||||
throw new IllegalArgumentException("version mismatch on settings update expected: " + version + " but was: " + Version.indexCreated(settings));
|
||||
}
|
||||
final String newUUID = settings.get(IndexMetaData.SETTING_INDEX_UUID, IndexMetaData.INDEX_UUID_NA_VALUE);
|
||||
if (newUUID.equals(getUUID()) == false) {
|
||||
throw new IllegalArgumentException("uuid mismatch on settings update expected: " + uuid + " but was: " + newUUID);
|
||||
}
|
||||
|
||||
if (this.settings.getByPrefix(IndexMetaData.INDEX_SETTING_PREFIX).getAsMap().equals(settings.getByPrefix(IndexMetaData.INDEX_SETTING_PREFIX).getAsMap())) {
|
||||
|
@ -95,6 +136,9 @@ public final class IndexSettings {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all settings update consumers
|
||||
*/
|
||||
List<Consumer<Settings>> getUpdateListeners() { // for testing
|
||||
return updateListeners;
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ public class AnalysisService extends AbstractIndexComponent implements Closeable
|
|||
@Nullable Map<String, CharFilterFactoryFactory> charFilterFactoryFactories,
|
||||
@Nullable Map<String, TokenFilterFactoryFactory> tokenFilterFactoryFactories) {
|
||||
super(indexSettings);
|
||||
Settings defaultSettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, indexSettings.getVersion()).build();
|
||||
Settings defaultSettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, indexSettings.getIndexVersionCreated()).build();
|
||||
Map<String, TokenizerFactory> tokenizers = new HashMap<>();
|
||||
if (tokenizerFactoryFactories != null) {
|
||||
Map<String, Settings> tokenizersSettings = this.indexSettings.getSettings().getGroups("index.analysis.tokenizer");
|
||||
|
@ -266,7 +266,7 @@ public class AnalysisService extends AbstractIndexComponent implements Closeable
|
|||
throw new IllegalArgumentException("no default analyzer configured");
|
||||
}
|
||||
if (analyzers.containsKey("default_index")) {
|
||||
final Version createdVersion = indexSettings.getVersion();
|
||||
final Version createdVersion = indexSettings.getIndexVersionCreated();
|
||||
if (createdVersion.onOrAfter(Version.V_3_0_0)) {
|
||||
throw new IllegalArgumentException("setting [index.analysis.analyzer.default_index] is not supported anymore, use [index.analysis.analyzer.default] instead for index [" + index().getName() + "]");
|
||||
} else {
|
||||
|
|
|
@ -80,7 +80,7 @@ public class CustomAnalyzerProvider extends AbstractIndexAnalyzerProvider<Custom
|
|||
int positionIncrementGap = StringFieldMapper.Defaults.positionIncrementGap(Version.indexCreated(indexSettings.getSettings()));
|
||||
|
||||
if (analyzerSettings.getAsMap().containsKey("position_offset_gap")){
|
||||
if (indexSettings.getVersion().before(Version.V_2_0_0)){
|
||||
if (indexSettings.getIndexVersionCreated().before(Version.V_2_0_0)){
|
||||
if (analyzerSettings.getAsMap().containsKey("position_increment_gap")){
|
||||
throw new IllegalArgumentException("Custom Analyzer [" + name() +
|
||||
"] defined both [position_offset_gap] and [position_increment_gap], use only [position_increment_gap]");
|
||||
|
|
|
@ -55,7 +55,7 @@ public class EdgeNGramTokenizerFactory extends AbstractTokenizerFactory {
|
|||
this.maxGram = settings.getAsInt("max_gram", NGramTokenizer.DEFAULT_MAX_NGRAM_SIZE);
|
||||
this.side = Lucene43EdgeNGramTokenizer.Side.getSide(settings.get("side", Lucene43EdgeNGramTokenizer.DEFAULT_SIDE.getLabel()));
|
||||
this.matcher = parseTokenChars(settings.getAsArray("token_chars"));
|
||||
this.esVersion = indexSettings.getVersion();
|
||||
this.esVersion = indexSettings.getIndexVersionCreated();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -94,7 +94,7 @@ public class NGramTokenizerFactory extends AbstractTokenizerFactory {
|
|||
this.minGram = settings.getAsInt("min_gram", NGramTokenizer.DEFAULT_MIN_NGRAM_SIZE);
|
||||
this.maxGram = settings.getAsInt("max_gram", NGramTokenizer.DEFAULT_MAX_NGRAM_SIZE);
|
||||
this.matcher = parseTokenChars(settings.getAsArray("token_chars"));
|
||||
this.esVersion = indexSettings.getVersion();
|
||||
this.esVersion = indexSettings.getIndexVersionCreated();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
|
|
|
@ -43,7 +43,7 @@ public class PatternAnalyzerProvider extends AbstractIndexAnalyzerProvider<Analy
|
|||
public PatternAnalyzerProvider(IndexSettings indexSettings, Environment env, @Assisted String name, @Assisted Settings settings) {
|
||||
super(indexSettings, name, settings);
|
||||
|
||||
Version esVersion = indexSettings.getVersion();
|
||||
Version esVersion = indexSettings.getIndexVersionCreated();
|
||||
final CharArraySet defaultStopwords;
|
||||
if (esVersion.onOrAfter(Version.V_1_0_0_RC1)) {
|
||||
defaultStopwords = CharArraySet.EMPTY_SET;
|
||||
|
|
|
@ -40,7 +40,7 @@ public class StandardAnalyzerProvider extends AbstractIndexAnalyzerProvider<Stan
|
|||
@Inject
|
||||
public StandardAnalyzerProvider(IndexSettings indexSettings, Environment env, @Assisted String name, @Assisted Settings settings) {
|
||||
super(indexSettings, name, settings);
|
||||
this.esVersion = indexSettings.getVersion();
|
||||
this.esVersion = indexSettings.getIndexVersionCreated();
|
||||
final CharArraySet defaultStopwords;
|
||||
if (esVersion.onOrAfter(Version.V_1_0_0_Beta1)) {
|
||||
defaultStopwords = CharArraySet.EMPTY_SET;
|
||||
|
|
|
@ -39,7 +39,7 @@ public class StandardHtmlStripAnalyzerProvider extends AbstractIndexAnalyzerProv
|
|||
@Inject
|
||||
public StandardHtmlStripAnalyzerProvider(IndexSettings indexSettings, Environment env, @Assisted String name, @Assisted Settings settings) {
|
||||
super(indexSettings, name, settings);
|
||||
this.esVersion = indexSettings.getVersion();
|
||||
this.esVersion = indexSettings.getIndexVersionCreated();
|
||||
final CharArraySet defaultStopwords;
|
||||
if (esVersion.onOrAfter(Version.V_1_0_0_RC1)) {
|
||||
defaultStopwords = CharArraySet.EMPTY_SET;
|
||||
|
|
|
@ -74,7 +74,7 @@ public class StemmerTokenFilterFactory extends AbstractTokenFilterFactory {
|
|||
|
||||
@Override
|
||||
public TokenStream create(TokenStream tokenStream) {
|
||||
final Version indexVersion = indexSettings.getVersion();
|
||||
final Version indexVersion = indexSettings.getIndexVersionCreated();
|
||||
|
||||
if ("arabic".equalsIgnoreCase(language)) {
|
||||
return new ArabicStemFilter(tokenStream);
|
||||
|
|
|
@ -220,7 +220,7 @@ public class MapperService extends AbstractIndexComponent implements Closeable {
|
|||
if (mapper.type().length() == 0) {
|
||||
throw new InvalidTypeNameException("mapping type name is empty");
|
||||
}
|
||||
if (indexSettings.getVersion().onOrAfter(Version.V_2_0_0_beta1) && mapper.type().length() > 255) {
|
||||
if (indexSettings.getIndexVersionCreated().onOrAfter(Version.V_2_0_0_beta1) && mapper.type().length() > 255) {
|
||||
throw new InvalidTypeNameException("mapping type name [" + mapper.type() + "] is too long; limit is length 255 but was [" + mapper.type().length() + "]");
|
||||
}
|
||||
if (mapper.type().charAt(0) == '_') {
|
||||
|
@ -236,7 +236,7 @@ public class MapperService extends AbstractIndexComponent implements Closeable {
|
|||
throw new IllegalArgumentException("The [_parent.type] option can't point to the same type");
|
||||
}
|
||||
if (typeNameStartsWithIllegalDot(mapper)) {
|
||||
if (indexSettings.getVersion().onOrAfter(Version.V_2_0_0_beta1)) {
|
||||
if (indexSettings.getIndexVersionCreated().onOrAfter(Version.V_2_0_0_beta1)) {
|
||||
throw new IllegalArgumentException("mapping type name [" + mapper.type() + "] must not start with a '.'");
|
||||
} else {
|
||||
logger.warn("Type [{}] starts with a '.', it is recommended not to start a type name with a '.'", mapper.type());
|
||||
|
|
|
@ -207,7 +207,7 @@ public class IndexQueryParserService extends AbstractIndexComponent {
|
|||
* @return The lowest node version in the cluster when the index was created or <code>null</code> if that was unknown
|
||||
*/
|
||||
public Version getIndexCreatedVersion() {
|
||||
return indexSettings.getVersion();
|
||||
return indexSettings.getIndexVersionCreated();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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.index;
|
||||
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.cluster.metadata.IndexMetaData;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
import org.elasticsearch.test.VersionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class IndexSettingsTests extends ESTestCase {
|
||||
|
||||
public void testRunListener() {
|
||||
Version version = VersionUtils.getPreviousVersion();
|
||||
Settings theSettings = Settings.settingsBuilder().put(IndexMetaData.SETTING_VERSION_CREATED, version).put(IndexMetaData.SETTING_INDEX_UUID, "0xdeadbeef").build();
|
||||
final AtomicInteger integer = new AtomicInteger(0);
|
||||
Consumer<Settings> settingsConsumer = (s) -> integer.set(s.getAsInt("index.test.setting.int", -1));
|
||||
IndexSettings settings = new IndexSettings(new Index("index"), theSettings, Collections.singleton(settingsConsumer));
|
||||
assertEquals(version, settings.getIndexVersionCreated());
|
||||
assertEquals("0xdeadbeef", settings.getUUID());
|
||||
|
||||
assertEquals(1, settings.getUpdateListeners().size());
|
||||
assertFalse(settings.updateSettings(theSettings));
|
||||
assertSame(theSettings, settings.getSettings());
|
||||
assertEquals(0, integer.get());
|
||||
assertTrue(settings.updateSettings(Settings.builder().put(theSettings).put("index.test.setting.int", 42).build()));
|
||||
assertEquals(42, integer.get());
|
||||
}
|
||||
|
||||
public void testListenerCanThrowException() {
|
||||
Version version = VersionUtils.getPreviousVersion();
|
||||
Settings theSettings = Settings.settingsBuilder().put(IndexMetaData.SETTING_VERSION_CREATED, version).put(IndexMetaData.SETTING_INDEX_UUID, "0xdeadbeef").build();
|
||||
final AtomicInteger integer = new AtomicInteger(0);
|
||||
Consumer<Settings> settingsConsumer = (s) -> integer.set(s.getAsInt("index.test.setting.int", -1));
|
||||
Consumer<Settings> exceptionConsumer = (s) -> {throw new RuntimeException("boom");};
|
||||
List<Consumer<Settings>> list = new ArrayList<>();
|
||||
list.add(settingsConsumer);
|
||||
list.add(exceptionConsumer);
|
||||
Collections.shuffle(list, random());
|
||||
IndexSettings settings = new IndexSettings(new Index("index"), theSettings, list);
|
||||
assertEquals(0, integer.get());
|
||||
assertTrue(settings.updateSettings(Settings.builder().put(theSettings).put("index.test.setting.int", 42).build()));
|
||||
assertEquals(42, integer.get());
|
||||
}
|
||||
|
||||
public void testSettingsConsistency() {
|
||||
Version version = VersionUtils.getPreviousVersion();
|
||||
Settings theSettings = Settings.settingsBuilder().put(IndexMetaData.SETTING_VERSION_CREATED, version).build();
|
||||
IndexSettings settings = new IndexSettings(new Index("index"), theSettings, Collections.EMPTY_LIST);
|
||||
assertEquals(version, settings.getIndexVersionCreated());
|
||||
assertEquals("_na_", settings.getUUID());
|
||||
try {
|
||||
settings.updateSettings(Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).put("index.test.setting.int", 42).build());
|
||||
fail("version has changed");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertTrue(ex.getMessage(), ex.getMessage().startsWith("version mismatch on settings update expected: "));
|
||||
}
|
||||
|
||||
theSettings = Settings.settingsBuilder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).put(IndexMetaData.SETTING_INDEX_UUID, "0xdeadbeef").build();
|
||||
settings = new IndexSettings(new Index("index"), theSettings, Collections.EMPTY_LIST);
|
||||
try {
|
||||
settings.updateSettings(Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT).put("index.test.setting.int", 42).build());
|
||||
fail("uuid missing/change");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertEquals("uuid mismatch on settings update expected: 0xdeadbeef but was: _na_", ex.getMessage());
|
||||
}
|
||||
assertSame(theSettings, settings.getSettings());
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -60,7 +60,7 @@ public class MockFSIndexStore extends IndexStore {
|
|||
}
|
||||
|
||||
public void onModule(IndexModule module) {
|
||||
Settings indexSettings = module.getIndexSettings();
|
||||
Settings indexSettings = module.getSettings();
|
||||
if ("mock".equals(indexSettings.get(IndexStoreModule.STORE_TYPE))) {
|
||||
if (indexSettings.getAsBoolean(CHECK_INDEX_ON_CLOSE, true)) {
|
||||
module.addIndexEventListener(new Listener());
|
||||
|
|
Loading…
Reference in New Issue