All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.spotify.autoscaler.algorithm.CPUAlgorithm Maven / Gradle / Ivy

/*-
 * -\-\-
 * bigtable-autoscaler
 * --
 * Copyright (C) 2018 Spotify AB
 * --
 * Licensed 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.
 * -/-/-
 */

package com.spotify.autoscaler.algorithm;

import com.spotify.autoscaler.ScalingEvent;
import com.spotify.autoscaler.client.StackdriverClient;
import com.spotify.autoscaler.db.BigtableCluster;
import com.spotify.autoscaler.db.ClusterResizeLogBuilder;
import com.spotify.autoscaler.metric.AutoscalerMetrics;
import com.spotify.autoscaler.metric.ClusterLoadGauges;
import java.time.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class CPUAlgorithm implements Algorithm {

  private static final double MAX_REDUCTION_RATIO = 0.7;
  private static final double CPU_OVERLOAD_THRESHOLD = 0.9;
  private static final Logger LOGGER = LoggerFactory.getLogger(CPUAlgorithm.class);
  private final StackdriverClient stackdriverClient;
  private final AutoscalerMetrics autoscalerMetrics;

  public CPUAlgorithm(
      final StackdriverClient stackdriverClient, final AutoscalerMetrics autoscalerMetrics) {
    this.stackdriverClient = stackdriverClient;
    this.autoscalerMetrics = autoscalerMetrics;
  }

  public ScalingEvent calculateWantedNodes(
      final BigtableCluster cluster,
      final ClusterResizeLogBuilder clusterResizeLogBuilder,
      final Duration samplingDuration,
      final int currentNodes) {
    return cpuStrategy(cluster, clusterResizeLogBuilder, samplingDuration, currentNodes);
  }

  private ScalingEvent cpuStrategy(
      final BigtableCluster cluster,
      final ClusterResizeLogBuilder clusterResizeLogBuilder,
      final Duration samplingDuration,
      final int nodes) {
    double currentCpu = 0d;

    try {
      currentCpu = getCurrentCpu(cluster, samplingDuration);
    } finally {
      autoscalerMetrics.registerClusterLoadMetrics(cluster, currentCpu, ClusterLoadGauges.CPU);
    }

    LOGGER.info(
        "Running autoscale job. Nodes: {} (min={}, max={}, minNodesOverride={}), CPU: {} (target={})",
        nodes,
        cluster.minNodes(),
        cluster.maxNodes(),
        cluster.minNodesOverride(),
        currentCpu,
        cluster.cpuTarget());

    final double initialDesiredNodes = currentCpu * nodes / cluster.cpuTarget();

    final double desiredNodes;
    final boolean scaleDown = (initialDesiredNodes < nodes);
    final String path;
    if ((currentCpu > CPU_OVERLOAD_THRESHOLD) && cluster.overloadStep().isPresent()) {
      // If cluster is overloaded and the overloadStep is set, bump by that amount
      path = "overload";
      desiredNodes = nodes + cluster.overloadStep().get();
    } else if (scaleDown) {
      // Don't reduce node count too quickly to avoid overload from tablet shuffling
      path = "throttled change";
      final double lowestAmountNodes = MAX_REDUCTION_RATIO * nodes;
      final double maxReduction = nodes - lowestAmountNodes;
      desiredNodes =
          maxReduction > 1
              ? Math.max(initialDesiredNodes, Math.ceil(lowestAmountNodes))
              : Math.max(initialDesiredNodes, Math.floor(lowestAmountNodes));
    } else {
      path = "normal";
      desiredNodes = initialDesiredNodes;
    }

    final int roundedDesiredNodes = (int) Math.ceil(desiredNodes);
    LOGGER.info(
        "Ideal node count: {}. Revised nodes: {}. Reason: {}.",
        initialDesiredNodes,
        desiredNodes,
        path);
    clusterResizeLogBuilder.cpuUtilization(currentCpu);
    clusterResizeLogBuilder.addResizeReason(" >>CPU strategy: " + path);
    return new ScalingEvent(roundedDesiredNodes, "cpu-constraint");
  }

  private double getCurrentCpu(final BigtableCluster cluster, final Duration samplingDuration) {
    return stackdriverClient.getCpuLoad(cluster, samplingDuration);
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy