Make SettingsUpdater less stateful
This commit is contained in:
parent
8011e18880
commit
86a18a08fb
|
@ -32,7 +32,7 @@ import java.util.function.Consumer;
|
|||
* This service offers transactional application of updates settings.
|
||||
*/
|
||||
public abstract class AbstractScopedSettings extends AbstractComponent {
|
||||
private Settings lastSettingsApplied;
|
||||
private Settings lastSettingsApplied = Settings.EMPTY;
|
||||
private final List<SettingUpdater> settingUpdaters = new ArrayList<>();
|
||||
private final Map<String, Setting<?>> groupSettings = new HashMap<>();
|
||||
private final Map<String, Setting<?>> keySettings = new HashMap<>();
|
||||
|
@ -62,12 +62,14 @@ public abstract class AbstractScopedSettings extends AbstractComponent {
|
|||
* method will not change any settings but will fail if any of the settings can't be applied.
|
||||
*/
|
||||
public synchronized Settings dryRun(Settings settings) {
|
||||
final Settings build = Settings.builder().put(this.settings).put(settings).build();
|
||||
try {
|
||||
final Settings current = Settings.builder().put(this.settings).put(settings).build();
|
||||
final Settings previous = Settings.builder().put(this.settings).put(this.lastSettingsApplied).build();
|
||||
List<RuntimeException> exceptions = new ArrayList<>();
|
||||
for (SettingUpdater settingUpdater : settingUpdaters) {
|
||||
try {
|
||||
settingUpdater.prepareApply(build);
|
||||
if (settingUpdater.hasChanged(current, previous)) {
|
||||
settingUpdater.getValue(current, previous);
|
||||
}
|
||||
} catch (RuntimeException ex) {
|
||||
exceptions.add(ex);
|
||||
logger.debug("failed to prepareCommit settings for [{}]", ex, settingUpdater);
|
||||
|
@ -75,16 +77,7 @@ public abstract class AbstractScopedSettings extends AbstractComponent {
|
|||
}
|
||||
// here we are exhaustive and record all settings that failed.
|
||||
ExceptionsHelper.rethrowAndSuppress(exceptions);
|
||||
} finally {
|
||||
for (SettingUpdater settingUpdater : settingUpdaters) {
|
||||
try {
|
||||
settingUpdater.rollback();
|
||||
} catch (Exception e) {
|
||||
logger.error("failed to rollback settings for [{}]", e, settingUpdater);
|
||||
}
|
||||
}
|
||||
}
|
||||
return build;
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -99,34 +92,25 @@ public abstract class AbstractScopedSettings extends AbstractComponent {
|
|||
// nothing changed in the settings, ignore
|
||||
return newSettings;
|
||||
}
|
||||
final Settings build = Settings.builder().put(this.settings).put(newSettings).build();
|
||||
boolean success = false;
|
||||
final Settings current = Settings.builder().put(this.settings).put(newSettings).build();
|
||||
final Settings previous = Settings.builder().put(this.settings).put(this.lastSettingsApplied).build();
|
||||
try {
|
||||
List<Runnable> applyRunnables = new ArrayList<>();
|
||||
for (SettingUpdater settingUpdater : settingUpdaters) {
|
||||
try {
|
||||
settingUpdater.prepareApply(build);
|
||||
applyRunnables.add(settingUpdater.updater(current, previous));
|
||||
} catch (Exception ex) {
|
||||
logger.warn("failed to prepareCommit settings for [{}]", ex, settingUpdater);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
for (SettingUpdater settingUpdater : settingUpdaters) {
|
||||
settingUpdater.apply();
|
||||
for (Runnable settingUpdater : applyRunnables) {
|
||||
settingUpdater.run();
|
||||
}
|
||||
success = true;
|
||||
} catch (Exception ex) {
|
||||
logger.warn("failed to apply settings", ex);
|
||||
throw ex;
|
||||
} finally {
|
||||
if (success == false) {
|
||||
for (SettingUpdater settingUpdater : settingUpdaters) {
|
||||
try {
|
||||
settingUpdater.rollback();
|
||||
} catch (Exception e) {
|
||||
logger.error("failed to refresh settings for [{}]", e, settingUpdater);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return lastSettingsApplied = newSettings;
|
||||
}
|
||||
|
@ -141,7 +125,7 @@ public abstract class AbstractScopedSettings extends AbstractComponent {
|
|||
if (setting != get(setting.getKey())) {
|
||||
throw new IllegalArgumentException("Setting is not registered for key [" + setting.getKey() + "]");
|
||||
}
|
||||
this.settingUpdaters.add(setting.newUpdater(consumer, logger, settings, predicate));
|
||||
this.settingUpdaters.add(setting.newUpdater(consumer, logger, predicate));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -159,7 +143,7 @@ public abstract class AbstractScopedSettings extends AbstractComponent {
|
|||
if (b != get(b.getKey())) {
|
||||
throw new IllegalArgumentException("Setting is not registered for key [" + b.getKey() + "]");
|
||||
}
|
||||
this.settingUpdaters.add(Setting.compoundUpdater(consumer, a, b, logger, settings));
|
||||
this.settingUpdaters.add(Setting.compoundUpdater(consumer, a, b, logger));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -176,24 +160,51 @@ public abstract class AbstractScopedSettings extends AbstractComponent {
|
|||
* Transactional interface to update settings.
|
||||
* @see Setting
|
||||
*/
|
||||
public interface SettingUpdater {
|
||||
/**
|
||||
* Prepares applying the given settings to this updater. All the heavy lifting like parsing and validation
|
||||
* happens in this method. Yet the actual setting should not be changed by this call.
|
||||
* @param settings the settings to apply
|
||||
* @return <code>true</code> if this updater will update a setting on calling {@link #apply()} otherwise <code>false</code>
|
||||
*/
|
||||
boolean prepareApply(Settings settings);
|
||||
public interface SettingUpdater<T> {
|
||||
|
||||
/**
|
||||
* Applies the settings passed to {@link #prepareApply(Settings)}
|
||||
* Returns true if this updaters setting has changed with the current update
|
||||
* @param current the current settings
|
||||
* @param previous the previous setting
|
||||
* @return true if this updaters setting has changed with the current update
|
||||
*/
|
||||
void apply();
|
||||
boolean hasChanged(Settings current, Settings previous);
|
||||
|
||||
/**
|
||||
* Rolls back to the state before {@link #prepareApply(Settings)} was called. All internal prepared state is cleared after this call.
|
||||
* Returns the instance value for the current settings. This method is stateless and idempotent.
|
||||
*/
|
||||
void rollback();
|
||||
T getValue(Settings current, Settings previous);
|
||||
|
||||
/**
|
||||
* Applies the given value to the updater. This methods will actually run the update.
|
||||
*/
|
||||
void apply(T value, Settings current, Settings previous);
|
||||
|
||||
/**
|
||||
* Updates this updaters value if it has changed.
|
||||
* @return <code>true</code> iff the value has been updated.
|
||||
*/
|
||||
default boolean apply(Settings current, Settings previous) {
|
||||
if (hasChanged(current, previous)) {
|
||||
T value = getValue(current, previous);
|
||||
apply(value, current, previous);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a callable runnable that calls {@link #apply(Object, Settings, Settings)} if the settings
|
||||
* actually changed. This allows to defer the update to a later point in time while keeping type safety.
|
||||
* If the value didn't change the returned runnable is a noop.
|
||||
*/
|
||||
default Runnable updater(Settings current, Settings previous) {
|
||||
if (hasChanged(current, previous)) {
|
||||
T value = getValue(current, previous);
|
||||
return () -> { apply(value, current, previous);};
|
||||
}
|
||||
return () -> {};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,6 +21,7 @@ package org.elasticsearch.common.settings;
|
|||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.action.support.ToXContentToBytes;
|
||||
import org.elasticsearch.common.Booleans;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.logging.ESLogger;
|
||||
import org.elasticsearch.common.regex.Regex;
|
||||
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||
|
@ -150,13 +151,13 @@ public class Setting<T> extends ToXContentToBytes {
|
|||
INDEX;
|
||||
}
|
||||
|
||||
final AbstractScopedSettings.SettingUpdater newUpdater(Consumer<T> consumer, ESLogger logger, Settings settings) {
|
||||
return newUpdater(consumer, logger, settings, (s) -> {});
|
||||
final AbstractScopedSettings.SettingUpdater newUpdater(Consumer<T> consumer, ESLogger logger) {
|
||||
return newUpdater(consumer, logger, (s) -> {});
|
||||
}
|
||||
|
||||
AbstractScopedSettings.SettingUpdater newUpdater(Consumer<T> consumer, ESLogger logger, Settings settings, Consumer<T> accept) {
|
||||
AbstractScopedSettings.SettingUpdater newUpdater(Consumer<T> consumer, ESLogger logger, Consumer<T> accept) {
|
||||
if (isDynamic()) {
|
||||
return new Updater(consumer, logger, settings, accept);
|
||||
return new Updater(consumer, logger, accept);
|
||||
} else {
|
||||
throw new IllegalStateException("setting [" + getKey() + "] is not dynamic");
|
||||
}
|
||||
|
@ -166,39 +167,23 @@ public class Setting<T> extends ToXContentToBytes {
|
|||
* this is used for settings that depend on each other... see {@link org.elasticsearch.common.settings.AbstractScopedSettings#addSettingsUpdateConsumer(Setting, Setting, BiConsumer)} and it's
|
||||
* usage for details.
|
||||
*/
|
||||
static <A, B> AbstractScopedSettings.SettingUpdater compoundUpdater(final BiConsumer<A,B> consumer, final Setting<A> aSettting, final Setting<B> bSetting, ESLogger logger, Settings settings) {
|
||||
final AtomicReference<A> aRef = new AtomicReference<>();
|
||||
final AtomicReference<B> bRef = new AtomicReference<>();
|
||||
final AbstractScopedSettings.SettingUpdater aSettingUpdater = aSettting.newUpdater(aRef::set, logger, settings);
|
||||
final AbstractScopedSettings.SettingUpdater bSettingUpdater = bSetting.newUpdater(bRef::set, logger, settings);
|
||||
return new AbstractScopedSettings.SettingUpdater() {
|
||||
boolean aHasChanged = false;
|
||||
boolean bHasChanged = false;
|
||||
static <A, B> AbstractScopedSettings.SettingUpdater<Tuple<A, B>> compoundUpdater(final BiConsumer<A,B> consumer, final Setting<A> aSettting, final Setting<B> bSetting, ESLogger logger) {
|
||||
final AbstractScopedSettings.SettingUpdater<A> aSettingUpdater = aSettting.newUpdater(null, logger);
|
||||
final AbstractScopedSettings.SettingUpdater<B> bSettingUpdater = bSetting.newUpdater(null, logger);
|
||||
return new AbstractScopedSettings.SettingUpdater<Tuple<A, B>>() {
|
||||
@Override
|
||||
public boolean prepareApply(Settings settings) {
|
||||
aHasChanged = aSettingUpdater.prepareApply(settings);
|
||||
bHasChanged = bSettingUpdater.prepareApply(settings);
|
||||
return aHasChanged || bHasChanged;
|
||||
public boolean hasChanged(Settings current, Settings previous) {
|
||||
return aSettingUpdater.hasChanged(current, previous) || bSettingUpdater.hasChanged(current, previous);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
aSettingUpdater.apply();
|
||||
bSettingUpdater.apply();
|
||||
if (aHasChanged || bHasChanged) {
|
||||
consumer.accept(aRef.get(), bRef.get());
|
||||
}
|
||||
public Tuple<A, B> getValue(Settings current, Settings previous) {
|
||||
return new Tuple<>(aSettingUpdater.getValue(current, previous), bSettingUpdater.getValue(current, previous));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
try {
|
||||
aRef.set(null);
|
||||
aSettingUpdater.rollback();
|
||||
} finally {
|
||||
bRef.set(null);
|
||||
bSettingUpdater.rollback();
|
||||
}
|
||||
public void apply(Tuple<A, B> value, Settings current, Settings previous) {
|
||||
consumer.accept(value.v1(), value.v2());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -209,63 +194,47 @@ public class Setting<T> extends ToXContentToBytes {
|
|||
}
|
||||
|
||||
|
||||
private class Updater implements AbstractScopedSettings.SettingUpdater {
|
||||
private class Updater implements AbstractScopedSettings.SettingUpdater<T> {
|
||||
private final Consumer<T> consumer;
|
||||
private final ESLogger logger;
|
||||
private final Consumer<T> accept;
|
||||
private String value;
|
||||
private boolean commitPending;
|
||||
private String pendingValue;
|
||||
private T valueInstance;
|
||||
|
||||
public Updater(Consumer<T> consumer, ESLogger logger, Settings settings, Consumer<T> accept) {
|
||||
public Updater(Consumer<T> consumer, ESLogger logger, Consumer<T> accept) {
|
||||
this.consumer = consumer;
|
||||
this.logger = logger;
|
||||
value = getRaw(settings);
|
||||
this.accept = accept;
|
||||
}
|
||||
|
||||
|
||||
public boolean prepareApply(Settings settings) {
|
||||
final String newValue = getRaw(settings);
|
||||
if (value.equals(newValue) == false) {
|
||||
T inst = get(settings);
|
||||
try {
|
||||
accept.accept(inst);
|
||||
} catch (Exception | AssertionError e) {
|
||||
throw new IllegalArgumentException("illegal value can't update [" + key + "] from [" + value + "] to [" + getRaw(settings) + "]", e);
|
||||
}
|
||||
pendingValue = newValue;
|
||||
valueInstance = inst;
|
||||
commitPending = true;
|
||||
|
||||
} else {
|
||||
commitPending = false;
|
||||
}
|
||||
return commitPending;
|
||||
}
|
||||
|
||||
public void apply() {
|
||||
if (commitPending) {
|
||||
logger.info("update [{}] from [{}] to [{}]", key, value, pendingValue);
|
||||
value = pendingValue;
|
||||
consumer.accept(valueInstance);
|
||||
}
|
||||
commitPending = false;
|
||||
valueInstance = null;
|
||||
pendingValue = null;
|
||||
}
|
||||
|
||||
public void rollback() {
|
||||
commitPending = false;
|
||||
valueInstance = null;
|
||||
pendingValue = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Updater for: " + Setting.this.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasChanged(Settings current, Settings previous) {
|
||||
final String newValue = getRaw(current);
|
||||
final String value = getRaw(previous);
|
||||
return value.equals(newValue) == false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T getValue(Settings current, Settings previous) {
|
||||
final String newValue = getRaw(current);
|
||||
final String value = getRaw(previous);
|
||||
T inst = get(current);
|
||||
try {
|
||||
accept.accept(inst);
|
||||
} catch (Exception | AssertionError e) {
|
||||
throw new IllegalArgumentException("illegal value can't update [" + key + "] from [" + value + "] to [" + newValue + "]", e);
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(T value, Settings current, Settings previous) {
|
||||
logger.info("update [{}] from [{}] to [{}]", key, getRaw(previous), getRaw(current));
|
||||
consumer.accept(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -329,43 +298,35 @@ public class Setting<T> extends ToXContentToBytes {
|
|||
}
|
||||
|
||||
@Override
|
||||
public AbstractScopedSettings.SettingUpdater newUpdater(Consumer<Settings> consumer, ESLogger logger, Settings settings, Consumer<Settings> accept) {
|
||||
public AbstractScopedSettings.SettingUpdater<Settings> newUpdater(Consumer<Settings> consumer, ESLogger logger, Consumer<Settings> accept) {
|
||||
if (isDynamic() == false) {
|
||||
throw new IllegalStateException("setting [" + getKey() + "] is not dynamic");
|
||||
}
|
||||
final Setting<?> setting = this;
|
||||
return new AbstractScopedSettings.SettingUpdater() {
|
||||
private Settings pendingSettings;
|
||||
private Settings committedSettings = get(settings);
|
||||
return new AbstractScopedSettings.SettingUpdater<Settings>() {
|
||||
|
||||
@Override
|
||||
public boolean prepareApply(Settings settings) {
|
||||
Settings currentSettings = get(settings);
|
||||
if (currentSettings.equals(committedSettings) == false) {
|
||||
public boolean hasChanged(Settings current, Settings previous) {
|
||||
Settings currentSettings = get(current);
|
||||
Settings previousSettings = get(previous);
|
||||
return currentSettings.equals(previousSettings) == false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Settings getValue(Settings current, Settings previous) {
|
||||
Settings currentSettings = get(current);
|
||||
Settings previousSettings = get(previous);
|
||||
try {
|
||||
accept.accept(currentSettings);
|
||||
} catch (Exception | AssertionError e) {
|
||||
throw new IllegalArgumentException("illegal value can't update [" + key + "] from [" + committedSettings.getAsMap() + "] to [" + currentSettings.getAsMap() + "]", e);
|
||||
}
|
||||
pendingSettings = currentSettings;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
throw new IllegalArgumentException("illegal value can't update [" + key + "] from [" + previousSettings.getAsMap() + "] to [" + currentSettings.getAsMap() + "]", e);
|
||||
}
|
||||
return currentSettings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply() {
|
||||
if (pendingSettings != null) {
|
||||
consumer.accept(pendingSettings);
|
||||
committedSettings = pendingSettings;
|
||||
}
|
||||
pendingSettings = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void rollback() {
|
||||
pendingSettings = null;
|
||||
public void apply(Settings value, Settings current, Settings previous) {
|
||||
consumer.accept(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
package org.elasticsearch.common.settings;
|
||||
|
||||
import org.elasticsearch.ElasticsearchParseException;
|
||||
import org.elasticsearch.common.collect.Tuple;
|
||||
import org.elasticsearch.common.unit.ByteSizeValue;
|
||||
import org.elasticsearch.common.unit.TimeValue;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
@ -42,38 +43,33 @@ public class SettingTests extends ESTestCase {
|
|||
ByteSizeValue byteSizeValue = byteSizeValueSetting.get(Settings.EMPTY);
|
||||
assertEquals(byteSizeValue.bytes(), 1024);
|
||||
AtomicReference<ByteSizeValue> value = new AtomicReference<>(null);
|
||||
ClusterSettings.SettingUpdater settingUpdater = byteSizeValueSetting.newUpdater(value::set, logger, Settings.EMPTY);
|
||||
ClusterSettings.SettingUpdater settingUpdater = byteSizeValueSetting.newUpdater(value::set, logger);
|
||||
try {
|
||||
settingUpdater.prepareApply(Settings.builder().put("a.byte.size", 12).build());
|
||||
settingUpdater.apply(Settings.builder().put("a.byte.size", 12).build(), Settings.EMPTY);
|
||||
fail("no unit");
|
||||
} catch (ElasticsearchParseException ex) {
|
||||
assertEquals("failed to parse setting [a.byte.size] with value [12] as a size in bytes: unit is missing or unrecognized", ex.getMessage());
|
||||
}
|
||||
|
||||
assertTrue(settingUpdater.prepareApply(Settings.builder().put("a.byte.size", "12b").build()));
|
||||
settingUpdater.apply();
|
||||
assertTrue(settingUpdater.apply(Settings.builder().put("a.byte.size", "12b").build(), Settings.EMPTY));
|
||||
assertEquals(new ByteSizeValue(12), value.get());
|
||||
}
|
||||
|
||||
public void testSimpleUpdate() {
|
||||
Setting<Boolean> booleanSetting = Setting.boolSetting("foo.bar", false, true, Setting.Scope.CLUSTER);
|
||||
AtomicReference<Boolean> atomicBoolean = new AtomicReference<>(null);
|
||||
ClusterSettings.SettingUpdater settingUpdater = booleanSetting.newUpdater(atomicBoolean::set, logger, Settings.EMPTY);
|
||||
ClusterSettings.SettingUpdater settingUpdater = booleanSetting.newUpdater(atomicBoolean::set, logger);
|
||||
Settings build = Settings.builder().put("foo.bar", false).build();
|
||||
settingUpdater.prepareApply(build);
|
||||
assertNull(atomicBoolean.get());
|
||||
settingUpdater.rollback();
|
||||
settingUpdater.apply(build, Settings.EMPTY);
|
||||
assertNull(atomicBoolean.get());
|
||||
build = Settings.builder().put("foo.bar", true).build();
|
||||
settingUpdater.prepareApply(build);
|
||||
assertNull(atomicBoolean.get());
|
||||
settingUpdater.apply();
|
||||
settingUpdater.apply(build, Settings.EMPTY);
|
||||
assertTrue(atomicBoolean.get());
|
||||
|
||||
// try update bogus value
|
||||
build = Settings.builder().put("foo.bar", "I am not a boolean").build();
|
||||
try {
|
||||
settingUpdater.prepareApply(build);
|
||||
settingUpdater.apply(build, Settings.EMPTY);
|
||||
fail("not a boolean");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertEquals("Failed to parse value [I am not a boolean] for setting [foo.bar]", ex.getMessage());
|
||||
|
@ -85,7 +81,7 @@ public class SettingTests extends ESTestCase {
|
|||
assertFalse(booleanSetting.isGroupSetting());
|
||||
AtomicReference<Boolean> atomicBoolean = new AtomicReference<>(null);
|
||||
try {
|
||||
booleanSetting.newUpdater(atomicBoolean::set, logger, Settings.EMPTY);
|
||||
booleanSetting.newUpdater(atomicBoolean::set, logger);
|
||||
fail("not dynamic");
|
||||
} catch (IllegalStateException ex) {
|
||||
assertEquals("setting [foo.bar] is not dynamic", ex.getMessage());
|
||||
|
@ -96,11 +92,9 @@ public class SettingTests extends ESTestCase {
|
|||
Setting<Boolean> booleanSetting = Setting.boolSetting("foo.bar", false, true, Setting.Scope.CLUSTER);
|
||||
AtomicReference<Boolean> ab1 = new AtomicReference<>(null);
|
||||
AtomicReference<Boolean> ab2 = new AtomicReference<>(null);
|
||||
ClusterSettings.SettingUpdater settingUpdater = booleanSetting.newUpdater(ab1::set, logger, Settings.EMPTY);
|
||||
settingUpdater.prepareApply(Settings.builder().put("foo.bar", true).build());
|
||||
assertNull(ab1.get());
|
||||
assertNull(ab2.get());
|
||||
settingUpdater.apply();
|
||||
ClusterSettings.SettingUpdater settingUpdater = booleanSetting.newUpdater(ab1::set, logger);
|
||||
ClusterSettings.SettingUpdater settingUpdater2 = booleanSetting.newUpdater(ab2::set, logger);
|
||||
settingUpdater.apply(Settings.builder().put("foo.bar", true).build(), Settings.EMPTY);
|
||||
assertTrue(ab1.get());
|
||||
assertNull(ab2.get());
|
||||
}
|
||||
|
@ -124,40 +118,22 @@ public class SettingTests extends ESTestCase {
|
|||
assertFalse(setting.isGroupSetting());
|
||||
ref.set(setting.get(Settings.EMPTY));
|
||||
ComplexType type = ref.get();
|
||||
ClusterSettings.SettingUpdater settingUpdater = setting.newUpdater(ref::set, logger, Settings.EMPTY);
|
||||
assertFalse(settingUpdater.prepareApply(Settings.EMPTY));
|
||||
settingUpdater.apply();
|
||||
ClusterSettings.SettingUpdater settingUpdater = setting.newUpdater(ref::set, logger);
|
||||
assertFalse(settingUpdater.apply(Settings.EMPTY, Settings.EMPTY));
|
||||
assertSame("no update - type has not changed", type, ref.get());
|
||||
|
||||
// change from default
|
||||
assertTrue(settingUpdater.prepareApply(Settings.builder().put("foo.bar", "2").build()));
|
||||
settingUpdater.apply();
|
||||
assertTrue(settingUpdater.apply(Settings.builder().put("foo.bar", "2").build(), Settings.EMPTY));
|
||||
assertNotSame("update - type has changed", type, ref.get());
|
||||
assertEquals("2", ref.get().foo);
|
||||
|
||||
|
||||
// change back to default...
|
||||
assertTrue(settingUpdater.prepareApply(Settings.builder().put("foo.bar.baz", "2").build()));
|
||||
settingUpdater.apply();
|
||||
assertTrue(settingUpdater.apply(Settings.EMPTY, Settings.builder().put("foo.bar", "2").build()));
|
||||
assertNotSame("update - type has changed", type, ref.get());
|
||||
assertEquals("", ref.get().foo);
|
||||
}
|
||||
|
||||
public void testRollback() {
|
||||
Setting<Integer> integerSetting = Setting.intSetting("foo.int.bar", 1, true, Setting.Scope.CLUSTER);
|
||||
assertFalse(integerSetting.isGroupSetting());
|
||||
AtomicReference<Integer> ref = new AtomicReference<>(null);
|
||||
ClusterSettings.SettingUpdater settingUpdater = integerSetting.newUpdater(ref::set, logger, Settings.EMPTY);
|
||||
assertNull(ref.get());
|
||||
assertTrue(settingUpdater.prepareApply(Settings.builder().put("foo.int.bar", "2").build()));
|
||||
settingUpdater.rollback();
|
||||
settingUpdater.apply();
|
||||
assertNull(ref.get());
|
||||
assertTrue(settingUpdater.prepareApply(Settings.builder().put("foo.int.bar", "2").build()));
|
||||
settingUpdater.apply();
|
||||
assertEquals(2, ref.get().intValue());
|
||||
}
|
||||
|
||||
public void testType() {
|
||||
Setting<Integer> integerSetting = Setting.intSetting("foo.int.bar", 1, true, Setting.Scope.CLUSTER);
|
||||
assertEquals(integerSetting.getScope(), Setting.Scope.CLUSTER);
|
||||
|
@ -169,10 +145,11 @@ public class SettingTests extends ESTestCase {
|
|||
AtomicReference<Settings> ref = new AtomicReference<>(null);
|
||||
Setting<Settings> setting = Setting.groupSetting("foo.bar.", true, Setting.Scope.CLUSTER);
|
||||
assertTrue(setting.isGroupSetting());
|
||||
ClusterSettings.SettingUpdater settingUpdater = setting.newUpdater(ref::set, logger, Settings.EMPTY);
|
||||
ClusterSettings.SettingUpdater settingUpdater = setting.newUpdater(ref::set, logger);
|
||||
|
||||
assertTrue(settingUpdater.prepareApply(Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "2").put("foo.bar.3.value", "3").build()));
|
||||
settingUpdater.apply();
|
||||
Settings currentInput = Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "2").put("foo.bar.3.value", "3").build();
|
||||
Settings previousInput = Settings.EMPTY;
|
||||
assertTrue(settingUpdater.apply(currentInput, previousInput));
|
||||
assertNotNull(ref.get());
|
||||
Settings settings = ref.get();
|
||||
Map<String, Settings> asMap = settings.getAsGroups();
|
||||
|
@ -181,14 +158,16 @@ public class SettingTests extends ESTestCase {
|
|||
assertEquals(asMap.get("2").get("value"), "2");
|
||||
assertEquals(asMap.get("3").get("value"), "3");
|
||||
|
||||
previousInput = currentInput;
|
||||
currentInput = Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "2").put("foo.bar.3.value", "3").build();
|
||||
Settings current = ref.get();
|
||||
assertFalse(settingUpdater.prepareApply(Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "2").put("foo.bar.3.value", "3").build()));
|
||||
settingUpdater.apply();
|
||||
assertFalse(settingUpdater.apply(currentInput, previousInput));
|
||||
assertSame(current, ref.get());
|
||||
|
||||
previousInput = currentInput;
|
||||
currentInput = Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "2").build();
|
||||
// now update and check that we got it
|
||||
assertTrue(settingUpdater.prepareApply(Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "2").build()));
|
||||
settingUpdater.apply();
|
||||
assertTrue(settingUpdater.apply(currentInput, previousInput));
|
||||
assertNotSame(current, ref.get());
|
||||
|
||||
asMap = ref.get().getAsGroups();
|
||||
|
@ -196,9 +175,10 @@ public class SettingTests extends ESTestCase {
|
|||
assertEquals(asMap.get("1").get("value"), "1");
|
||||
assertEquals(asMap.get("2").get("value"), "2");
|
||||
|
||||
previousInput = currentInput;
|
||||
currentInput = Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "4").build();
|
||||
// now update and check that we got it
|
||||
assertTrue(settingUpdater.prepareApply(Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "4").build()));
|
||||
settingUpdater.apply();
|
||||
assertTrue(settingUpdater.apply(currentInput, previousInput));
|
||||
assertNotSame(current, ref.get());
|
||||
|
||||
asMap = ref.get().getAsGroups();
|
||||
|
@ -209,9 +189,9 @@ public class SettingTests extends ESTestCase {
|
|||
assertTrue(setting.match("foo.bar.baz"));
|
||||
assertFalse(setting.match("foo.baz.bar"));
|
||||
|
||||
ClusterSettings.SettingUpdater predicateSettingUpdater = setting.newUpdater(ref::set, logger, Settings.EMPTY, (s) -> assertFalse(true));
|
||||
ClusterSettings.SettingUpdater predicateSettingUpdater = setting.newUpdater(ref::set, logger,(s) -> assertFalse(true));
|
||||
try {
|
||||
predicateSettingUpdater.prepareApply(Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "2").build());
|
||||
predicateSettingUpdater.apply(Settings.builder().put("foo.bar.1.value", "1").put("foo.bar.2.value", "2").build(), Settings.EMPTY);
|
||||
fail("not accepted");
|
||||
} catch (IllegalArgumentException ex) {
|
||||
assertEquals(ex.getMessage(), "illegal value can't update [foo.bar.] from [{}] to [{1.value=1, 2.value=2}]");
|
||||
|
@ -243,32 +223,27 @@ public class SettingTests extends ESTestCase {
|
|||
Composite c = new Composite();
|
||||
Setting<Integer> a = Setting.intSetting("foo.int.bar.a", 1, true, Setting.Scope.CLUSTER);
|
||||
Setting<Integer> b = Setting.intSetting("foo.int.bar.b", 1, true, Setting.Scope.CLUSTER);
|
||||
ClusterSettings.SettingUpdater settingUpdater = Setting.compoundUpdater(c::set, a, b, logger, Settings.EMPTY);
|
||||
assertFalse(settingUpdater.prepareApply(Settings.EMPTY));
|
||||
settingUpdater.apply();
|
||||
ClusterSettings.SettingUpdater<Tuple<Integer, Integer>> settingUpdater = Setting.compoundUpdater(c::set, a, b, logger);
|
||||
assertFalse(settingUpdater.apply(Settings.EMPTY, Settings.EMPTY));
|
||||
assertNull(c.a);
|
||||
assertNull(c.b);
|
||||
|
||||
Settings build = Settings.builder().put("foo.int.bar.a", 2).build();
|
||||
assertTrue(settingUpdater.prepareApply(build));
|
||||
settingUpdater.apply();
|
||||
assertTrue(settingUpdater.apply(build, Settings.EMPTY));
|
||||
assertEquals(2, c.a.intValue());
|
||||
assertNull(c.b);
|
||||
assertEquals(1, c.b.intValue());
|
||||
|
||||
Integer aValue = c.a;
|
||||
assertFalse(settingUpdater.prepareApply(build));
|
||||
settingUpdater.apply();
|
||||
assertFalse(settingUpdater.apply(build, build));
|
||||
assertSame(aValue, c.a);
|
||||
|
||||
Settings previous = build;
|
||||
build = Settings.builder().put("foo.int.bar.a", 2).put("foo.int.bar.b", 5).build();
|
||||
assertTrue(settingUpdater.prepareApply(build));
|
||||
settingUpdater.apply();
|
||||
assertTrue(settingUpdater.apply(build, previous));
|
||||
assertEquals(2, c.a.intValue());
|
||||
assertEquals(5, c.b.intValue());
|
||||
|
||||
// reset to default
|
||||
assertTrue(settingUpdater.prepareApply(Settings.EMPTY));
|
||||
settingUpdater.apply();
|
||||
assertTrue(settingUpdater.apply(Settings.EMPTY, build));
|
||||
assertEquals(1, c.a.intValue());
|
||||
assertEquals(1, c.b.intValue());
|
||||
|
||||
|
|
Loading…
Reference in New Issue