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

Fix setting notification for complex setting (affixMap settings) that could cause transient settings to be ignored #28317

Merged
merged 4 commits into from
Jan 22, 2018

Conversation

dakrone
Copy link
Member

@dakrone dakrone commented Jan 19, 2018

Previously if an affixMap setting was registered, and then a completely
different setting was applied, the affixMap update consumer would be notified
with an empty map. This caused settings that were previously set to be unset in
local state in a consumer that assumed it would only be called when the affixMap
setting was changed.

This commit changes the behavior so if a prefix foo. is registered, any
setting under the prefix will have the update consumer notified if there are
changes starting with foo..

Resolves #28316

Previously if an affixMap setting was registered, and then a completely
different setting was applied, the affixMap update consumer would be notified
with an empty map. This caused settings that were previously set to be unset in
local state in a consumer that assumed it would only be called when the affixMap
setting was changed.

This commit changes the behavior so if a prefix `foo.` is registered, any
setting under the prefix will have the update consumer notified if there are
changes starting with `foo.`.

Resolves elastic#28316
@dakrone dakrone added :Core/Infra/Settings Settings infrastructure and APIs v7.0.0 v6.2.0 v6.1.3 labels Jan 19, 2018
@dakrone dakrone requested a review from s1monw January 19, 2018 23:32
@dakrone
Copy link
Member Author

dakrone commented Jan 19, 2018

There might be a better way to solve this, which is why I am marking this as "discuss"

@s1monw
Copy link
Contributor

s1monw commented Jan 20, 2018

@dakrone it's unclear to me what you are fixing here. is it possible to have a simple unittest that fails previously and passes now?

@dakrone
Copy link
Member Author

dakrone commented Jan 20, 2018 via email

@s1monw
Copy link
Contributor

s1monw commented Jan 22, 2018

++ @dakrone

@colings86 colings86 added v6.3.0 and removed v6.2.0 labels Jan 22, 2018
@s1monw s1monw added the blocker label Jan 22, 2018
@jpountz
Copy link
Contributor

jpountz commented Jan 22, 2018

This should be backported to 6.2, it's confusing to have a fix in the 6.1 and 6.3 series but not 6.2?

@s1monw
Copy link
Contributor

s1monw commented Jan 22, 2018

absolutely this should go into 6.2

@s1monw s1monw added v6.2.0 and removed v6.3.0 labels Jan 22, 2018
@s1monw
Copy link
Contributor

s1monw commented Jan 22, 2018

@dakrone I looked at it to make sure I understand what's going on. I suggest this patch based on your changes:

diff --git a/server/src/main/java/org/elasticsearch/common/settings/Setting.java b/server/src/main/java/org/elasticsearch/common/settings/Setting.java
index 4ecb8b1442..fd91a8a760 100644
--- a/server/src/main/java/org/elasticsearch/common/settings/Setting.java
+++ b/server/src/main/java/org/elasticsearch/common/settings/Setting.java
@@ -597,21 +597,7 @@ public class Setting<T> implements ToXContentObject {
 
                 @Override
                 public boolean hasChanged(Settings current, Settings previous) {
-                    return Stream.concat(matchStream(current), matchStream(previous)).distinct().anyMatch(aKey -> {
-                        String namespace = key.getNamespace(aKey);
-                        Setting<T> concreteSetting = getConcreteSetting(aKey);
-                        AbstractScopedSettings.SettingUpdater<T> updater =
-                            concreteSetting.newUpdater((v) -> {}, logger, (v) -> validator.accept(namespace, v));
-                        T value = updater.getValue(current, previous);
-                        if (updater.hasChanged(current, previous)) {
-                            // only the ones that have changed otherwise we might get too many updates
-                            // the hasChanged above checks only if there are any changes
-                            if ((omitDefaults && value.equals(concreteSetting.getDefault(current))) == false) {
-                                return true;
-                            }
-                        }
-                        return false;
-                    });
+                    return current.filter(k -> match(k)).equals(previous.filter(k -> match(k))) == false;
                 }
 
                 @Override
@@ -623,8 +609,14 @@ public class Setting<T> implements ToXContentObject {
                         Setting<T> concreteSetting = getConcreteSetting(aKey);
                         AbstractScopedSettings.SettingUpdater<T> updater =
                             concreteSetting.newUpdater((v) -> {}, logger, (v) -> validator.accept(namespace, v));
-                        T value = updater.getValue(current, previous);
-                        result.put(namespace, value);
+                        if (updater.hasChanged(current, previous)) {
+                            // only the ones that have changed otherwise we might get too many updates
+                            // the hasChanged above checks only if there are any changes
+                            T value = updater.getValue(current, previous);
+                            if ((omitDefaults && value.equals(concreteSetting.getDefault(current))) == false) {
+                                result.put(namespace, value);
+                            }
+                        }
                     });
                     return result;
                 }
