com.yahoo.vespa.hosted.provision.autoscale.ResourceChange Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of node-repository Show documentation
Show all versions of node-repository Show documentation
Keeps track of node assignment in a multi-application setup.
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.autoscale;
import com.yahoo.config.provision.ClusterSpec;
import com.yahoo.config.provision.NodeResources;
import java.time.Duration;
/**
* A resource change.
*
* @author bratseth
*/
public class ResourceChange {
private final AllocatableResources from, to;
private final ClusterModel model;
public ResourceChange(ClusterModel model, AllocatableResources to) {
this.from = model.current();
this.to = to;
this.model = model;
}
/** Returns the estimated total cost of this resource change (coming in addition to the "to" resource cost). */
public double cost() {
if (model.isContent()) {
if (requiresNodeReplacement()) return toHours(model.redistributionDuration()) * from.cost();
return toHours(model.redistributionDuration()) * from.advertisedResources().cost() * nodesToRetire();
}
else {
if (requiresNodeReplacement()) return toHours(model.nodeReplacementDuration()) * from.cost();
return 0;
}
}
/**
* Returns the estimated number of nodes that will be retired by this change,
* given that it is a content cluster and no node replacement is necessary.
* This is not necessarily always perfectly correct if this changes group layout.
*/
private int nodesToRetire() {
return Math.max(0, from.nodes() - to.nodes());
}
/** Returns true if the *existing* nodes of this needs to be replaced in this change. */
private boolean requiresNodeReplacement() {
var fromNodes = from.advertisedResources().nodeResources();
var toNodes = to.advertisedResources().nodeResources();
if (model.isExclusive()) {
return ! fromNodes.equals(toNodes);
}
else {
if ( ! fromNodes.justNonNumbers().equalsWhereSpecified(toNodes.justNonNumbers())) return true;
if ( ! canInPlaceResize()) return true;
return false;
}
}
private double toHours(Duration duration) {
return duration.toMillis() / 3600000.0;
}
private boolean canInPlaceResize() {
return canInPlaceResize(from.nodes(), from.advertisedResources().nodeResources(),
to.nodes(), to.advertisedResources().nodeResources(),
model.clusterSpec().type(), model.isExclusive(), from.groups() != to.groups());
}
public static boolean canInPlaceResize(int fromCount, NodeResources fromResources,
int toCount, NodeResources toResources,
ClusterSpec.Type type, boolean exclusive, boolean hasTopologyChange) {
if (exclusive) return false; // exclusive resources must match the host
// Never allow in-place resize when also changing topology or decreasing cluster size
if (hasTopologyChange || toCount < fromCount) return false;
// Do not allow increasing cluster size and decreasing node resources at the same time for content nodes
if (type.isContent() && toCount > fromCount && !toResources.satisfies(fromResources.justNumbers()))
return false;
return true;
}
}