add unittests & javadocs

This commit is contained in:
Simon Willnauer 2015-10-22 22:26:24 +02:00
parent 66d5d0c4f2
commit 2245f480c9
14 changed files with 184 additions and 23 deletions

View File

@ -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");

View File

@ -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;
}

View File

@ -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 {

View File

@ -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]");

View File

@ -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

View File

@ -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")

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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());

View File

@ -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();
}
/**

View File

@ -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());
}
}

View File

@ -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());