diff --git a/server/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java b/server/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java
index 298a82a138..fba760bc60 100644
--- a/server/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java
+++ b/server/src/test/java/org/elasticsearch/common/settings/ScopedSettingsTests.java
@@ -226,6 +226,27 @@ public class ScopedSettingsTests extends ESTestCase {
         assertEquals(1, intResults.size());
     }
 
+    public void testPrefixKeySetting() {
+        Setting.AffixSetting<String> setting = Setting.prefixKeySetting("foo.",
+            (k) ->  Setting.simpleString(k, Property.Dynamic, Property.NodeScope));
+        Setting<String> stringSetting = Setting.simpleString("test.bar", Property.Dynamic, Property.NodeScope);
+        AbstractScopedSettings service = new ClusterSettings(Settings.EMPTY,new HashSet<>(Arrays.asList(setting, stringSetting)));
+        Map<String, String> consumerMap = new HashMap<>();
+        service.addAffixMapUpdateConsumer(setting, (m) -> {
+            consumerMap.clear();
+            consumerMap.putAll(m);
+        }, (s, k) -> {}, false);
+        service.applySettings(Settings.builder().put("foo.bar", "1").put("foo.baz", "2").build());
+        assertEquals(2, consumerMap.size());
+        assertEquals("1", consumerMap.get("bar"));
+        assertEquals("2", consumerMap.get("baz"));
+        service.applySettings(Settings.builder().put("foo.bar", "1").put("foo.baz", "2").put("test.bar", "foo").build());
+
+        assertEquals(2, consumerMap.size());
+        assertEquals("1", consumerMap.get("bar"));
+        assertEquals("2", consumerMap.get("baz"));
+    }
+
     public void testAddConsumerAffixMap() {
         Setting.AffixSetting<Integer> intSetting = Setting.affixKeySetting("foo.", "bar",
             (k) ->  Setting.intSetting(k, 1, Property.Dynamic, Property.NodeScope));
@@ -261,6 +282,21 @@ public class ScopedSettingsTests extends ESTestCase {
         assertEquals(2, listResults.size());
         assertEquals(2, intResults.size());
 
+        service.applySettings(Settings.builder()
+            .put("foo.test.bar", 2)
+            .put("foo.test_1.bar", 7)
+            .putList("foo.test_list.list", "16", "17")
+            .putList("foo.test_list_1.list", "18", "19", "20")
+            .build());
+
+        assertEquals(2, intResults.get("test").intValue());
+        assertEquals(7, intResults.get("test_1").intValue());
+        assertEquals(Arrays.asList(16, 17), listResults.get("test_list"));
+        assertEquals(Arrays.asList(18, 19, 20), listResults.get("test_list_1"));
+        assertEquals(2, listResults.size());
+        assertEquals(2, intResults.size());
+
+
         listResults.clear();
         intResults.clear();
 
@@ -270,19 +306,18 @@ public class ScopedSettingsTests extends ESTestCase {
             .putList("foo.test_list.list", "16", "17")
             .putNull("foo.test_list_1.list")
             .build());
-        // TODO: should this assertion be kept?
-        // assertNull("test wasn't changed", intResults.get("test"));
+        assertNull("test wasn't changed", intResults.get("test"));
         assertEquals(8, intResults.get("test_1").intValue());
-        // assertNull("test_list wasn't changed", listResults.get("test_list"));
+        assertNull("test_list wasn't changed", listResults.get("test_list"));
         if (omitDefaults) {
             assertNull(listResults.get("test_list_1"));
             assertFalse(listResults.containsKey("test_list_1"));
             assertEquals(0, listResults.size());
-            assertEquals(2, intResults.size());
+            assertEquals(1, intResults.size());
         } else {
             assertEquals(Arrays.asList(1), listResults.get("test_list_1")); // reset to default
-            assertEquals(2, listResults.size());
-            assertEquals(2, intResults.size());
+            assertEquals(1, listResults.size());
+            assertEquals(1, intResults.size());
         }
 
     }

@dakrone
Copy link
Member Author

dakrone commented Jan 22, 2018

@s1monw whoops I didn't see that before I started working on mine, I added a unit test that demonstrates this

@dakrone
Copy link
Member Author

dakrone commented Jan 22, 2018

I will apply your comments, thanks Simon

@dakrone
Copy link
Member Author

dakrone commented Jan 22, 2018

@s1monw thanks for the suggested patch, can you take another look please?

@dakrone dakrone added review and removed discuss labels Jan 22, 2018
Copy link
Contributor

@s1monw s1monw left a comment

Choose a reason for hiding this comment

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

LGTM

@@ -156,5 +161,58 @@ public void testInvalidIPFilterClusterSettings() {
.execute().actionGet());
assertEquals("invalid IP address [192.168.1.1.] for [" + filterSetting.getKey() + ipKey + "]", e.getMessage());
}

public void testTransientSettingsStillApplied() throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think we should test the patch for persistent settings as well, or a mix of the two? We observed that issue effects persistent and transient settings.

Copy link
Member Author

Choose a reason for hiding this comment

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

@jhalterman the unit test for this is outside the scope of persistent or transient (the fix is to the settings themselves, it actually doesn't matter whether the settings are transient or persistent), so I don't think a test for that is necessary.

@dakrone dakrone merged commit ba5b583 into elastic:master Jan 22, 2018
dakrone added a commit that referenced this pull request Jan 22, 2018
…#28317)

* Notify affixMap settings when any under the registered prefix matches

Previously if an affixMap setting was registered, and then a completely
different setting was applied, the affixMap update consumer would be notified
with an empty map. This caused settings that were previously set to be unset in
local state in a consumer that assumed it would only be called when the affixMap
setting was changed.

This commit changes the behavior so if a prefix `foo.` is registered, any
setting under the prefix will have the update consumer notified if there are
changes starting with `foo.`.

Resolves #28316

* Add unit test

* Address feedback
dakrone added a commit that referenced this pull request Jan 22, 2018
…#28317)

* Notify affixMap settings when any under the registered prefix matches

Previously if an affixMap setting was registered, and then a completely
different setting was applied, the affixMap update consumer would be notified
with an empty map. This caused settings that were previously set to be unset in
local state in a consumer that assumed it would only be called when the affixMap
setting was changed.

This commit changes the behavior so if a prefix `foo.` is registered, any
setting under the prefix will have the update consumer notified if there are
changes starting with `foo.`.

Resolves #28316

* Add unit test

* Address feedback
dakrone added a commit that referenced this pull request Jan 22, 2018
…#28317)

* Notify affixMap settings when any under the registered prefix matches

Previously if an affixMap setting was registered, and then a completely
different setting was applied, the affixMap update consumer would be notified
with an empty map. This caused settings that were previously set to be unset in
local state in a consumer that assumed it would only be called when the affixMap
setting was changed.

This commit changes the behavior so if a prefix `foo.` is registered, any
setting under the prefix will have the update consumer notified if there are
changes starting with `foo.`.

Resolves #28316

* Add unit test

* Address feedback
martijnvg added a commit that referenced this pull request Jan 24, 2018
* es/master:
  Remove redundant argument for buildConfiguration of s3 plugin (#28281)
  Completely remove Painless Type from AnalyzerCaster in favor of Java Class. (#28329)
  Fix spelling error
  Reindex: Wait for deletion in test
  Reindex: log more on rare test failure
  Ensure we protect Collections obtained from scripts from self-referencing (#28335)
  [Docs] Fix asciidoc style in composite agg docs
  Adds the ability to specify a format on composite date_histogram source (#28310)
  Provide a better error message for the case when all shards failed (#28333)
  [Test] Re-Add integer_range and date_range field types for query builder tests (#28171)
  Added Put Mapping API to high-level Rest client (#27869)
  Revert change that does not return all indices if a specific alias is requested via get alias api. (#28294)
  Painless: Replace Painless Type with Java Class during Casts (#27847)
  Notify affixMap settings when any under the registered prefix matches (#28317)
martijnvg added a commit that referenced this pull request Jan 24, 2018
* es/6.x:
  Remove redundant argument for buildConfiguration of s3 plugin (#28281)
  Completely remove Painless Type from AnalyzerCaster in favor of Java Class. (#28329)
  Fix spelling error
  Reindex: Wait for deletion in test
  Reindex: log more on rare test failure
  Ensure we protect Collections obtained from scripts from self-referencing (#28335)
  Provide a better error message for the case when all shards failed (#28333)
  REST high-level client: add support for update_all_types option to put mapping API
  Added Put Mapping API to high-level Rest client (#27869)
  [Test] Re-Add integer_range and date_range field types for query builder tests (#28171)
  Revert change that does not return all indices if a specific alias is requested via get alias api. (#28294)
  Painless: Replace Painless Type with Java Class during Casts (#27847)
  Notify affixMap settings when any under the registered prefix matches (#28317)
jasontedor added a commit to matarrese/elasticsearch that referenced this pull request Jan 24, 2018
* master: (94 commits)
  Completely remove Painless Type from AnalyzerCaster in favor of Java Class. (elastic#28329)
  Fix spelling error
  Reindex: Wait for deletion in test
  Reindex: log more on rare test failure
  Ensure we protect Collections obtained from scripts from self-referencing (elastic#28335)
  [Docs] Fix asciidoc style in composite agg docs
  Adds the ability to specify a format on composite date_histogram source (elastic#28310)
  Provide a better error message for the case when all shards failed (elastic#28333)
  [Test] Re-Add integer_range and date_range field types for query builder tests (elastic#28171)
  Added Put Mapping API to high-level Rest client (elastic#27869)
  Revert change that does not return all indices if a specific alias is requested via get alias api. (elastic#28294)
  Painless: Replace Painless Type with Java Class during Casts (elastic#27847)
  Notify affixMap settings when any under the registered prefix matches (elastic#28317)
  Trim down usages of `ShardOperationFailedException` interface (elastic#28312)
  Do not return all indices if a specific alias is requested via get aliases api.
  [Test] Lower bwc version for rank-eval rest tests
  CountedBitSet doesn't need to extend BitSet. (elastic#28239)
  Calculate sum in Kahan summation algorithm in aggregations (elastic#27807) (elastic#27848)
  Remove the `update_all_types` option. (elastic#28288)
  Add information when master node left to DiscoveryNodes' shortSummary() (elastic#28197)
  ...
@bleskes bleskes changed the title Notify affixMap settings when any under the registered prefix matches Fix settting notificaiton for complex setting (affixMap settings) that could cause transient settings to be ignored Jan 29, 2018
@clintongormley clintongormley changed the title Fix settting notificaiton for complex setting (affixMap settings) that could cause transient settings to be ignored Fix setting notification for complex setting (affixMap settings) that could cause transient settings to be ignored Jan 30, 2018
@dakrone dakrone deleted the affix-setting-fix branch April 19, 2018 14:46
sebgl added a commit to sebgl/cloud-on-k8s that referenced this pull request Jun 26, 2019
We don't need this custom fix anymore, since it is fixed in ES v6.8+ and
ES v7.0+.

See elastic/elasticsearch#28317.
sebgl added a commit to elastic/cloud-on-k8s that referenced this pull request Jun 27, 2019
We don't need this custom fix anymore, since it is fixed in ES v6.8+ and
ES v7.0+.

See elastic/elasticsearch#28317.
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.

Setting persistent settings causes transient settings not to be applied
6 participants