Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate out validation of groups of settings #34184

Merged
merged 44 commits into from
Jan 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
8852b05
Validate current setting against all target settings (#28309)
cbismuth Oct 1, 2018
5a84c18
Fix misspelled comment (#28309)
cbismuth Oct 2, 2018
71cfe1a
Test sequence of updates (#28309)
cbismuth Oct 4, 2018
f77d1e7
Add setting to apply to validation collection (#28309)
cbismuth Oct 4, 2018
b0dd2de
Test update of validation dependant settings (#28309)
cbismuth Oct 4, 2018
177f560
Fix comment (#28309)
cbismuth Oct 5, 2018
ad0212e
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
cbismuth Oct 5, 2018
94ac1d1
Improve variable naming to ease readability (#28309)
cbismuth Oct 5, 2018
68044ba
Don't update settings when no value has changed (#28309)
cbismuth Oct 5, 2018
395c398
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
cbismuth Oct 8, 2018
8e138d9
Move validation API with other ones (#28309)
cbismuth Oct 8, 2018
6539bd0
Add a test case when settings are below "low" default value (#28309)
cbismuth Oct 8, 2018
df341e9
Rework "testSequenceOfUpdates" with "updateSettings" API (#28309)
cbismuth Oct 8, 2018
c44b2a3
Filter target settings eagerly to avoid unnecessary validations (#28309)
cbismuth Oct 8, 2018
3e67ca4
Split settings validation with and without dependencies (#28309)
cbismuth Oct 8, 2018
75048ac
Reduce diffs with "master" branch (#28309)
cbismuth Oct 8, 2018
158c482
Improve Java documentation (#28309)
cbismuth Oct 9, 2018
c195f74
Extract methods to create unknown and invalid settings in tests (#28309)
cbismuth Oct 9, 2018
3e0f417
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
cbismuth Oct 26, 2018
8d2e144
Fix compilation after rebasing with master (#28309)
cbismuth Oct 26, 2018
88f42ed
Add missing validation step without dependencies validation (#28309)
cbismuth Oct 26, 2018
5ff1d59
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
cbismuth Oct 26, 2018
5bded6e
Improve JavaDoc comment (#28309).
DaveCTurner Oct 30, 2018
9890ab7
Improve JavaDoc comment (#28309)
DaveCTurner Oct 30, 2018
f277ae9
Fix typo in test name (#28309)
DaveCTurner Oct 30, 2018
e4e7a64
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
cbismuth Oct 31, 2018
2efdf1e
Split invalid in-isolation/with-dependencies test settings (#28309)
cbismuth Oct 31, 2018
d6f99cf
Merge remote-tracking branch 'origin/28309_disk_watermark_validation_…
cbismuth Oct 31, 2018
3e58473
Split invalid in-isolation/with-dependencies setting validation (#28309)
cbismuth Oct 31, 2018
adf128b
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
cbismuth Oct 31, 2018
43840f1
Fix Checkstyle line length violation (#28309)
cbismuth Oct 31, 2018
1447577
Rework simple string setting with validator API (#28309)
cbismuth Oct 31, 2018
64aeb4c
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
cbismuth Nov 5, 2018
bfcd828
Remove unnecessary parentheses around lambdas (#28309)
cbismuth Nov 5, 2018
453a03a
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
cbismuth Nov 5, 2018
e83e210
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
cbismuth Nov 15, 2018
7c33bfe
Fix Java documentation of the Validator class (#elasticsearch-28309)
cbismuth Nov 16, 2018
17b3ab2
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
cbismuth Nov 16, 2018
adf5afb
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
cbismuth Nov 16, 2018
e277b81
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
cbismuth Nov 19, 2018
da718f1
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
cbismuth Nov 25, 2018
f76692f
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
cbismuth Dec 10, 2018
de93e7c
Merge branch 'master' into 28309_disk_watermark_validation_rejects_ap…
DaveCTurner Jan 7, 2019
02a62fb
Minor Javadoc rewording
DaveCTurner Jan 7, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public class ExampleCustomSettingsConfig {
/**
* A string setting that can be dynamically updated and that is validated by some logic
*/
static final Setting<String> VALIDATED_SETTING = Setting.simpleString("custom.validated", (value, settings) -> {
static final Setting<String> VALIDATED_SETTING = Setting.simpleString("custom.validated", value -> {
if (value != null && value.contains("forbidden")) {
throw new IllegalArgumentException("Setting must not contain [forbidden]");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ static Setting<Integer> buildNumberOfShardsSetting() {
public static final Setting<Integer> INDEX_NUMBER_OF_ROUTING_SHARDS_SETTING =
Setting.intSetting("index.number_of_routing_shards", INDEX_NUMBER_OF_SHARDS_SETTING,
1, new Setting.Validator<Integer>() {
@Override
public void validate(Integer value) {
}

@Override
public void validate(Integer numRoutingShards, Map<Setting<Integer>, Integer> settings) {
Integer numShards = settings.get(INDEX_NUMBER_OF_SHARDS_SETTING);
Expand Down Expand Up @@ -223,14 +227,14 @@ public Iterator<Setting<Integer>> settings() {
public static final String INDEX_ROUTING_INCLUDE_GROUP_PREFIX = "index.routing.allocation.include";
public static final String INDEX_ROUTING_EXCLUDE_GROUP_PREFIX = "index.routing.allocation.exclude";
public static final Setting.AffixSetting<String> INDEX_ROUTING_REQUIRE_GROUP_SETTING =
Setting.prefixKeySetting(INDEX_ROUTING_REQUIRE_GROUP_PREFIX + ".", (key) ->
Setting.simpleString(key, (value, map) -> IP_VALIDATOR.accept(key, value), Property.Dynamic, Property.IndexScope));
Setting.prefixKeySetting(INDEX_ROUTING_REQUIRE_GROUP_PREFIX + ".", key ->
Setting.simpleString(key, value -> IP_VALIDATOR.accept(key, value), Property.Dynamic, Property.IndexScope));
public static final Setting.AffixSetting<String> INDEX_ROUTING_INCLUDE_GROUP_SETTING =
Setting.prefixKeySetting(INDEX_ROUTING_INCLUDE_GROUP_PREFIX + ".", (key) ->
Setting.simpleString(key, (value, map) -> IP_VALIDATOR.accept(key, value), Property.Dynamic, Property.IndexScope));
Setting.prefixKeySetting(INDEX_ROUTING_INCLUDE_GROUP_PREFIX + ".", key ->
Setting.simpleString(key, value -> IP_VALIDATOR.accept(key, value), Property.Dynamic, Property.IndexScope));
public static final Setting.AffixSetting<String> INDEX_ROUTING_EXCLUDE_GROUP_SETTING =
Setting.prefixKeySetting(INDEX_ROUTING_EXCLUDE_GROUP_PREFIX + ".", (key) ->
Setting.simpleString(key, (value, map) -> IP_VALIDATOR.accept(key, value), Property.Dynamic, Property.IndexScope));
Setting.prefixKeySetting(INDEX_ROUTING_EXCLUDE_GROUP_PREFIX + ".", key ->
Setting.simpleString(key, value -> IP_VALIDATOR.accept(key, value), Property.Dynamic, Property.IndexScope));
public static final Setting.AffixSetting<String> INDEX_ROUTING_INITIAL_RECOVERY_GROUP_SETTING =
Setting.prefixKeySetting("index.routing.allocation.initial_recovery.", key -> Setting.simpleString(key));
// this is only setable internally not a registered setting!!
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ public DiskThresholdSettings(Settings settings, ClusterSettings clusterSettings)

static final class LowDiskWatermarkValidator implements Setting.Validator<String> {

@Override
public void validate(String value) {
}

@Override
public void validate(String value, Map<Setting<String>, String> settings) {
final String highWatermarkRaw = settings.get(CLUSTER_ROUTING_ALLOCATION_HIGH_DISK_WATERMARK_SETTING);
Expand All @@ -112,6 +116,10 @@ public Iterator<Setting<String>> settings() {

static final class HighDiskWatermarkValidator implements Setting.Validator<String> {

@Override
public void validate(String value) {
}

@Override
public void validate(String value, Map<Setting<String>, String> settings) {
final String lowWatermarkRaw = settings.get(CLUSTER_ROUTING_ALLOCATION_LOW_DISK_WATERMARK_SETTING);
Expand All @@ -131,6 +139,10 @@ public Iterator<Setting<String>> settings() {

static final class FloodStageValidator implements Setting.Validator<String> {

@Override
public void validate(String value) {
}

@Override
public void validate(String value, Map<Setting<String>, String> settings) {
final String lowWatermarkRaw = settings.get(CLUSTER_ROUTING_ALLOCATION_LOW_DISK_WATERMARK_SETTING);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@ public class FilterAllocationDecider extends AllocationDecider {
private static final String CLUSTER_ROUTING_INCLUDE_GROUP_PREFIX = "cluster.routing.allocation.include";
private static final String CLUSTER_ROUTING_EXCLUDE_GROUP_PREFIX = "cluster.routing.allocation.exclude";
public static final Setting.AffixSetting<String> CLUSTER_ROUTING_REQUIRE_GROUP_SETTING =
Setting.prefixKeySetting(CLUSTER_ROUTING_REQUIRE_GROUP_PREFIX + ".", (key) ->
Setting.simpleString(key, (value, map) -> IP_VALIDATOR.accept(key, value), Property.Dynamic, Property.NodeScope));
Setting.prefixKeySetting(CLUSTER_ROUTING_REQUIRE_GROUP_PREFIX + ".", key ->
Setting.simpleString(key, value -> IP_VALIDATOR.accept(key, value), Property.Dynamic, Property.NodeScope));
public static final Setting.AffixSetting<String> CLUSTER_ROUTING_INCLUDE_GROUP_SETTING =
Setting.prefixKeySetting(CLUSTER_ROUTING_INCLUDE_GROUP_PREFIX + ".", (key) ->
Setting.simpleString(key, (value, map) -> IP_VALIDATOR.accept(key, value), Property.Dynamic, Property.NodeScope));
Setting.prefixKeySetting(CLUSTER_ROUTING_INCLUDE_GROUP_PREFIX + ".", key ->
Setting.simpleString(key, value -> IP_VALIDATOR.accept(key, value), Property.Dynamic, Property.NodeScope));
public static final Setting.AffixSetting<String>CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING =
Setting.prefixKeySetting(CLUSTER_ROUTING_EXCLUDE_GROUP_PREFIX + ".", (key) ->
Setting.simpleString(key, (value, map) -> IP_VALIDATOR.accept(key, value), Property.Dynamic, Property.NodeScope));
Setting.prefixKeySetting(CLUSTER_ROUTING_EXCLUDE_GROUP_PREFIX + ".", key ->
Setting.simpleString(key, value -> IP_VALIDATOR.accept(key, value), Property.Dynamic, Property.NodeScope));

/**
* The set of {@link RecoverySource.Type} values for which the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -723,7 +723,7 @@ private boolean updateSettings(Settings toApply, Settings.Builder target, Settin
} else if (get(key) == null) {
throw new IllegalArgumentException(type + " setting [" + key + "], not recognized");
} else if (isDelete == false && canUpdate.test(key)) {
validate(key, toApply, false); // we might not have a full picture here do to a dependency validation
get(key).validateWithoutDependencies(toApply); // we might not have a full picture here do to a dependency validation
settingsBuilder.copy(key, toApply);
updates.copy(key, toApply);
changed |= toApply.get(key).equals(target.get(key)) == false;
Expand Down
52 changes: 35 additions & 17 deletions server/src/main/java/org/elasticsearch/common/settings/Setting.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ private void checkPropertyRequiresIndexScope(final EnumSet<Property> properties,
* @param properties properties for this setting like scope, filtering...
*/
public Setting(Key key, Function<Settings, String> defaultValue, Function<String, T> parser, Property... properties) {
this(key, defaultValue, parser, (v, s) -> {}, properties);
this(key, defaultValue, parser, v -> {}, properties);
}

/**
Expand Down Expand Up @@ -246,7 +246,7 @@ public Setting(String key, Function<Settings, String> defaultValue, Function<Str
* @param properties properties for this setting like scope, filtering...
*/
public Setting(Key key, Setting<T> fallbackSetting, Function<String, T> parser, Property... properties) {
this(key, fallbackSetting, fallbackSetting::getRaw, parser, (v, m) -> {}, properties);
this(key, fallbackSetting, fallbackSetting::getRaw, parser, v -> {}, properties);
}

/**
Expand Down Expand Up @@ -354,6 +354,14 @@ boolean hasComplexMatcher() {
return isGroupSetting();
}

/**
* Validate the current setting value only without dependencies with {@link Setting.Validator#validate(Object)}.
* @param settings a settings object for settings that has a default value depending on another setting if available
*/
void validateWithoutDependencies(Settings settings) {
validator.validate(get(settings, false));
}

/**
* Returns the default value string representation for this setting.
* @param settings a settings object for settings that has a default value depending on another setting if available
Expand Down Expand Up @@ -414,6 +422,7 @@ private T get(Settings settings, boolean validate) {
} else {
map = Collections.emptyMap();
}
validator.validate(parsed);
validator.validate(parsed, map);
}
return parsed;
Expand Down Expand Up @@ -805,26 +814,39 @@ public Map<String, T> getAsMap(Settings settings) {
}

/**
* Represents a validator for a setting. The {@link #validate(Object, Map)} method is invoked with the value of this setting and a map
* from the settings specified by {@link #settings()}} to their values. All these values come from the same {@link Settings} instance.
* Represents a validator for a setting. The {@link #validate(Object)} method is invoked early in the update setting process with the
* value of this setting for a fail-fast validation. Later on, the {@link #validate(Object, Map)} method is invoked with the value of
* this setting and a map from the settings specified by {@link #settings()}} to their values. All these values come from the same
* {@link Settings} instance.
*
* @param <T> the type of the {@link Setting}
*/
@FunctionalInterface
public interface Validator<T> {

/**
* The validation routine for this validator.
* Validate this setting's value in isolation.
*
* @param value the value of this setting
*/
void validate(T value);

/**
* Validate this setting against its dependencies, specified by {@link #settings()}. The default implementation does nothing,
* accepting any value as valid as long as it passes the validation in {@link #validate(Object)}.
*
* @param value the value of this setting
* @param settings a map from the settings specified by {@link #settings()}} to their values
*/
void validate(T value, Map<Setting<T>, T> settings);
default void validate(T value, Map<Setting<T>, T> settings) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to delegate to validate(T value) here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from an interface perspective I think we might be able to only have validate(T value, Optional<Map<Setting<T>, T>> settings) and instead check the optional if needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @s1monw.

I'd prefer to keep these two API as most clients will only be interested in validating a setting in isolation (i.e. not against dependencies).
Besides, we do a fail-fast validation here where we don't have resolved setting dependencies yet. Therefore, if we want to keep this fail-fast shortcut, any client will have to implement an if optional present/absent block even if he's interested in validating a setting against its dependencies.

What do you think about these two points?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I prefer the two methods to the optional parameter too. In practice there are very few dependencies between settings, so the one-argument lambda is less noisy in almost all cases. Settings with dependencies can't use a lambda anyway because they have to override the settings() function too.

I also think there's no need for the dependent validation to delegate to the other one, because we call both during settings validation. However I think this should be mentioned in the Javadocs as implementors shouldn't need to go to the implementation to check this (and I know I'd forget and have to check). Also the interface-level comment needs updating as it's no longer wholly accurate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @DaveCTurner, here is a additional commit 7c33bfe to improve the Validator class documentation, thank you.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have strong feelings but if I see two methods I'd only implement one and don't know what I need to do with the other. The optional makes me think about the opportunity to validate against others. I don't know what you mean by clients?

Copy link
Contributor Author

@cbismuth cbismuth Nov 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant developers who will implement this interface.

We tried to make javadoc more explicit, but I see what you mean: the API doesn't enforce the developer to ask himself whether or not he would like to implement dependencies validation. But as it's an edge case, I still hope the developer would read the javadoc or jump to the interface definition if he needs to do so.

Copy link
Contributor Author

@cbismuth cbismuth Nov 19, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another alternative would be to remove the validation shortcut and keep only the validate(T value, Map<Setting<T>, T> settings) interface API, I think that would make sense.

We would remove this validation shortcut here and keep only this validation call here (and remove this line).

What do you think about it?

}

/**
* The settings needed by this validator.
* The settings on which the validity of this setting depends. The values of the specified settings are passed to
* {@link #validate(Object, Map)}. By default this returns an empty iterator, indicating that this setting does not depend on any
* other settings.
*
* @return the settings needed to validate; these can be used for cross-settings validation
* @return the settings on which the validity of this setting depends.
*/
default Iterator<Setting<T>> settings() {
return Collections.emptyIterator();
Expand Down Expand Up @@ -1021,8 +1043,8 @@ public static Setting<String> simpleString(String key, Property... properties) {
return new Setting<>(key, s -> "", Function.identity(), properties);
}

public static Setting<String> simpleString(String key, Function<String, String> parser, Property... properties) {
return new Setting<>(key, s -> "", parser, properties);
public static Setting<String> simpleString(String key, Validator<String> validator, Property... properties) {
return new Setting<>(new SimpleKey(key), null, s -> "", Function.identity(), validator, properties);
}

public static Setting<String> simpleString(String key, Setting<String> fallback, Property... properties) {
Expand All @@ -1037,10 +1059,6 @@ public static Setting<String> simpleString(
return new Setting<>(key, fallback, parser, properties);
}

public static Setting<String> simpleString(String key, Validator<String> validator, Property... properties) {
return new Setting<>(new SimpleKey(key), null, s -> "", Function.identity(), validator, properties);
}

/**
* Creates a new Setting instance with a String value
*
Expand Down Expand Up @@ -1279,9 +1297,9 @@ private ListSetting(
super(
new ListKey(key),
fallbackSetting,
(s) -> Setting.arrayToParsableString(defaultStringValue.apply(s)),
s -> Setting.arrayToParsableString(defaultStringValue.apply(s)),
parser,
(v,s) -> {},
v -> {},
properties);
this.defaultStringValue = defaultStringValue;
}
Expand Down Expand Up @@ -1339,7 +1357,7 @@ public static Setting<TimeValue> timeSetting(
fallbackSetting,
fallbackSetting::getRaw,
minTimeValueParser(key, minValue),
(v, s) -> {},
v -> {},
properties);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,12 @@ public final class AutoQueueAdjustingExecutorBuilder extends ExecutorBuilder<Aut
this.minQueueSizeSetting = new Setting<>(
minSizeKey,
Integer.toString(minQueueSize),
(s) -> Setting.parseInt(s, 0, minSizeKey),
s -> Setting.parseInt(s, 0, minSizeKey),
new Setting.Validator<Integer>() {
@Override
public void validate(Integer value) {
}

@Override
public void validate(Integer value, Map<Setting<Integer>, Integer> settings) {
if (value > settings.get(tempMaxQueueSizeSetting)) {
Expand All @@ -94,8 +98,12 @@ public Iterator<Setting<Integer>> settings() {
this.maxQueueSizeSetting = new Setting<>(
maxSizeKey,
Integer.toString(maxQueueSize),
(s) -> Setting.parseInt(s, 0, maxSizeKey),
s -> Setting.parseInt(s, 0, maxSizeKey),
new Setting.Validator<Integer>() {
@Override
public void validate(Integer value) {
}

@Override
public void validate(Integer value, Map<Setting<Integer>, Integer> settings) {
if (value < settings.get(tempMinQueueSizeSetting)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ public String getKey(final String key) {
if (Strings.hasLength(s)) {
parsePort(s);
}
return s;
},
Setting.Property.Deprecated,
Setting.Property.Dynamic,
Expand Down
Loading