/*
* Licensed to Crate under one or more contributor license agreements.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership. Crate licenses this file
* to you under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* However, if you have executed another commercial license agreement
* with Crate these terms will supersede the license and you may use the
* software solely pursuant to the terms of the relevant commercial
* agreement.
*/
package io.crate.metadata.settings;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import io.crate.breaker.CrateCircuitBreakerService;
import io.crate.cluster.gracefulstop.DecommissioningService;
import io.crate.metadata.ReferenceImplementation;
import io.crate.operation.collect.stats.JobsLogService;
import io.crate.operation.reference.NestedObjectExpression;
import io.crate.operation.udf.UserDefinedFunctionService;
import io.crate.planner.TableStatsService;
import io.crate.protocols.postgres.PostgresNetty;
import io.crate.settings.CrateSetting;
import io.crate.settings.SharedSettings;
import io.crate.types.DataTypes;
import io.crate.udc.service.UDCService;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.action.bulk.BulkShardProcessor;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterStateListener;
import org.elasticsearch.cluster.InternalClusterInfoService;
import org.elasticsearch.cluster.routing.allocation.DiskThresholdSettings;
import org.elasticsearch.cluster.routing.allocation.allocator.BalancedShardsAllocator;
import org.elasticsearch.cluster.routing.allocation.decider.*;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.discovery.DiscoverySettings;
import org.elasticsearch.discovery.zen.ElectMasterService;
import org.elasticsearch.discovery.zen.ZenDiscovery;
import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.index.store.IndexStoreConfig;
import org.elasticsearch.indices.breaker.HierarchyCircuitBreakerService;
import org.elasticsearch.indices.recovery.RecoverySettings;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class CrateSettings implements ClusterStateListener {
public static final List<CrateSetting> CRATE_CLUSTER_SETTINGS = Collections.unmodifiableList(
Arrays.asList(
// STATS
JobsLogService.STATS_ENABLED_SETTING,
JobsLogService.STATS_JOBS_LOG_SIZE_SETTING,
JobsLogService.STATS_JOBS_LOG_EXPIRATION_SETTING,
JobsLogService.STATS_OPERATIONS_LOG_SIZE_SETTING,
JobsLogService.STATS_OPERATIONS_LOG_EXPIRATION_SETTING,
TableStatsService.STATS_SERVICE_REFRESH_INTERVAL_SETTING,
CrateCircuitBreakerService.JOBS_LOG_CIRCUIT_BREAKER_LIMIT_SETTING,
CrateCircuitBreakerService.JOBS_LOG_CIRCUIT_BREAKER_OVERHEAD_SETTING,
CrateCircuitBreakerService.OPERATIONS_LOG_CIRCUIT_BREAKER_LIMIT_SETTING,
CrateCircuitBreakerService.OPERATIONS_LOG_CIRCUIT_BREAKER_OVERHEAD_SETTING,
// INDICES
CrateCircuitBreakerService.QUERY_CIRCUIT_BREAKER_LIMIT_SETTING,
CrateCircuitBreakerService.QUERY_CIRCUIT_BREAKER_OVERHEAD_SETTING,
// BULK
BulkShardProcessor.BULK_REQUEST_TIMEOUT_SETTING,
// GRACEFUL STOP
DecommissioningService.DECOMMISSION_INTERNAL_SETTING_GROUP,
DecommissioningService.GRACEFUL_STOP_MIN_AVAILABILITY_SETTING,
DecommissioningService.GRACEFUL_STOP_REALLOCATE_SETTING,
DecommissioningService.GRACEFUL_STOP_TIMEOUT_SETTING,
DecommissioningService.GRACEFUL_STOP_FORCE_SETTING,
// UDC
UDCService.UDC_ENABLED_SETTING,
UDCService.UDC_URL_SETTING,
UDCService.UDC_INITIAL_DELAY_SETTING,
UDCService.UDC_INTERVAL_SETTING,
// ENTERPRISE
SharedSettings.ENTERPRISE_LICENSE_SETTING,
SharedSettings.LICENSE_IDENT_SETTING
));
private static final List<CrateSetting> EXPOSED_ES_SETTINGS = Collections.unmodifiableList(
Arrays.asList(
// CLUSTER
CrateSetting.of(InternalClusterInfoService.INTERNAL_CLUSTER_INFO_UPDATE_INTERVAL_SETTING, DataTypes.STRING),
// CLUSTER ROUTING
CrateSetting.of(EnableAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ENABLE_SETTING, DataTypes.STRING),
CrateSetting.of(ClusterRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_ALLOW_REBALANCE_SETTING, DataTypes.STRING),
CrateSetting.of(ConcurrentRebalanceAllocationDecider.CLUSTER_ROUTING_ALLOCATION_CLUSTER_CONCURRENT_REBALANCE_SETTING, DataTypes.INTEGER),
CrateSetting.of(ThrottlingAllocationDecider.CLUSTER_ROUTING_ALLOCATION_NODE_INITIAL_PRIMARIES_RECOVERIES_SETTING, DataTypes.INTEGER),
CrateSetting.of(ThrottlingAllocationDecider.CLUSTER_ROUTING_ALLOCATION_NODE_CONCURRENT_RECOVERIES_SETTING, DataTypes.INTEGER),
CrateSetting.of(Setting.simpleString(
FilterAllocationDecider.CLUSTER_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "_ip",
Setting.Property.NodeScope, Setting.Property.Dynamic), DataTypes.STRING),
CrateSetting.of(Setting.simpleString(
FilterAllocationDecider.CLUSTER_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "_id",
Setting.Property.NodeScope, Setting.Property.Dynamic), DataTypes.STRING),
CrateSetting.of(Setting.simpleString(
FilterAllocationDecider.CLUSTER_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "_host",
Setting.Property.NodeScope, Setting.Property.Dynamic), DataTypes.STRING),
CrateSetting.of(Setting.simpleString(
FilterAllocationDecider.CLUSTER_ROUTING_INCLUDE_GROUP_SETTING.getKey() + "_name",
Setting.Property.NodeScope, Setting.Property.Dynamic), DataTypes.STRING),
CrateSetting.of(Setting.simpleString(
FilterAllocationDecider.CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "_ip",
Setting.Property.NodeScope, Setting.Property.Dynamic), DataTypes.STRING),
CrateSetting.of(Setting.simpleString(
FilterAllocationDecider.CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "_id",
Setting.Property.NodeScope, Setting.Property.Dynamic), DataTypes.STRING),
CrateSetting.of(Setting.simpleString(
FilterAllocationDecider.CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "_host",
Setting.Property.NodeScope, Setting.Property.Dynamic), DataTypes.STRING),
CrateSetting.of(Setting.simpleString(
FilterAllocationDecider.CLUSTER_ROUTING_EXCLUDE_GROUP_SETTING.getKey() + "_name",
Setting.Property.NodeScope, Setting.Property.Dynamic), DataTypes.STRING),
CrateSetting.of(Setting.simpleString(
FilterAllocationDecider.CLUSTER_ROUTING_REQUIRE_GROUP_SETTING.getKey() + "_ip",
Setting.Property.NodeScope, Setting.Property.Dynamic), DataTypes.STRING),
CrateSetting.of(Setting.simpleString(
FilterAllocationDecider.CLUSTER_ROUTING_REQUIRE_GROUP_SETTING.getKey() + "_id",
Setting.Property.NodeScope, Setting.Property.Dynamic), DataTypes.STRING),
CrateSetting.of(Setting.simpleString(
FilterAllocationDecider.CLUSTER_ROUTING_REQUIRE_GROUP_SETTING.getKey() + "_host",
Setting.Property.NodeScope, Setting.Property.Dynamic), DataTypes.STRING),
CrateSetting.of(Setting.simpleString(
FilterAllocationDecider.CLUSTER_ROUTING_REQUIRE_GROUP_SETTING.getKey() + "_name",
Setting.Property.NodeScope, Setting.Property.Dynamic), DataTypes.STRING),
CrateSetting.of(BalancedShardsAllocator.SHARD_BALANCE_FACTOR_SETTING, DataTypes.FLOAT),
CrateSetting.of(BalancedShardsAllocator.INDEX_BALANCE_FACTOR_SETTING, DataTypes.FLOAT),
CrateSetting.of(BalancedShardsAllocator.THRESHOLD_SETTING, DataTypes.FLOAT),
CrateSetting.of(DiskThresholdSettings.CLUSTER_ROUTING_ALLOCATION_DISK_THRESHOLD_ENABLED_SETTING, DataTypes.BOOLEAN),
CrateSetting.of(DiskThresholdSettings.CLUSTER_ROUTING_ALLOCATION_LOW_DISK_WATERMARK_SETTING, DataTypes.STRING),
CrateSetting.of(DiskThresholdSettings.CLUSTER_ROUTING_ALLOCATION_HIGH_DISK_WATERMARK_SETTING, DataTypes.STRING),
// DISCOVERY
CrateSetting.of(ElectMasterService.DISCOVERY_ZEN_MINIMUM_MASTER_NODES_SETTING, DataTypes.INTEGER),
CrateSetting.of(ZenDiscovery.PING_TIMEOUT_SETTING, DataTypes.STRING),
CrateSetting.of(DiscoverySettings.PUBLISH_TIMEOUT_SETTING, DataTypes.STRING),
// GATEWAY
CrateSetting.of(GatewayService.RECOVER_AFTER_NODES_SETTING, DataTypes.INTEGER),
CrateSetting.of(GatewayService.RECOVER_AFTER_TIME_SETTING, DataTypes.STRING),
CrateSetting.of(GatewayService.EXPECTED_NODES_SETTING, DataTypes.INTEGER),
// INDICES
CrateSetting.of(RecoverySettings.INDICES_RECOVERY_MAX_BYTES_PER_SEC_SETTING, DataTypes.STRING),
CrateSetting.of(RecoverySettings.INDICES_RECOVERY_RETRY_DELAY_STATE_SYNC_SETTING, DataTypes.STRING),
CrateSetting.of(RecoverySettings.INDICES_RECOVERY_RETRY_DELAY_NETWORK_SETTING, DataTypes.STRING),
CrateSetting.of(RecoverySettings.INDICES_RECOVERY_INTERNAL_ACTION_TIMEOUT_SETTING, DataTypes.STRING),
CrateSetting.of(RecoverySettings.INDICES_RECOVERY_INTERNAL_LONG_ACTION_TIMEOUT_SETTING, DataTypes.STRING),
CrateSetting.of(RecoverySettings.INDICES_RECOVERY_ACTIVITY_TIMEOUT_SETTING, DataTypes.STRING),
CrateSetting.of(IndexStoreConfig.INDICES_STORE_THROTTLE_TYPE_SETTING, DataTypes.STRING),
CrateSetting.of(IndexStoreConfig.INDICES_STORE_THROTTLE_MAX_BYTES_PER_SEC_SETTING, DataTypes.STRING),
CrateSetting.of(HierarchyCircuitBreakerService.FIELDDATA_CIRCUIT_BREAKER_LIMIT_SETTING, DataTypes.STRING),
CrateSetting.of(HierarchyCircuitBreakerService.FIELDDATA_CIRCUIT_BREAKER_OVERHEAD_SETTING, DataTypes.DOUBLE),
CrateSetting.of(HierarchyCircuitBreakerService.REQUEST_CIRCUIT_BREAKER_LIMIT_SETTING, DataTypes.STRING),
CrateSetting.of(HierarchyCircuitBreakerService.REQUEST_CIRCUIT_BREAKER_OVERHEAD_SETTING, DataTypes.DOUBLE)
));
public static final List<CrateSetting> BUILT_IN_SETTINGS = Stream.concat(CRATE_CLUSTER_SETTINGS.stream(), EXPOSED_ES_SETTINGS.stream())
.filter(cs -> cs.getKey().startsWith("crate.internal.") == false) // don't expose internal settings
.collect(Collectors.toList());
private static final List<String> BUILT_IN_SETTING_NAMES = BUILT_IN_SETTINGS.stream()
.map(CrateSetting::getKey)
.collect(Collectors.toList());
private static final Map<String, String> BUILT_IN_SETTINGS_DEFAULTS_MAP = BUILT_IN_SETTINGS.stream()
.collect(Collectors.toMap(CrateSetting::getKey, s -> s.setting().getDefaultRaw(Settings.EMPTY)));
private static final Joiner DOT_JOINER = Joiner.on(".");
public static boolean isValidSetting(String name) {
return isLoggingSetting(name) ||
BUILT_IN_SETTING_NAMES.contains(name) ||
BUILT_IN_SETTING_NAMES.stream().filter(s -> s.startsWith(name + "."))
.collect(Collectors.toList()).isEmpty() == false;
}
public static List<String> settingNamesByPrefix(String prefix) {
if (isLoggingSetting(prefix)) {
return Collections.singletonList(prefix);
}
List<String> filteredList = new ArrayList<>();
for (String key : BUILT_IN_SETTING_NAMES) {
if (key.startsWith(prefix)) {
filteredList.add(key);
}
}
return filteredList;
}
public static void checkIfRuntimeSetting(String name) {
for (CrateSetting<?> crateSetting : BUILT_IN_SETTINGS) {
Setting<?> setting = crateSetting.setting();
if (setting.getKey().equals(name) && setting.isDynamic() == false) {
throw new UnsupportedOperationException(String.format(Locale.ENGLISH,
"Setting '%s' cannot be set/reset at runtime", name));
}
}
}
public static void flattenSettings(Settings.Builder settingsBuilder,
String key,
Object value) {
if (value instanceof Map) {
for (Map.Entry<String, Object> setting : ((Map<String, Object>) value).entrySet()) {
flattenSettings(settingsBuilder, DOT_JOINER.join(key, setting.getKey()), setting.getValue());
}
} else {
if (value instanceof BytesRef) {
value = BytesRefs.toString(value);
}
settingsBuilder.put(key, value);
}
}
private static boolean isLoggingSetting(String name) {
return name.startsWith("logger.");
}
private final Logger logger;
private final Settings initialSettings;
private final Map<String, ReferenceImplementation> referenceImplementationTree;
private volatile Settings settings;
@Inject
public CrateSettings(ClusterService clusterService, Settings settings) {
logger = Loggers.getLogger(this.getClass(), settings);
initialSettings = Settings.builder()
.put(BUILT_IN_SETTINGS_DEFAULTS_MAP)
.put(settings)
.build();
this.settings = initialSettings;
referenceImplementationTree = buildReferenceTree();
clusterService.add(this);
}
@Override
public void clusterChanged(ClusterChangedEvent event) {
try {
// nothing to do until we actually recover from the gateway or any other block indicates we need to disable persistency
if (event.state().blocks().disableStatePersistence() == false && event.metaDataChanged()) {
Settings incomingSetting = event.state().metaData().settings();
settings = Settings.builder().put(initialSettings).put(incomingSetting).build();
}
} catch (Exception ex) {
logger.warn("failed to apply cluster settings", ex);
}
}
Settings settings() {
return settings;
}
public Map<String, ReferenceImplementation> referenceImplementationTree() {
return referenceImplementationTree;
}
private Map<String, ReferenceImplementation> buildReferenceTree() {
Map<String, ReferenceImplementation> referenceMap = new HashMap<>(BUILT_IN_SETTINGS.size());
for (CrateSetting crateSetting : BUILT_IN_SETTINGS) {
if (crateSetting.isGroupSetting()) {
Map<String, Settings> settingsMap = initialSettings.getGroups(crateSetting.getKey(), true);
for (Map.Entry<String, Settings> entry : settingsMap.entrySet()) {
buildGroupSettingReferenceTree(crateSetting.getKey(), entry.getKey(), entry.getValue(),
referenceMap);
}
}
buildReferenceTree(referenceMap, crateSetting);
}
return referenceMap;
}
@VisibleForTesting
void buildGroupSettingReferenceTree(String prefix,
String settingKey,
Settings settingValue,
Map<String, ReferenceImplementation> referenceMap) {
Map<String, String> settingsMap = settingValue.getAsMap();
//this is a nested setting
if (!settingsMap.isEmpty()) {
//we need to build the reference tree for the current setting
buildReferenceTree(referenceMap,
CrateSetting.of(Setting.groupSetting(prefix + settingKey + ".",
Setting.Property.NodeScope),
DataTypes.OBJECT));
//build the reference tree for every child setting
for (Map.Entry<String, String> entry : settingsMap.entrySet()) {
String nestedPrefix = prefix + settingKey + "." + entry.getKey();
buildReferenceTree(referenceMap,
CrateSetting.of(Setting.simpleString(nestedPrefix,
Setting.Property.NodeScope),
DataTypes.STRING));
}
}
}
private void buildReferenceTree(Map<String, ReferenceImplementation> referenceMap, CrateSetting<?> crateSetting) {
String fullName = crateSetting.setting().getKey();
List<String> parts = crateSetting.path();
int numParts = parts.size();
String name = parts.get(numParts - 1);
if (numParts == 1) {
// top level setting
referenceMap.put(fullName, new SettingExpression(this, crateSetting, fullName));
} else {
ReferenceImplementation referenceImplementation = new SettingExpression(this, crateSetting, name);
String topLevelName = parts.get(0);
NestedSettingExpression topLevelImpl = (NestedSettingExpression) referenceMap.get(topLevelName);
if (topLevelImpl == null) {
topLevelImpl = new NestedSettingExpression();
referenceMap.put(topLevelName, topLevelImpl);
}
// group settings have empty name, parent is created above
if (numParts == 2 && name.isEmpty() == false) {
topLevelImpl.putChildImplementation(name, referenceImplementation);
} else {
// find parent impl
NestedSettingExpression parentImpl = topLevelImpl;
for (int i = 1; i < numParts - 1; i++) {
String currentName = parts.get(i);
NestedSettingExpression current = (NestedSettingExpression) parentImpl.childImplementations().get(currentName);
if (current == null) {
current = new NestedSettingExpression();
parentImpl.putChildImplementation(currentName, current);
}
parentImpl = current;
}
// group settings have empty name, parents are created above
if (name.isEmpty() == false) {
parentImpl.putChildImplementation(name, referenceImplementation);
}
}
}
}
static class SettingExpression implements ReferenceImplementation<Object> {
private final CrateSettings crateSettings;
private final CrateSetting<?> crateSetting;
private final String name;
SettingExpression(CrateSettings crateSettings, CrateSetting<?> crateSetting, String name) {
this.crateSettings = crateSettings;
this.crateSetting = crateSetting;
this.name = name;
}
public String name() {
return name;
}
@Override
public Object value() {
return crateSetting.dataType().value(crateSetting.setting().get(crateSettings.settings()));
}
}
@VisibleForTesting
static class NestedSettingExpression extends NestedObjectExpression {
void putChildImplementation(String name, ReferenceImplementation settingExpression) {
childImplementations.put(name, settingExpression);
}
public Map<String, ReferenceImplementation> childImplementations() {
return childImplementations;
}
}
}