From 435bd33a52fcbf462aaf771f795e19e968353da7 Mon Sep 17 00:00:00 2001 From: Gordon Brown Date: Tue, 4 Sep 2018 16:14:18 -0600 Subject: [PATCH] Add user-defined cluster metadata (#33325) Adds a place for users to store cluster-wide data they wish to associate with the cluster via the Cluster Settings API. This is strictly for user-defined data, Elasticsearch makes no other other use of these settings. --- docs/reference/modules/cluster/misc.asciidoc | 21 +++++++++++++ .../cluster/service/ClusterService.java | 5 +++ .../common/settings/ClusterSettings.java | 1 + .../cluster/settings/ClusterSettingsIT.java | 31 +++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/docs/reference/modules/cluster/misc.asciidoc b/docs/reference/modules/cluster/misc.asciidoc index 9686749486826..bdc5699955384 100644 --- a/docs/reference/modules/cluster/misc.asciidoc +++ b/docs/reference/modules/cluster/misc.asciidoc @@ -22,6 +22,27 @@ user with access to the <> API can make the cluster read-write again. +[[user-defined-data]] +==== User Defined Cluster Metadata + +User-defined metadata can be stored and retrieved using the Cluster Settings API. +This can be used to store arbitrary, infrequently-changing data about the cluster +without the need to create an index to store it. This data may be stored using +any key prefixed with `cluster.metadata.`. For example, to store the email +address of the administrator of a cluster under the key `cluster.metadata.administrator`, +issue this request: + +[source,js] +------------------------------- +PUT /_cluster/settings +{ + "persistent": { + "cluster.metadata.administrator": "sysadmin@example.com" + } +} +------------------------------- +// CONSOLE + [[cluster-max-tombstones]] ==== Index Tombstones diff --git a/server/src/main/java/org/elasticsearch/cluster/service/ClusterService.java b/server/src/main/java/org/elasticsearch/cluster/service/ClusterService.java index fcd4cae96ec2a..f9af23b374c8d 100644 --- a/server/src/main/java/org/elasticsearch/cluster/service/ClusterService.java +++ b/server/src/main/java/org/elasticsearch/cluster/service/ClusterService.java @@ -52,6 +52,9 @@ public class ClusterService extends AbstractLifecycleComponent { Setting.positiveTimeSetting("cluster.service.slow_task_logging_threshold", TimeValue.timeValueSeconds(30), Property.Dynamic, Property.NodeScope); + public static final org.elasticsearch.common.settings.Setting.AffixSetting USER_DEFINED_META_DATA = + Setting.prefixKeySetting("cluster.metadata.", (key) -> Setting.simpleString(key, Property.Dynamic, Property.NodeScope)); + private final ClusterName clusterName; private final OperationRouting operationRouting; @@ -68,6 +71,8 @@ public ClusterService(Settings settings, ClusterSettings clusterSettings, Thread this.clusterName = ClusterName.CLUSTER_NAME_SETTING.get(settings); this.clusterSettings.addSettingsUpdateConsumer(CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING, this::setSlowTaskLoggingThreshold); + // Add a no-op update consumer so changes are logged + this.clusterSettings.addAffixUpdateConsumer(USER_DEFINED_META_DATA, (first, second) -> {}, (first, second) -> {}); this.initialClusterStateCustoms = initialClusterStateCustoms; this.clusterApplierService = new ClusterApplierService(settings, clusterSettings, threadPool, this::newClusterStateBuilder); } diff --git a/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java b/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java index f6fc020a6fdc3..eb47c9a28363e 100644 --- a/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java +++ b/server/src/main/java/org/elasticsearch/common/settings/ClusterSettings.java @@ -269,6 +269,7 @@ public void apply(Settings value, Settings current, Settings previous) { HierarchyCircuitBreakerService.ACCOUNTING_CIRCUIT_BREAKER_OVERHEAD_SETTING, IndexModule.NODE_STORE_ALLOW_MMAPFS, ClusterService.CLUSTER_SERVICE_SLOW_TASK_LOGGING_THRESHOLD_SETTING, + ClusterService.USER_DEFINED_META_DATA, SearchService.DEFAULT_SEARCH_TIMEOUT_SETTING, SearchService.DEFAULT_ALLOW_PARTIAL_SEARCH_RESULTS, ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING, diff --git a/server/src/test/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java b/server/src/test/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java index cdcaf4a1b9c20..b53d61280f72d 100644 --- a/server/src/test/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java +++ b/server/src/test/java/org/elasticsearch/cluster/settings/ClusterSettingsIT.java @@ -22,6 +22,7 @@ import org.apache.logging.log4j.Level; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsRequestBuilder; import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsResponse; +import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse; import org.elasticsearch.cluster.metadata.MetaData; import org.elasticsearch.cluster.routing.allocation.decider.EnableAllocationDecider; import org.elasticsearch.common.logging.ESLoggerFactory; @@ -380,4 +381,34 @@ public void testLoggerLevelUpdate() { } } + public void testUserMetadata() { + String key = "cluster.metadata." + randomAlphaOfLengthBetween(5, 20); + String value = randomRealisticUnicodeOfCodepointLengthBetween(5, 50); + String updatedValue = randomRealisticUnicodeOfCodepointLengthBetween(5, 50); + logger.info("Attempting to store [{}]: [{}], then update to [{}]", key, value, updatedValue); + + final Settings settings = Settings.builder().put(key, value).build(); + final Settings updatedSettings = Settings.builder().put(key, updatedValue).build(); + if (randomBoolean()) { + logger.info("Using persistent settings"); + + client().admin().cluster().prepareUpdateSettings().setPersistentSettings(settings).execute().actionGet(); + ClusterStateResponse state = client().admin().cluster().prepareState().execute().actionGet(); + assertEquals(value, state.getState().getMetaData().persistentSettings().get(key)); + + client().admin().cluster().prepareUpdateSettings().setPersistentSettings(updatedSettings).execute().actionGet(); + ClusterStateResponse updatedState = client().admin().cluster().prepareState().execute().actionGet(); + assertEquals(updatedValue, updatedState.getState().getMetaData().persistentSettings().get(key)); + } else { + logger.info("Using transient settings"); + client().admin().cluster().prepareUpdateSettings().setTransientSettings(settings).execute().actionGet(); + ClusterStateResponse state = client().admin().cluster().prepareState().execute().actionGet(); + assertEquals(value, state.getState().getMetaData().transientSettings().get(key)); + + client().admin().cluster().prepareUpdateSettings().setTransientSettings(updatedSettings).execute().actionGet(); + ClusterStateResponse updatedState = client().admin().cluster().prepareState().execute().actionGet(); + assertEquals(updatedValue, updatedState.getState().getMetaData().transientSettings().get(key)); + } + } + }