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

com.netflix.ndbench.plugin.dynamodb.DynamoDBAutoscalingConfigurer Maven / Gradle / Ivy

The newest version!
/*
 *  Copyright 2018 Netflix, Inc.
 *
 *  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.netflix.ndbench.plugin.dynamodb;

import com.amazonaws.auth.AWSCredentialsProvider;
import com.amazonaws.services.applicationautoscaling.AWSApplicationAutoScaling;
import com.amazonaws.services.applicationautoscaling.AWSApplicationAutoScalingClientBuilder;
import com.amazonaws.services.applicationautoscaling.model.DescribeScalableTargetsRequest;
import com.amazonaws.services.applicationautoscaling.model.DescribeScalingPoliciesRequest;
import com.amazonaws.services.applicationautoscaling.model.MetricType;
import com.amazonaws.services.applicationautoscaling.model.PolicyType;
import com.amazonaws.services.applicationautoscaling.model.PredefinedMetricSpecification;
import com.amazonaws.services.applicationautoscaling.model.PutScalingPolicyRequest;
import com.amazonaws.services.applicationautoscaling.model.RegisterScalableTargetRequest;
import com.amazonaws.services.applicationautoscaling.model.ScalableDimension;
import com.amazonaws.services.applicationautoscaling.model.ServiceNamespace;
import com.amazonaws.services.applicationautoscaling.model.TargetTrackingScalingPolicyConfiguration;
import com.amazonaws.services.dynamodbv2.model.DescribeLimitsResult;
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
import com.amazonaws.services.securitytoken.model.GetCallerIdentityRequest;
import com.google.inject.Inject;
import com.google.inject.Singleton;

@Singleton
public class DynamoDBAutoscalingConfigurer {
    private static final int DEFAULT_SCALE_IN_OUT_COOLDOWN = 60;

    private final AWSApplicationAutoScaling autoScalingClient;
    private final AWSSecurityTokenService securityTokenService;

    @Inject
    public DynamoDBAutoscalingConfigurer(final AWSCredentialsProvider awsCredentialsProvider) {
        autoScalingClient = AWSApplicationAutoScalingClientBuilder.standard()
                .withCredentials(awsCredentialsProvider)
                .build();
        securityTokenService = AWSSecurityTokenServiceClientBuilder.standard()
                .withCredentials(awsCredentialsProvider)
                .build();
    }

    /**
     * configure autoscaling as per aws docs:
     * https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/AutoScaling.HowTo.SDK.html
     * @param readCapacityUnits
     * @param writeCapacityUnits
     * @param tableName
     * @param limits
     * @param targetWriteUtilization
     * @param targetReadUtilization
     */
    public void setupAutoscaling(Long readCapacityUnits,
                                  Long writeCapacityUnits,
                                  String tableName,
                                  DescribeLimitsResult limits,
                                  Integer targetWriteUtilization,
                                  Integer targetReadUtilization) {
        String resourceID = "table/" + tableName;
        String accountId = securityTokenService.getCallerIdentity(new GetCallerIdentityRequest()).getAccount();
        String serviceRoleArn = "arn:aws:iam::" + accountId
                + ":role/aws-service-role/dynamodb.application-autoscaling.amazonaws.com/AWSServiceRoleForApplicationAutoScaling_"
                + tableName;
        createAndVerifyScalableTargetAndScalingPolicy(serviceRoleArn,
                resourceID,
                ScalableDimension.DynamodbTableWriteCapacityUnits,
                MetricType.DynamoDBWriteCapacityUtilization,
                writeCapacityUnits,
                limits.getTableMaxWriteCapacityUnits().intValue(),
                targetWriteUtilization);
        createAndVerifyScalableTargetAndScalingPolicy(serviceRoleArn,
                resourceID,
                ScalableDimension.DynamodbTableReadCapacityUnits,
                MetricType.DynamoDBReadCapacityUtilization,
                readCapacityUnits,
                limits.getTableMaxReadCapacityUnits().intValue(),
                targetReadUtilization);
    }

    private void createAndVerifyScalableTargetAndScalingPolicy(String serviceRoleArn,
                                                               String resourceID,
                                                               ScalableDimension scalableDimension,
                                                               MetricType metricType,
                                                               Long capacityUnits,
                                                               Integer maxCapacityUnits,
                                                               Integer utilizationTarget) {
        createAndVerifyScalableTarget(resourceID, serviceRoleArn, scalableDimension, capacityUnits.intValue(),
                maxCapacityUnits);
        createAndVerifyScalingPolicy(resourceID, scalableDimension, metricType, utilizationTarget.doubleValue());
    }

    private void createAndVerifyScalingPolicy(final String resourceID,
                                              final ScalableDimension scalableDimension,
                                              final MetricType metricType,
                                              final Double targetValue) {
        // Configure a scaling policy
        TargetTrackingScalingPolicyConfiguration targetTrackingScalingPolicyConfiguration =
                new TargetTrackingScalingPolicyConfiguration()
                        .withPredefinedMetricSpecification(new PredefinedMetricSpecification().withPredefinedMetricType(metricType))
                        .withTargetValue(targetValue)
                        .withScaleInCooldown(DEFAULT_SCALE_IN_OUT_COOLDOWN)
                        .withScaleOutCooldown(DEFAULT_SCALE_IN_OUT_COOLDOWN);

        // Create the scaling policy, based on your configuration
        PutScalingPolicyRequest pspRequest = new PutScalingPolicyRequest()
                .withServiceNamespace(ServiceNamespace.Dynamodb)
                .withScalableDimension(scalableDimension)
                .withResourceId(resourceID)
                .withPolicyName("My" + scalableDimension.name() + "ScalingPolicy")
                .withPolicyType(PolicyType.TargetTrackingScaling)
                .withTargetTrackingScalingPolicyConfiguration(targetTrackingScalingPolicyConfiguration);

        final String scalingPolicyArn;
        try {
            scalingPolicyArn = autoScalingClient.putScalingPolicy(pspRequest).getPolicyARN();
        } catch (Exception e) {
            throw new IllegalStateException("Unable to put scaling policy", e);
        }

        // Verify that the scaling policy was created
        DescribeScalingPoliciesRequest dspRequest = new DescribeScalingPoliciesRequest()
                .withServiceNamespace(ServiceNamespace.Dynamodb)
                .withScalableDimension(scalableDimension)
                .withResourceId(resourceID);

        try {
            autoScalingClient.describeScalingPolicies(dspRequest).getScalingPolicies().stream()
                    .filter(scalingPolicy -> scalingPolicy.getPolicyARN().equals(scalingPolicyArn))
                    .findFirst()
                    .orElseThrow(() -> new IllegalStateException("Scaling policy was created but arn did not match"));
        } catch (Exception e) {
            throw new IllegalStateException("Unable to verify scaling policy", e);
        }
    }

    private void createAndVerifyScalableTarget(final String resourceID,
                                               final String serviceRoleArn,
                                               final ScalableDimension scalableDimension,
                                               final Integer minimumCapacity,
                                               final Integer maximumCapacity) {
        // Define the scalable targets
        RegisterScalableTargetRequest rstRequest = new RegisterScalableTargetRequest()
                .withServiceNamespace(ServiceNamespace.Dynamodb)
                .withResourceId(resourceID)
                .withScalableDimension(scalableDimension)
                .withMinCapacity(minimumCapacity)
                .withMaxCapacity(maximumCapacity)
                .withRoleARN(serviceRoleArn);

        try {
            autoScalingClient.registerScalableTarget(rstRequest);
        } catch (Exception e) {
            throw new IllegalStateException("Unable to register scalable target", e);
        }

        // Verify that the target was created
        // note that scalable targets do not have arns of their own so need to verify the contents
        DescribeScalableTargetsRequest dscRequest = new DescribeScalableTargetsRequest()
                .withServiceNamespace(ServiceNamespace.Dynamodb)
                .withScalableDimension(scalableDimension)
                .withResourceIds(resourceID);
        try {
            autoScalingClient.describeScalableTargets(dscRequest).getScalableTargets().stream()
                    .filter(target -> target.getResourceId().equals(resourceID)
                            && target.getScalableDimension().equals(scalableDimension.toString())
                            && target.getServiceNamespace().equals(ServiceNamespace.Dynamodb.toString())
                            && target.getMinCapacity().equals(minimumCapacity)
                            && target.getMaxCapacity().equals(maximumCapacity)
                            && target.getRoleARN().equals(serviceRoleArn))
                    .findFirst()
                    .orElseThrow(() -> new IllegalStateException("Target was created but not all fields matched"));
        } catch (Exception e) {
            throw new IllegalStateException("Unable to verify scalable target", e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy