Skip to content

Commit

Permalink
Introduce autoscaling decisions (elastic#53934)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jasontedor authored Mar 24, 2020
1 parent f39aa75 commit 94e81b0
Show file tree
Hide file tree
Showing 12 changed files with 485 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,6 @@ The API returns the following result:
[source,console-result]
--------------------------------------------------
{
decisions: []
}
--------------------------------------------------
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@
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> {

Expand Down Expand Up @@ -60,24 +65,37 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa

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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.elasticsearch.transport.TransportService;

import java.io.IOException;
import java.util.Collections;
import java.util.TreeMap;

public class TransportGetAutoscalingDecisionAction extends TransportMasterNodeAction<
GetAutoscalingDecisionAction.Request,
Expand Down Expand Up @@ -61,7 +63,7 @@ protected void masterOperation(
final ClusterState state,
final ActionListener<GetAutoscalingDecisionAction.Response> listener
) {
listener.onResponse(new GetAutoscalingDecisionAction.Response());
listener.onResponse(new GetAutoscalingDecisionAction.Response(Collections.unmodifiableSortedMap(new TreeMap<>())));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -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));
}

}
Loading

0 comments on commit 94e81b0

Please sign in to comment.