Introduce autoscaling decisions (#53934)
This is the first in a series of commits that will introduce the autoscaling deciders framework. This commit introduces the basic framework for representing autoscaling decisions.
This commit is contained in:
parent
c1c9f7a735
commit
e3ca124537
|
@ -47,6 +47,6 @@ The API returns the following result:
|
|||
[source,console-result]
|
||||
--------------------------------------------------
|
||||
{
|
||||
|
||||
decisions: []
|
||||
}
|
||||
--------------------------------------------------
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.autoscaling;
|
||||
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents an autoscaling decision.
|
||||
*/
|
||||
public class AutoscalingDecision implements ToXContent, Writeable {
|
||||
|
||||
private final String name;
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
private final AutoscalingDecisionType type;
|
||||
|
||||
public AutoscalingDecisionType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
private final String reason;
|
||||
|
||||
public String reason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public AutoscalingDecision(final String name, final AutoscalingDecisionType type, final String reason) {
|
||||
this.name = Objects.requireNonNull(name);
|
||||
this.type = Objects.requireNonNull(type);
|
||||
this.reason = Objects.requireNonNull(reason);
|
||||
}
|
||||
|
||||
public AutoscalingDecision(final StreamInput in) throws IOException {
|
||||
this.name = in.readString();
|
||||
this.type = AutoscalingDecisionType.readFrom(in);
|
||||
this.reason = in.readString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(final StreamOutput out) throws IOException {
|
||||
out.writeString(name);
|
||||
type.writeTo(out);
|
||||
out.writeString(reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(final XContentBuilder builder, final ToXContent.Params params) throws IOException {
|
||||
builder.startObject();
|
||||
{
|
||||
builder.field("name", name);
|
||||
builder.field("type", type);
|
||||
builder.field("reason", reason);
|
||||
}
|
||||
builder.endObject();
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final AutoscalingDecision that = (AutoscalingDecision) o;
|
||||
return name.equals(that.name) && type == that.type && reason.equals(that.reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, type, reason);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.autoscaling;
|
||||
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.xcontent.ToXContentFragment;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Represents the type of an autoscaling decision: to indicating if a scale down, no scaling event, or a scale up is needed.
|
||||
*/
|
||||
public enum AutoscalingDecisionType implements Writeable, ToXContentFragment {
|
||||
|
||||
/**
|
||||
* Indicates that a scale down event is needed.
|
||||
*/
|
||||
SCALE_DOWN((byte) 0),
|
||||
|
||||
/**
|
||||
* Indicates that no scaling event is needed.
|
||||
*/
|
||||
NO_SCALE((byte) 1),
|
||||
|
||||
/**
|
||||
* Indicates that a scale up event is needed.
|
||||
*/
|
||||
SCALE_UP((byte) 2);
|
||||
|
||||
private final byte id;
|
||||
|
||||
byte id() {
|
||||
return id;
|
||||
}
|
||||
|
||||
AutoscalingDecisionType(final byte id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public static AutoscalingDecisionType readFrom(final StreamInput in) throws IOException {
|
||||
final byte id = in.readByte();
|
||||
switch (id) {
|
||||
case 0:
|
||||
return SCALE_DOWN;
|
||||
case 1:
|
||||
return NO_SCALE;
|
||||
case 2:
|
||||
return SCALE_UP;
|
||||
default:
|
||||
throw new IllegalArgumentException("unexpected value [" + id + "] for autoscaling decision type");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(final StreamOutput out) throws IOException {
|
||||
out.writeByte(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
|
||||
builder.value(name().toLowerCase(Locale.ROOT));
|
||||
return builder;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.autoscaling;
|
||||
|
||||
import org.elasticsearch.common.io.stream.StreamInput;
|
||||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.common.xcontent.ToXContent;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a collection of individual autoscaling decisions that can be aggregated into a single autoscaling decision.
|
||||
*/
|
||||
public class AutoscalingDecisions implements ToXContent, Writeable {
|
||||
|
||||
private final Collection<AutoscalingDecision> decisions;
|
||||
|
||||
public AutoscalingDecisions(final Collection<AutoscalingDecision> decisions) {
|
||||
Objects.requireNonNull(decisions);
|
||||
if (decisions.isEmpty()) {
|
||||
throw new IllegalArgumentException("decisions can not be empty");
|
||||
}
|
||||
this.decisions = decisions;
|
||||
}
|
||||
|
||||
public AutoscalingDecisions(final StreamInput in) throws IOException {
|
||||
this.decisions = in.readList(AutoscalingDecision::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(final StreamOutput out) throws IOException {
|
||||
out.writeCollection(decisions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
|
||||
for (final AutoscalingDecision decision : decisions) {
|
||||
decision.toXContent(builder, params);
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
public AutoscalingDecisionType type() {
|
||||
if (decisions.stream().anyMatch(p -> p.type() == AutoscalingDecisionType.SCALE_UP)) {
|
||||
// if any deciders say to scale up
|
||||
return AutoscalingDecisionType.SCALE_UP;
|
||||
} else if (decisions.stream().allMatch(p -> p.type() == AutoscalingDecisionType.SCALE_DOWN)) {
|
||||
// if all deciders say to scale down
|
||||
return AutoscalingDecisionType.SCALE_DOWN;
|
||||
} else {
|
||||
// otherwise, do not scale
|
||||
return AutoscalingDecisionType.NO_SCALE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
final org.elasticsearch.xpack.autoscaling.AutoscalingDecisions that = (org.elasticsearch.xpack.autoscaling.AutoscalingDecisions) o;
|
||||
return decisions.equals(that.decisions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(decisions);
|
||||
}
|
||||
|
||||
}
|
|
@ -14,8 +14,13 @@ import org.elasticsearch.common.io.stream.StreamInput;
|
|||
import org.elasticsearch.common.io.stream.StreamOutput;
|
||||
import org.elasticsearch.common.xcontent.ToXContentObject;
|
||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||
import org.elasticsearch.xpack.autoscaling.AutoscalingDecisions;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class GetAutoscalingDecisionAction extends ActionType<GetAutoscalingDecisionAction.Response> {
|
||||
|
||||
|
@ -60,24 +65,37 @@ public class GetAutoscalingDecisionAction extends ActionType<GetAutoscalingDecis
|
|||
|
||||
public static class Response extends ActionResponse implements ToXContentObject {
|
||||
|
||||
public Response() {
|
||||
private final SortedMap<String, AutoscalingDecisions> decisions;
|
||||
|
||||
public Response(final SortedMap<String, AutoscalingDecisions> decisions) {
|
||||
this.decisions = Objects.requireNonNull(decisions);
|
||||
}
|
||||
|
||||
public Response(final StreamInput in) throws IOException {
|
||||
super(in);
|
||||
decisions = new TreeMap<>(in.readMap(StreamInput::readString, AutoscalingDecisions::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeTo(final StreamOutput out) {
|
||||
|
||||
public void writeTo(final StreamOutput out) throws IOException {
|
||||
out.writeMap(decisions, StreamOutput::writeString, (o, decision) -> decision.writeTo(o));
|
||||
}
|
||||
|
||||
@Override
|
||||
public XContentBuilder toXContent(final XContentBuilder builder, final Params params) throws IOException {
|
||||
builder.startObject();
|
||||
{
|
||||
|
||||
builder.startArray("decisions");
|
||||
{
|
||||
for (final Map.Entry<String, AutoscalingDecisions> decision : decisions.entrySet()) {
|
||||
builder.startObject();
|
||||
{
|
||||
builder.field(decision.getKey(), decision.getValue());
|
||||
}
|
||||
builder.endObject();
|
||||
}
|
||||
}
|
||||
builder.endArray();
|
||||
}
|
||||
builder.endObject();
|
||||
return builder;
|
||||
|
|
|
@ -19,6 +19,8 @@ import org.elasticsearch.threadpool.ThreadPool;
|
|||
import org.elasticsearch.transport.TransportService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class TransportGetAutoscalingDecisionAction extends TransportMasterNodeAction<
|
||||
GetAutoscalingDecisionAction.Request,
|
||||
|
@ -59,7 +61,7 @@ public class TransportGetAutoscalingDecisionAction extends TransportMasterNodeAc
|
|||
final ClusterState state,
|
||||
final ActionListener<GetAutoscalingDecisionAction.Response> listener
|
||||
) {
|
||||
listener.onResponse(new GetAutoscalingDecisionAction.Response());
|
||||
listener.onResponse(new GetAutoscalingDecisionAction.Response(Collections.unmodifiableSortedMap(new TreeMap<>())));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.autoscaling;
|
||||
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class AutoscalingDecisionTests extends AutoscalingTestCase {
|
||||
|
||||
public void testAutoscalingDecisionType() {
|
||||
final AutoscalingDecisionType type = randomFrom(AutoscalingDecisionType.values());
|
||||
final AutoscalingDecision decision = randomAutoscalingDecisionOfType(type);
|
||||
assertThat(decision.type(), equalTo(type));
|
||||
}
|
||||
|
||||
public void testAutoscalingDecisionTypeSerialization() throws IOException {
|
||||
final AutoscalingDecisionType before = randomFrom(AutoscalingDecisionType.values());
|
||||
final BytesStreamOutput out = new BytesStreamOutput();
|
||||
before.writeTo(out);
|
||||
final AutoscalingDecisionType after = AutoscalingDecisionType.readFrom(out.bytes().streamInput());
|
||||
assertThat(after, equalTo(before));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.autoscaling;
|
||||
|
||||
import org.elasticsearch.common.io.stream.BytesStreamOutput;
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.test.AbstractWireSerializingTestCase;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class AutoscalingDecisionTypeWireSerializingTests extends AbstractWireSerializingTestCase<AutoscalingDecisionType> {
|
||||
|
||||
@Override
|
||||
protected Writeable.Reader<AutoscalingDecisionType> instanceReader() {
|
||||
return AutoscalingDecisionType::readFrom;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AutoscalingDecisionType createTestInstance() {
|
||||
return randomFrom(AutoscalingDecisionType.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void assertEqualInstances(final AutoscalingDecisionType expectedInstance, final AutoscalingDecisionType newInstance) {
|
||||
assertSame(expectedInstance, newInstance);
|
||||
assertEquals(expectedInstance, newInstance);
|
||||
assertEquals(expectedInstance.hashCode(), newInstance.hashCode());
|
||||
}
|
||||
|
||||
public void testInvalidAutoscalingDecisionTypeSerialization() throws IOException {
|
||||
final BytesStreamOutput out = new BytesStreamOutput();
|
||||
final Set<Byte> values = Arrays.stream(AutoscalingDecisionType.values())
|
||||
.map(AutoscalingDecisionType::id)
|
||||
.collect(Collectors.toSet());
|
||||
final byte value = randomValueOtherThanMany(values::contains, ESTestCase::randomByte);
|
||||
out.writeByte(value);
|
||||
final IllegalArgumentException e = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> AutoscalingDecisionType.readFrom(out.bytes().streamInput())
|
||||
);
|
||||
assertThat(e.getMessage(), equalTo("unexpected value [" + value + "] for autoscaling decision type"));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.autoscaling;
|
||||
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.test.AbstractWireSerializingTestCase;
|
||||
|
||||
public class AutoscalingDecisionWireSerializingTests extends AbstractWireSerializingTestCase<AutoscalingDecision> {
|
||||
|
||||
@Override
|
||||
protected Writeable.Reader<AutoscalingDecision> instanceReader() {
|
||||
return AutoscalingDecision::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AutoscalingDecision createTestInstance() {
|
||||
return AutoscalingTestCase.randomAutoscalingDecision();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.autoscaling;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class AutoscalingDecisionsTests extends AutoscalingTestCase {
|
||||
|
||||
public void testAutoscalingDecisionsRejectsEmptyDecisions() {
|
||||
final IllegalArgumentException e = expectThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> new AutoscalingDecisions(Collections.emptyList())
|
||||
);
|
||||
assertThat(e.getMessage(), equalTo("decisions can not be empty"));
|
||||
}
|
||||
|
||||
public void testAutoscalingDecisionsTypeDown() {
|
||||
final AutoscalingDecisions decisions = randomAutoscalingDecisions(randomIntBetween(1, 8), 0, 0);
|
||||
assertThat(decisions.type(), equalTo(AutoscalingDecisionType.SCALE_DOWN));
|
||||
}
|
||||
|
||||
public void testAutoscalingDecisionsTypeNo() {
|
||||
final AutoscalingDecisions decision = randomAutoscalingDecisions(randomIntBetween(0, 8), randomIntBetween(1, 8), 0);
|
||||
assertThat(decision.type(), equalTo(AutoscalingDecisionType.NO_SCALE));
|
||||
}
|
||||
|
||||
public void testAutoscalingDecisionsTypeUp() {
|
||||
final AutoscalingDecisions decision = randomAutoscalingDecisions(0, 0, randomIntBetween(1, 8));
|
||||
assertThat(decision.type(), equalTo(AutoscalingDecisionType.SCALE_UP));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.autoscaling;
|
||||
|
||||
import org.elasticsearch.common.io.stream.Writeable;
|
||||
import org.elasticsearch.test.AbstractWireSerializingTestCase;
|
||||
|
||||
public class AutoscalingDecisionsWireSerializingTests extends AbstractWireSerializingTestCase<AutoscalingDecisions> {
|
||||
|
||||
@Override
|
||||
protected Writeable.Reader<AutoscalingDecisions> instanceReader() {
|
||||
return AutoscalingDecisions::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AutoscalingDecisions createTestInstance() {
|
||||
return AutoscalingTestCase.randomAutoscalingDecisions();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.autoscaling;
|
||||
|
||||
import org.elasticsearch.common.Randomness;
|
||||
import org.elasticsearch.test.ESTestCase;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AutoscalingTestCase extends ESTestCase {
|
||||
|
||||
static AutoscalingDecision randomAutoscalingDecision() {
|
||||
return randomAutoscalingDecisionOfType(randomFrom(AutoscalingDecisionType.values()));
|
||||
}
|
||||
|
||||
static AutoscalingDecision randomAutoscalingDecisionOfType(final AutoscalingDecisionType type) {
|
||||
return new AutoscalingDecision(randomAlphaOfLength(8), type, randomAlphaOfLength(8));
|
||||
}
|
||||
|
||||
static AutoscalingDecisions randomAutoscalingDecisions() {
|
||||
final int numberOfDecisions = 1 + randomIntBetween(1, 8);
|
||||
final List<AutoscalingDecision> decisions = new ArrayList<>(numberOfDecisions);
|
||||
for (int i = 0; i < numberOfDecisions; i++) {
|
||||
decisions.add(randomAutoscalingDecisionOfType(AutoscalingDecisionType.SCALE_DOWN));
|
||||
}
|
||||
final int numberOfDownDecisions = randomIntBetween(0, 8);
|
||||
final int numberOfNoDecisions = randomIntBetween(0, 8);
|
||||
final int numberOfUpDecisions = randomIntBetween(numberOfDownDecisions + numberOfNoDecisions == 0 ? 1 : 0, 8);
|
||||
return randomAutoscalingDecisions(numberOfDownDecisions, numberOfNoDecisions, numberOfUpDecisions);
|
||||
}
|
||||
|
||||
static AutoscalingDecisions randomAutoscalingDecisions(
|
||||
final int numberOfDownDecisions,
|
||||
final int numberOfNoDecisions,
|
||||
final int numberOfUpDecisions
|
||||
) {
|
||||
final List<AutoscalingDecision> decisions = new ArrayList<>(numberOfDownDecisions + numberOfNoDecisions + numberOfUpDecisions);
|
||||
for (int i = 0; i < numberOfDownDecisions; i++) {
|
||||
decisions.add(randomAutoscalingDecisionOfType(AutoscalingDecisionType.SCALE_DOWN));
|
||||
}
|
||||
for (int i = 0; i < numberOfNoDecisions; i++) {
|
||||
decisions.add(randomAutoscalingDecisionOfType(AutoscalingDecisionType.NO_SCALE));
|
||||
}
|
||||
for (int i = 0; i < numberOfUpDecisions; i++) {
|
||||
decisions.add(randomAutoscalingDecisionOfType(AutoscalingDecisionType.SCALE_UP));
|
||||
}
|
||||
Randomness.shuffle(decisions);
|
||||
return new AutoscalingDecisions(decisions);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue