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

Allow affix settings to specify dependencies #27161

Merged
merged 12 commits into from
Nov 13, 2017

Conversation

s1monw
Copy link
Contributor

@s1monw s1monw commented Oct 29, 2017

We use affix settings to group settings / values under a certain namespace.
In some cases like login information for instance a setting is only valid if
one or more other settings are present. For instance x.test.user is only valid
if there is an x.test.passwd present and vice versa. This change allows to specify
such a dependency to prevent settings updates that leave settings in an inconsistent
state.

We use affix settings to group settings / values under a certain namespace.
In some cases like login information for instance a setting is only valid if
one or more other settings are present. For instance `x.test.user` is only valid
if there is an `x.test.passwd` present and vice versa. This change allows to specify
such a dependency to prevent settings updates that leave settings in an inconsistent
state.
@s1monw
Copy link
Contributor Author

s1monw commented Oct 29, 2017

/cc @javanna

@javanna
Copy link
Member

javanna commented Oct 31, 2017

@s1monw I have been trying this out in #27182. Do I understand correctly that a setting which is dependent on another setting must always be provided in the same request as its dependency? That makes sense for user and password, but in CCS one may want to change the skip_if_disconnected setting without touching the seeds, in which case an error is thrown as seeds haven't been specified in the same request, although they were previously set for that remote cluster. Not even sure if this is possible, but we may want to consider this. Thoughts?

You can reproduce with the following unit test:

AbstractScopedSettings service = new ClusterSettings(Settings.builder().put("search.remote.foo.seeds", seed).build(),
                        new HashSet<>(Arrays.asList(RemoteClusterAware.REMOTE_CLUSTERS_SEEDS,
                                RemoteClusterService.REMOTE_CLUSTER_SKIP_IF_DISCONNECTED)));
service.validate(Settings.builder().put("search.remote.foo.skip_if_disconnected", randomBoolean()));

@s1monw
Copy link
Contributor Author

s1monw commented Nov 1, 2017

@javanna I applied your feedback thx

@s1monw
Copy link
Contributor Author

s1monw commented Nov 1, 2017

@elasticmachine test this please

@s1monw
Copy link
Contributor Author

s1monw commented Nov 2, 2017

@javanna can you take another look?

Copy link
Member

@javanna javanna left a comment

Choose a reason for hiding this comment

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

I tested it out and it works well with the changes from #27182 . I left a few minor comments and a couple of questions. The only minor thing that I wish we could improve, but I am not sure we can, is the error message when you set to null a setting that another one left behind depends on. It may not be clear to users what they have to do to fix that (e.g. set to null the other setting too).

public static final Setting<String> PROXY_HOST_SETTING = Setting.affixKeySetting(PREFIX, "proxy.host",
(key) -> Setting.simpleString(key, Property.NodeScope));
public static final Setting<String> PROXY_HOST_SETTING = Setting.affixKeySetting("azure.client.", "proxy.host",
(key) -> Setting.simpleString(key, Property.NodeScope), KEY_SETTING, ACCOUNT_SETTING, KEY_SETTING);
Copy link
Member

Choose a reason for hiding this comment

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

I guess KEY_SETTING shouldn't be repeated twice?

public static final Setting<Integer> PROXY_PORT_SETTING = Setting.affixKeySetting(PREFIX, "proxy.port",
(key) -> Setting.intSetting(key, 0, 0, 65535, Setting.Property.NodeScope));
public static final Setting<Integer> PROXY_PORT_SETTING = Setting.affixKeySetting("azure.client.", "proxy.port",
(key) -> Setting.intSetting(key, 0, 0, 65535, Setting.Property.NodeScope), ACCOUNT_SETTING, KEY_SETTING);
Copy link
Member

Choose a reason for hiding this comment

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

wondering, shouldn't it also depend on proxy_host ?

(key) -> new Setting<>(key, "direct", s -> Proxy.Type.valueOf(s.toUpperCase(Locale.ROOT)), Property.NodeScope));
public static final AffixSetting<Proxy.Type> PROXY_TYPE_SETTING = Setting.affixKeySetting("azure.client.", "proxy.type",
(key) -> new Setting<>(key, "direct", s -> Proxy.Type.valueOf(s.toUpperCase(Locale.ROOT)), Property.NodeScope)
, ACCOUNT_SETTING, KEY_SETTING);
Copy link
Member

Choose a reason for hiding this comment

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

not too familiar with these settings, does proxy_type depend also on proxy_host and proxy_port?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah I am not sure about that


}


Copy link
Member

Choose a reason for hiding this comment

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

nit: remove this additional empty line

* Returns distinct namespaces for the given settings
*/
public Set<String> getNamespaces(Settings settings) {
return settings.keySet().stream().filter((key) -> match(key)).map(key::getNamespace).collect(Collectors.toSet());
Copy link
Member

Choose a reason for hiding this comment

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

nit: match(key) could be replaced with a method reference this::match

@@ -240,7 +241,9 @@ public ClusterState execute(ClusterState currentState) {
if (preserveExisting) {
indexSettings.put(indexMetaData.getSettings());
}
metaDataBuilder.put(IndexMetaData.builder(indexMetaData).settings(indexSettings));
Settings finalSettings = indexSettings.build();
indexScopedSettings.validate(finalSettings.filter(k -> indexScopedSettings.isPrivateSetting(k) == false), true);
Copy link
Member

Choose a reason for hiding this comment

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

question: private settings are internal only, hence we want to get them out of the validation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah

Settings.Builder persistentSettings = Settings.builder();
persistentSettings.put(currentState.metaData().persistentSettings());
changed |= clusterSettings.updateDynamicSettings(persistentToApply, persistentSettings, persistentUpdates, "persistent");

final ClusterState clusterState;
if (changed) {
Settings transientFinalSettings = transientSettings.build();
Settings persistentFinalSettings = persistentSettings.build();
// both transient and persistent settings must be consistent by itself we can't allow dependencies to be
Copy link
Member

Choose a reason for hiding this comment

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

"must be consistent on their own?"

Copy link
Member

Choose a reason for hiding this comment

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

"we can't allow dependencies between the two"?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

what if you have setting A to be transient and then it disappears on a full cluster restart?

Copy link
Member

Choose a reason for hiding this comment

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

sure I agree, I was just suggesting to rephrase the comment :)

@@ -276,7 +276,7 @@ private void validate(PutRequest request) {
}

try {
indexScopedSettings.validate(request.settings);
indexScopedSettings.validate(request.settings, true); // templates must be consistent with regards to dependecies
Copy link
Member

Choose a reason for hiding this comment

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

dependencies

metaDataBuilder.put(IndexMetaData.builder(indexMetaData).settings(indexSettings));
Settings finalSettings = indexSettings.build();
indexScopedSettings.validate(finalSettings.filter(k -> indexScopedSettings.isPrivateSetting(k) == false), true);
metaDataBuilder.put(IndexMetaData.builder(indexMetaData).settings(finalSettings));
Copy link
Member

Choose a reason for hiding this comment

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

I was wondering if this second step wasn't enough, compared to the first validation step that doesn't look at dependencies. Why have the two steps?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

one happens before we pass on to a clusterstate update task we should validate as soon as we can. but we have to do it here again

Copy link
Member

@jasontedor jasontedor left a comment

Choose a reason for hiding this comment

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

This looks good to me, I left one minor request.

@@ -77,7 +77,7 @@ protected void masterOperation(final PutIndexTemplateRequest request, final Clus
}
final Settings.Builder templateSettingsBuilder = Settings.builder();
templateSettingsBuilder.put(request.settings()).normalizePrefix(IndexMetaData.INDEX_SETTING_PREFIX);
indexScopedSettings.validate(templateSettingsBuilder);
indexScopedSettings.validate(templateSettingsBuilder.build(), true); // templates must be consistent with reg. to dependencies
Copy link
Member

Choose a reason for hiding this comment

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

Can you expand reg.? It took me a few seconds to figure out what it meant (regards) and I do not want to have to do it every time I read this comment?

@@ -41,41 +41,41 @@
import java.util.Set;

public final class AzureStorageSettings {
// prefix for azure client settings
private static final String PREFIX = "azure.client.";
Copy link
Member

Choose a reason for hiding this comment

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

Why remove the internal constant? Seems like this is just asking for a typo in the future in one setting to cause a storm of problems :/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fair enough. I can add it back.

@s1monw s1monw merged commit 2299c70 into elastic:master Nov 13, 2017
@s1monw s1monw deleted the settings_dependencies branch November 13, 2017 11:06
s1monw added a commit that referenced this pull request Nov 13, 2017
We use affix settings to group settings / values under a certain namespace.
In some cases like login information for instance a setting is only valid if
one or more other settings are present. For instance `x.test.user` is only valid
if there is an `x.test.passwd` present and vice versa. This change allows to specify
such a dependency to prevent settings updates that leave settings in an inconsistent
state.
martijnvg added a commit that referenced this pull request Nov 14, 2017
* es/master: (24 commits)
  Reduce synchronization on field data cache
  add json-processor support for non-map json types (#27335)
  Properly format IndexGraveyard deletion date as date (#27362)
  Upgrade AWS SDK Jackson Databind to 2.6.7.1
  Stop responding to ping requests before master abdication (#27329)
  [Test] Fix POI version in packaging tests
  Allow affix settings to specify dependencies (#27161)
  Tests: Improve size regex in documentation test (#26879)
  reword comment
  Remove unnecessary logger creation for doc values field data
  [Geo] Decouple geojson parse logic from ShapeBuilders
  [DOCS] Fixed link to docker content
  Plugins: Add versionless alias to all security policy codebase properties (#26756)
  [Test] #27342 Fix SearchRequests#testValidate
  [DOCS] Move X-Pack-specific Docker content (#27333)
  Fail queries with scroll that explicitely set request_cache (#27342)
  [Test] Fix S3BlobStoreContainerTests.testNumberOfMultiparts()
  Set minimum_master_nodes to all nodes for REST tests (#27344)
  [Tests] Relax allowed delta in extended_stats aggregation (#27171)
  Remove S3 output stream (#27280)
  ...
martijnvg added a commit that referenced this pull request Nov 14, 2017
* 6.x: (27 commits)
  Reduce synchronization on field data cache
  [Test] #27342 Fix SearchRequests#testValidate
  Fail queries with scroll that explicitely set request_cache (#27342)
  add json-processor support for non-map json types (#27335)
  Upgrade AWS SDK Jackson Databind to 2.6.7.1
  Properly format IndexGraveyard deletion date as date (#27362)
  Stop responding to ping requests before master abdication (#27329)
  [Test] Fix POI version in packaging tests
  Added release notes for 6.0.0
  Update 6.0.0-beta1.asciidoc
  Allow affix settings to specify dependencies (#27161)
  Tests: Improve size regex in documentation test (#26879)
  reword comment
  Ensure external refreshes will also refresh internal searcher to minimize segment creation (#27253)
  Remove unnecessary logger creation for doc values field data
  [DOCS] Fixed link to docker content
  Plugins: Add versionless alias to all security policy codebase properties (#26756)
  [DOCS] Move X-Pack-specific Docker content (#27333)
  [Test] Fix S3BlobStoreContainerTests.testNumberOfMultiparts()
  Set minimum_master_nodes to all nodes for REST tests (#27344)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants