/*
* 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.cluster.gracefulstop;
import com.google.common.collect.ImmutableSet;
import org.elasticsearch.cluster.routing.RoutingNode;
import org.elasticsearch.cluster.routing.ShardRouting;
import org.elasticsearch.cluster.routing.allocation.RoutingAllocation;
import org.elasticsearch.cluster.routing.allocation.decider.AllocationDecider;
import org.elasticsearch.cluster.routing.allocation.decider.Decision;
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.Settings;
import java.util.Map;
import java.util.Set;
public class DecommissionAllocationDecider extends AllocationDecider {
public static final String NAME = "decommission";
private Set<String> decommissioningNodes = ImmutableSet.of();
private DataAvailability dataAvailability;
public DecommissionAllocationDecider(Settings settings, ClusterSettings clusterSettings) {
super(settings);
updateDecommissioningNodes(DecommissioningService.DECOMMISSION_INTERNAL_SETTING_GROUP.setting().get(settings));
dataAvailability = DecommissioningService.GRACEFUL_STOP_MIN_AVAILABILITY_SETTING.setting().get(settings);
clusterSettings.addSettingsUpdateConsumer(
DecommissioningService.DECOMMISSION_INTERNAL_SETTING_GROUP.setting(), this::updateDecommissioningNodes);
clusterSettings.addSettingsUpdateConsumer(
DecommissioningService.GRACEFUL_STOP_MIN_AVAILABILITY_SETTING.setting(), this::updateMinAvailability);
}
private void updateDecommissioningNodes(Settings decommissionNodesSettings) {
Map<String, String> decommissionMap = decommissionNodesSettings.getAsMap();
decommissioningNodes = decommissionMap.keySet();
}
private void updateMinAvailability(DataAvailability availability) {
dataAvailability = availability;
}
@Override
public Decision canAllocate(ShardRouting shardRouting, RoutingNode node, RoutingAllocation allocation) {
if (decommissioningNodes.contains(node.nodeId())
&& dataAvailability == DataAvailability.PRIMARIES
&& !shardRouting.primary()) {
// if primaries are removed from this node it will try to re-balance non-primaries onto this node
// prevent this - replicas that are already here can remain, but no new replicas should be assigned
return allocation.decision(Decision.NO, NAME, "dataAvailability=primaries, shard=replica, decommissioned=true");
}
return canRemainOrAllocate(node.nodeId(), shardRouting, allocation);
}
@Override
public Decision canRemain(ShardRouting shardRouting, RoutingNode node, RoutingAllocation allocation) {
return canRemainOrAllocate(node.nodeId(), shardRouting, allocation);
}
private Decision canRemainOrAllocate(String nodeId, ShardRouting shardRouting, RoutingAllocation allocation) {
if (dataAvailability == DataAvailability.NONE) {
return allocation.decision(Decision.YES, NAME, "dataAvailability=none");
}
if (decommissioningNodes.contains(nodeId)) {
if (dataAvailability == DataAvailability.PRIMARIES && !shardRouting.primary()) {
return allocation.decision(Decision.YES, NAME, "dataAvailability=primaries shard=replica decommissioned=true");
}
return allocation.decision(Decision.NO, NAME, "node is being decommissioned");
}
return allocation.decision(Decision.YES, NAME, "node isn't decommissioned");
}
}