Dhalion-v2 provides simplified Dhalion api. Highlights:
1. Nested hashmaps represented by ComponentMetrics are replaced by Tables. The
   Table structure simplifies searching & filtering of metrics & event objects
2. All objects created by Dhalion phases have consistent structures.
3. The Policy Executor enforces the kind of facts a phase can create. These
   fact objects are make available through Execution context.
This commit is contained in:
Ashvin Agrawal 2018-02-22 14:32:29 -08:00
Родитель 2a803b38f1 1cf3173c4c
Коммит 5cc50fea92
38 изменённых файлов: 2238 добавлений и 1547 удалений

Просмотреть файл

@ -5,7 +5,7 @@
<groupId>com.microsoft.dhalion</groupId>
<artifactId>dhalion</artifactId>
<version>0.0.1_3</version>
<version>0.2.1</version>
<packaging>jar</packaging>
<name>${project.groupId}:${project.artifactId}</name>
@ -105,6 +105,11 @@
<artifactId>commons-cli</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>tech.tablesaw</groupId>
<artifactId>tablesaw-core</artifactId>
<version>0.11.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>

Просмотреть файл

@ -6,28 +6,45 @@
*/
package com.microsoft.dhalion.api;
import java.util.List;
import com.microsoft.dhalion.core.Measurement;
import com.microsoft.dhalion.core.Symptom;
import com.microsoft.dhalion.policy.PoliciesExecutor.ExecutionContext;
import com.microsoft.dhalion.detector.Symptom;
import java.util.Collection;
/**
* {@link IDetector} typically examines {@link Measurement}s and produce {@link Symptom}s for any observed anomalies or
* system health issues.
*/
public interface IDetector {
/**
* Initializes this instance and should be invoked once by the system before its use.
* @return returns types of {@link Symptom}s created by this {@link IDetector}
*/
default void initialize() {
default Collection<String> getSymptomTypes() {
throw new UnsupportedOperationException();
}
/**
* Detects a problem or anomaly with the distributed application
* Initializes this instance and will be invoked once before this instance is used.
*
* @return a list of issues detected by the symptom detectors
* @param context execution context for this instance
*/
default List<Symptom> detect() {
return null;
default void initialize(ExecutionContext context) {
}
/**
* Release all acquired resources and prepare for termination of this instance
* Triggers system health examination typically using latest {@link Measurement}s and produces {@link Symptom}s
* representing the observations.
*
* @param measurements most recently fetched {@link Measurement}s
* @return the {@link Symptom}s created using latest observations
*/
default Collection<Symptom> detect(Collection<Measurement> measurements) {
throw new UnsupportedOperationException();
}
/**
* Releases all acquired resources and prepare for termination of this {@link IDetector}
*/
default void close() {
}

Просмотреть файл

@ -6,30 +6,42 @@
*/
package com.microsoft.dhalion.api;
import java.util.List;
import java.util.Map;
import com.microsoft.dhalion.core.Diagnosis;
import com.microsoft.dhalion.core.Symptom;
import com.microsoft.dhalion.policy.PoliciesExecutor.ExecutionContext;
import com.microsoft.dhalion.detector.Symptom;
import com.microsoft.dhalion.diagnoser.Diagnosis;
import java.util.Collection;
/**
* A {@link IDiagnoser} evaluates one or more {@link Symptom}s and produces a {@link Diagnosis}, if
* any, representing a possible problem responsible for the observed {@link Symptom}s.
* A {@link IDiagnoser} examines and correlates one or more {@link Symptom}s and tries to identify a root cause. If
* a reason is found, {@link IDetector} produces a {@link Diagnosis} representing a possible problem responsible for
* the observed {@link Symptom}s.
*/
public interface IDiagnoser {
/**
* Initializes this instance and should be invoked once by the system before its use.
* @return returns types of {@link Diagnosis}s created by this {@link IDiagnoser}
*/
default void initialize() {
default Collection<String> getDiagnosisTypes() {
throw new UnsupportedOperationException();
}
/**
* Evaluates available {@link Symptom}s and determines if a problem exists
* Initializes this instance and will be invoked once before this instance is used.
*
* @param context execution context for this instance
*/
default void initialize(ExecutionContext context) {
}
/**
* Triggers examination of available {@link Symptom}s and to identify a health issue responsible for the
* {@link Symptom}s.
*
* @param symptoms recently identified {@link Symptom}s
* @return a {@link Diagnosis} instance representing a problem
*/
default List<Diagnosis> diagnose(List<Symptom> symptoms) {
return null;
default Collection<Diagnosis> diagnose(Collection<Symptom> symptoms) {
throw new UnsupportedOperationException();
}
/**

Просмотреть файл

@ -7,70 +7,74 @@
package com.microsoft.dhalion.api;
import java.util.List;
import java.util.concurrent.TimeUnit;
import com.microsoft.dhalion.core.Action;
import com.microsoft.dhalion.core.Diagnosis;
import com.microsoft.dhalion.core.Measurement;
import com.microsoft.dhalion.core.Symptom;
import com.microsoft.dhalion.policy.PoliciesExecutor.ExecutionContext;
import com.microsoft.dhalion.detector.Symptom;
import com.microsoft.dhalion.diagnoser.Diagnosis;
import com.microsoft.dhalion.resolver.Action;
import com.microsoft.dhalion.state.MetricsState;
import java.time.Duration;
import java.util.Collection;
/**
* A {@link IHealthPolicy} strives to keep a distributed application healthy. It uses one or more of
* {@link IDetector}s, {@link IDiagnoser}s and {@link IResolver}s to achieve this. It is expected that the policy
* will be executed periodically.
* {@link IDetector}s, {@link IDiagnoser}s and {@link IResolver}s to achieve this. Once initialized, a policy is
* executed periodically. The policy executor invokes in order {@link ISensor}s, {@link IDetector}s,
* {@link IDiagnoser}s and {@link IResolver}s.
*/
public interface IHealthPolicy {
/**
* Initializes this instance and should be invoked once by the system before its use.
*
* @param context execution context of the policy
*/
void initialize(List<ISensor> sensors, List<IDetector> detectors, List<IDiagnoser> diagnosers,
List<IResolver> resolvers);
void initialize(ExecutionContext context);
/**
* Invoked periodically, this method executes one or more {@link ISensor}s.
* Invoked periodically, this method executes one or more {@link ISensor}s. Typically, {@link ISensor} execution
* will result in addition of latest {@link Measurement}s in the {@link ExecutionContext}.
*
* @return most recently fetched {@link Measurement}s
*/
void executeSensors(MetricsState metricsState);
Collection<Measurement> executeSensors();
/**
* Typically invoked after {@link ISensor}s this method executes one or more {@link IDetector}s.
* Invoked after {@link ISensor}s this method executes one or more {@link IDetector}s. Most recently fetched
* {@link Measurement}s are provided, while additional {@link Measurement}s can be obtained from
* {@link ExecutionContext}.
*
* @param measurements most recently fetched {@link Measurement}s
* @return newly identified {@link Symptom}s
*/
List<Symptom> executeDetectors();
Collection<Symptom> executeDetectors(Collection<Measurement> measurements);
/**
* Typically invoked after {@link IDetector}s, this method executes one or more
* {@link IDiagnoser}s.
* Invoked after {@link IDetector}s, this method executes one or more {@link IDiagnoser}s.
* newly identified {@link Symptom}s
*
* @param symptoms recently identified {@link Symptom}s
* @return likely causes of the observed {@link Symptom}s
*/
List<Diagnosis> executeDiagnosers(List<Symptom> symptoms);
Collection<Diagnosis> executeDiagnosers(Collection<Symptom> symptoms);
/**
* Selects the most suitable {@link IResolver} based on the set of {@link Diagnosis} objects.
* Invoked after {@link IDiagnoser}s, this method executes one or more {@link IResolver} to fix any identified
* issues. Typically, a policy will invoke the most advantageous {@link IResolver} from the resolvers belonging to
* the policy to avoid overlapping {@link Action}s and conflicts.
*
* @param diagnosis recently identified likely-causes of the observed {@link Symptom}s
* @return actions executed to mitigate health issues
*/
IResolver selectResolver(List<Diagnosis> diagnosis);
Collection<Action> executeResolvers(Collection<Diagnosis> diagnosis);
/**
* Typically invoked after {@link IDiagnoser}s, this method executes one or more {@link IResolver}
* to fix any identified issues.
* @return the remaining delay before re-execution of this policy
*/
List<Action> executeResolver(IResolver resolver, List<Diagnosis> diagnosis);
/**
* @param unit the desired unit of time
* @return Returns the remaining delay before re-execution of this policy, in the
* given time unit.
*/
long getDelay(TimeUnit unit);
Duration getDelay();
/**
* Release all acquired resources and prepare for termination of this instance
*/
default void close() {
}
List<IDetector> getDetectors();
List<IDiagnoser> getDiagnosers();
List<IResolver> getResolvers();
}

Просмотреть файл

@ -6,32 +6,42 @@
*/
package com.microsoft.dhalion.api;
import java.util.List;
import com.microsoft.dhalion.core.Action;
import com.microsoft.dhalion.core.Diagnosis;
import com.microsoft.dhalion.core.Symptom;
import com.microsoft.dhalion.policy.PoliciesExecutor.ExecutionContext;
import com.microsoft.dhalion.diagnoser.Diagnosis;
import com.microsoft.dhalion.resolver.Action;
import java.util.Collection;
/**
* A {@link IResolver}'s major goal is to resolve the anomaly identified by a {@link Diagnosis}.
* Input to a {@link IResolver} is a {@link Diagnosis} instance and based on that, it executes
* appropriate action to bring a linked component or system back to a healthy state.
* A {@link IResolver}'s major goal is to execute {@link Action}s to resolve an anomaly or health issue identified by a
* {@link IDiagnoser}. {@link IResolver} typically consume {@link Diagnosis} and executes appropriate action to bring a
* linked component or system back to a healthy state.
*/
public interface IResolver {
/**
* This method is invoked once to initialize the {@link IResolver} instance
* @return returns types of {@link Action}s created by this {@link IResolver}
*/
default void initialize() {
default Collection<String> getActionTypes() {
throw new UnsupportedOperationException();
}
/**
* {@link IResolver#resolve} is invoked to fix one or more problems identified in the
* {@link Diagnosis} instance.
* Initializes this instance and will be invoked once before this instance is used.
*
* @param diagnosis a list of anomalies detected by a {@link IDiagnoser}s
* @param context execution context for this instance
*/
default void initialize(ExecutionContext context) {
}
/**
* Triggers execution of {@link Action}s which are expected to improved system health.
*
* @param diagnosis recently identified likely-causes of the observed {@link Symptom}s
* @return all the actions executed by this resolver to mitigate the problems
*/
default List<Action> resolve(List<Diagnosis> diagnosis){
return null;
default Collection<Action> resolve(Collection<Diagnosis> diagnosis) {
throw new UnsupportedOperationException();
}
/**

Просмотреть файл

@ -6,55 +6,43 @@
*/
package com.microsoft.dhalion.api;
import com.microsoft.dhalion.metrics.ComponentMetrics;
import com.microsoft.dhalion.metrics.MetricsStats;
import com.microsoft.dhalion.core.Measurement;
import com.microsoft.dhalion.policy.PoliciesExecutor.ExecutionContext;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Collection;
/**
* A {@link ISensor} typically provides a system metric. For e.g. execute count
* A {@link ISensor} provides {@link Measurement}s for one or more system metrics. For e.g. throughput, latency, etc
*/
public interface ISensor {
/**
* @return returns name of the metric on which this sensor operates
* @return returns types of metrics whose {@link Measurement}s are fetched by this {@link ISensor}
*/
default String getMetricName() {
return null;
}
/**
* Pulls a given metric information for all component from an external source. The sensor instance then manages the
* pulled information in local state. It also updates the {@link MetricsStats} associated with this sensor.
*/
default ComponentMetrics fetchMetrics() {
default Collection<String> getMetricTypes() {
throw new UnsupportedOperationException();
}
/**
* @return returns the most recently fetched metric value for all components
* Initializes this instance and will be invoked once before this instance is used.
*
* @param context execution context for this instance
*/
default ComponentMetrics readMetrics() {
return fetchMetrics();
default void initialize(ExecutionContext context) {
}
/**
* @return returns latest metric stats for all components as a map
* Provides {@link Measurement}s of the metrics managed by this {@link ISensor} for all components of the application.
* The {@link ISensor}'s configuration can be used to customize the result. For e.g. duration for which the
* {@link Measurement}s are needed and external source configuration.
*
* @return latest {@link Measurement}s
*/
default Map<String, MetricsStats> readStats() {
return new HashMap<>();
default Collection<Measurement> fetch() {
throw new UnsupportedOperationException();
}
/**
* @return returns latest metric stats for a specific component
*/
default Optional<MetricsStats> readStats(String component) {
return Optional.empty();
}
/**
* Releases all acquired resources and prepare for termination of this instance
* Releases all acquired resources and prepare for termination of this {@link ISensor}
*/
default void close() {
}

Просмотреть файл

@ -6,84 +6,60 @@
*/
package com.microsoft.dhalion.api;
import com.microsoft.dhalion.metrics.ComponentMetrics;
import com.microsoft.dhalion.metrics.InstanceMetrics;
import com.microsoft.dhalion.core.Measurement;
import java.time.Duration;
import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
/**
* A {@link MetricsProvider} implementation will fetch and provide metrics to the consumers. For
* e.g. a {@link IDetector} may use it to get execute latency for a component.
* A {@link MetricsProvider} implements common utility methods to produce {@link Measurement}s. In some cases it will
* provide for connecting and authorizing with a remote metrics source. Or provide end points where external services
* can produce {@link Measurement}s. Typically {@link ISensor} will use this implementation to obtain raw metrics.
* {@link ISensor} may then process and cleanse the metrics.
*/
public interface MetricsProvider {
/**
* Returns metric value for all instances of one or more {@code components}. For e.g.
* returns total number of records processed in 60 seconds by all instances of a storm bolt.
*
* @param metric id of the metric
* @param duration the duration for which the metric was aggregated
* @param components ids of the components for which the metric is needed
* @return components metrics
* Initializes this instance and will be invoked once before this instance is used.
*/
default ComponentMetrics getComponentMetrics(String metric, Duration duration, String... components) {
return null;
default void initialize() {
}
/**
* Returns metric value for a specific {@code instance} of a {@code component}. For e.g.
* returns total number of records processed in 60 seconds by instance-1 of a storm bolt.
* Returns raw {@link Measurement}s for all instances of one or more components of a distributed app in a specified
* time window. For e.g. returns total number of records processed between time t1 and t2 by all instances of a
* storm bolt.
*
* @param metric id of the metric
* @param duration the duration for which the metric was aggregated
* @param component id of the components for which the metric is needed
* @param instance id of the instance
* @return InstanceMetrics containing the value(s)
*/
default InstanceMetrics getInstanceMetrics(String metric, Duration duration, String component, String instance) {
return null;
}
/**
* Returns metric value for all instances of one or more components in a specified time window.
* For e.g. returns total number of records processed between time t1 and t2 by all instances
* of a storm bolt. The implementation may return multiple records per instance. For e.g. the
* implementation may return 3 records, one per minute, for an instance for a 3 minute long time
* window.
*
* @param metric id of the metric
* @param startTime metric aggregation window start time, endTime = startTime - duration
* @param duration the duration for which the metric was aggregated
* @param metrics ids of the metrics
* @param components ids of the components for which the metric is needed
* @return components metrics
* @return collection of {@link Measurement}s
*/
default ComponentMetrics getComponentMetrics(String metric,
Instant startTime,
Duration duration,
String... components) {
return null;
default Collection<Measurement> getMeasurements(Instant startTime,
Duration duration,
Collection<String> metrics,
Collection<String> components) {
throw new UnsupportedOperationException("This method is not implemented in the metrics provider");
}
/**
* Returns metric value for all instances of a specific instance of a components in a specified time
* window. For e.g. returns total number of records processed between time t1 and t2 by all instances
* of a storm bolt. The implementation may return multiple records per instance. For e.g. the
* implementation may return 3 records, one per minute, for an instance for a 3 minute long time
* window.
*
* @param metric id of the metric
* @param startTime metric aggregation window start time, endTime = startTime - duration
* @param duration the duration for which the metric was aggregated
* @param metric ids of the metrics
* @param component ids of the components for which the metric is needed
* @param instance id of the instance
* @return component metrics
* @return collection of {@link Measurement}s
* @see #getMeasurements(Instant, Duration, Collection, Collection)
*/
default InstanceMetrics getInstanceMetrics(String metric,
Instant startTime,
Duration duration,
String component,
String instance) {
return null;
default Collection<Measurement> getMeasurements(Instant startTime,
Duration duration,
String metric,
String component) {
return getMeasurements(startTime,
duration,
Collections.singletonList(metric),
Collections.singletonList(component));
}
/**

Просмотреть файл

@ -1,14 +0,0 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.common;
public class DuplicateMetricException extends RuntimeException {
public DuplicateMetricException(String component, String instance, String metric) {
super(String.format("Metric name %s already exists for %s/%s", metric, component, instance));
}
}

Просмотреть файл

@ -1,57 +0,0 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.common;
/**
* An {@link InstanceInfo} holds information identifying an instance of a component.
*/
public class InstanceInfo {
// id of the component
protected final String componentName;
// id of the instance
protected final String instanceName;
public InstanceInfo(String componentName, String instanceName) {
this.componentName = componentName;
this.instanceName = instanceName;
}
public String getComponentName() {
return componentName;
}
public String getInstanceName() {
return instanceName;
}
@Override
public String toString() {
return "InstanceInfo{" +
"componentName='" + componentName + '\'' +
", instanceName='" + instanceName + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InstanceInfo that = (InstanceInfo) o;
if (!componentName.equals(that.componentName)) return false;
return instanceName.equals(that.instanceName);
}
@Override
public int hashCode() {
int result = componentName.hashCode();
result = 31 * result + instanceName.hashCode();
return result;
}
}

Просмотреть файл

@ -0,0 +1,56 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.core;
import com.microsoft.dhalion.api.IResolver;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
/**
* {@link Action} is a representation of a action taken by {@link IResolver} to improve system health.
*/
public class Action extends Outcome {
private final Collection<Diagnosis> diagnosis = new ArrayList<>();
public Action(String type, Instant instant, Collection<String> assignments) {
this(type, instant, assignments, null);
}
public Action(String type, Instant instant, Collection<String> assignments, Collection<Diagnosis> diagnosis) {
super(type, instant, assignments);
if (diagnosis != null) {
this.diagnosis.addAll(diagnosis);
}
}
Action(int id, String type, Instant instant, Collection<String> assignments, Collection<Diagnosis> diagnosis) {
super(id, type, instant, assignments);
if (diagnosis != null) {
this.diagnosis.addAll(diagnosis);
}
}
/**
* @return {@link Diagnosis} referred to when this {@link Action} was created
*/
public Collection<Diagnosis> diagnosis() {
return Collections.unmodifiableCollection(diagnosis);
}
@Override
public String toString() {
return "Action{" +
"id=" + id() +
", type=" + type() +
", instant=" + instant() +
", assignments=" + assignments() +
'}';
}
}

Просмотреть файл

@ -0,0 +1,152 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.core;
import tech.tablesaw.api.Table;
import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
//TODO thread safety
/**
* An ordered collection of {@link Action}s. It provides methods to filter, query and aggregate the
* {@link Action}s.
*/
public class ActionTable extends OutcomeTable<Action> {
private ActionTable() {
super("Actions");
}
private ActionTable(Table table) {
super(table);
}
/**
* @param actions collections of actions
* @return a {@link ActionTable} holding the input
*/
public static ActionTable of(Collection<Action> actions) {
ActionTable table = new ActionTable();
table.addAll(actions);
return table;
}
private void addAll(Collection<Action> actions) {
actions.forEach(this::add);
}
/**
* @param id unique action id
* @return {@link Action}s with the given id
*/
public ActionTable id(int id) {
Table result = filterId(id);
return new ActionTable(result);
}
/**
* Retains all {@link Action}s with given action type
*
* @param types names of the action types, not null
* @return {@link ActionTable} containing filtered {@link Action}s
*/
public ActionTable type(Collection<String> types) {
return new ActionTable(filterType(types));
}
/**
* @param type a action type
* @return {@link ActionTable} containing filtered {@link Action}s
* @see #type(Collection)
*/
public ActionTable type(String type) {
return type(Collections.singletonList(type));
}
/**
* Retains all {@link Action}s with given assignment ids.
*
* @param assignments assignment ids, not null
* @return {@link ActionTable} containing filtered {@link Action}s
*/
public ActionTable assignment(Collection<String> assignments) {
return new ActionTable(filterAssignment(assignments));
}
/**
* @param assignment assignment id
* @return {@link ActionTable} containing filtered {@link Action}s
* @see #assignment(Collection)
*/
public ActionTable assignment(String assignment) {
return assignment(Collections.singletonList(assignment));
}
/**
* Retains all {@link Action}s with timestamp in the given range.
*
* @param oldest the oldest timestamp, null to ignore this condition
* @param newest the newest timestamp, null to ignore this condition
* @return {@link ActionTable} containing filtered {@link Action}s
*/
public ActionTable between(Instant oldest, Instant newest) {
return new ActionTable(filterTime(oldest, newest));
}
/**
* Sorts the {@link Action}s in this collection in the order of the specified keys
*
* @param descending false for ascending order, true for descending
* @param sortKeys one or more sort keys, e.g. {@link SortKey#ID}
* @return ordered {@link Action}s
*/
public ActionTable sort(boolean descending, SortKey... sortKeys) {
return new ActionTable(sortTable(descending, sortKeys));
}
/**
* Retains the {@link Action} positioned between <code>first</code> and <code>last</code>, both inclusive,
* positions in this collection
*
* @param first the lowest index {@link Action} to be retained
* @param last the highest index {@link Action} to be retained
* @return {@link ActionTable} containing specific {@link Action}s
*/
public ActionTable slice(int first, int last) {
return new ActionTable(sliceTable(first, last));
}
Action row2Obj(int index) {
return new Action(id.get(index),
type.get(index),
Instant.ofEpochMilli(timeStamp.get(index)),
Collections.singletonList(assignment.get(index)),
null);
}
/**
* Builds {@link ActionTable} instance and provides ability to update it.
*/
public static class Builder {
private final ActionTable actionsTable = new ActionTable();
public ActionTable get() {
return actionsTable;
}
public void addAll(Collection<Action> actions) {
if (actions == null) {
return;
}
this.actionsTable.addAll(actions);
}
}
}

Просмотреть файл

@ -0,0 +1,59 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.core;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
/**
* A {@link Diagnosis} is a representation of a possible causes of one or more {@link Symptom}s. For e.g. resource
* under-provisioning
*/
public class Diagnosis extends Outcome {
// symtoms referred to create this instance
private final Collection<Symptom> symptoms = new ArrayList<>();
public Diagnosis(String type, Instant instant, Collection<String> assignments) {
this(type, instant, assignments, null);
}
public Diagnosis(String type,
Instant instant,
Collection<String> assignments,
Collection<Symptom> symptoms) {
super(type, instant, assignments);
if (symptoms != null) {
this.symptoms.addAll(symptoms);
}
}
Diagnosis(int id, String type, Instant instant, Collection<String> assignments, Collection<Symptom> symptoms) {
super(id, type, instant, assignments);
if (symptoms != null) {
this.symptoms.addAll(symptoms);
}
}
/**
* @return {@link Symptom}s referred to when this {@link Diagnosis} was created
*/
public Collection<Symptom> symptoms() {
return Collections.unmodifiableCollection(symptoms);
}
@Override
public String toString() {
return "Diagnosis{" +
"id=" + id() +
", type=" + type() +
", instant=" + instant() +
", assignments=" + assignments() +
'}';
}
}

Просмотреть файл

@ -0,0 +1,152 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.core;
import tech.tablesaw.api.Table;
import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
//TODO thread safety
/**
* An ordered collection of {@link Diagnosis}. It provides methods to filter, query and aggregate the
* {@link Diagnosis} .
*/
public class DiagnosisTable extends OutcomeTable<Diagnosis> {
private DiagnosisTable() {
super("Symptoms");
}
private DiagnosisTable(Table table) {
super(table);
}
/**
* @param diagnosis collections of diagnosis
* @return a {@link DiagnosisTable} holding the input
*/
public static DiagnosisTable of(Collection<Diagnosis> diagnosis) {
DiagnosisTable table = new DiagnosisTable();
table.addAll(diagnosis);
return table;
}
private void addAll(Collection<Diagnosis> diagnosis) {
diagnosis.forEach(this::add);
}
/**
* @param id unique diagnosis id
* @return {@link Diagnosis} with the given id
*/
public DiagnosisTable id(int id) {
Table result = filterId(id);
return new DiagnosisTable(result);
}
/**
* Retains all {@link Diagnosis} with given diagnosis type
*
* @param types names of the diagnosis types, not null
* @return {@link DiagnosisTable} containing filtered {@link Diagnosis}
*/
public DiagnosisTable type(Collection<String> types) {
return new DiagnosisTable(filterType(types));
}
/**
* @param type a diagnosis type
* @return {@link DiagnosisTable} containing filtered {@link Diagnosis}
* @see #type(Collection)
*/
public DiagnosisTable type(String type) {
return type(Collections.singletonList(type));
}
/**
* Retains all {@link Diagnosis} with given assignment ids.
*
* @param assignments assignment ids, not null
* @return {@link DiagnosisTable} containing filtered {@link Diagnosis}
*/
public DiagnosisTable assignment(Collection<String> assignments) {
return new DiagnosisTable(filterAssignment(assignments));
}
/**
* @param assignment assignment id
* @return {@link DiagnosisTable} containing filtered {@link Diagnosis}
* @see #assignment(Collection)
*/
public DiagnosisTable assignment(String assignment) {
return assignment(Collections.singletonList(assignment));
}
/**
* Retains all {@link Diagnosis} with timestamp in the given range.
*
* @param oldest the oldest timestamp, null to ignore this condition
* @param newest the newest timestamp, null to ignore this condition
* @return {@link DiagnosisTable} containing filtered {@link Diagnosis}
*/
public DiagnosisTable between(Instant oldest, Instant newest) {
return new DiagnosisTable(filterTime(oldest, newest));
}
/**
* Sorts the {@link Diagnosis} in this collection in the order of the specified keys
*
* @param descending false for ascending order, true for descending
* @param sortKeys one or more sort keys, e.g. {@link SortKey#ID}
* @return ordered {@link Diagnosis}
*/
public DiagnosisTable sort(boolean descending, SortKey... sortKeys) {
return new DiagnosisTable(sortTable(descending, sortKeys));
}
/**
* Retains the {@link Diagnosis} positioned between <code>first</code> and <code>last</code>, both inclusive,
* positions in this collection
*
* @param first the lowest index {@link Diagnosis} to be retained
* @param last the highest index {@link Diagnosis} to be retained
* @return {@link DiagnosisTable} containing specific {@link Diagnosis}s
*/
public DiagnosisTable slice(int first, int last) {
return new DiagnosisTable(sliceTable(first, last));
}
Diagnosis row2Obj(int index) {
return new Diagnosis(id.get(index),
type.get(index),
Instant.ofEpochMilli(timeStamp.get(index)),
Collections.singletonList(assignment.get(index)),
null);
}
/**
* Builds {@link DiagnosisTable} instance and provides ability to update it.
*/
public static class Builder {
private final DiagnosisTable diagnosisTable = new DiagnosisTable();
public DiagnosisTable get() {
return diagnosisTable;
}
public void addAll(Collection<Diagnosis> diagnosis) {
if (diagnosis == null) {
return;
}
this.diagnosisTable.addAll(diagnosis);
}
}
}

Просмотреть файл

@ -0,0 +1,72 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.core;
import java.time.Instant;
/**
* A {@link Measurement} is value of a metric corresponding to an instance at a instant of time.
*/
public class Measurement {
private final String component;
private final String instance;
private final String type;
private final Instant instant;
private final double value;
public Measurement(String component, String instance, String type, Instant instant, double value) {
this.component = component;
this.instance = instance;
this.type = type;
this.instant = instant;
this.value = value;
}
public String component() {
return component;
}
public String instance() {
return instance;
}
public String type() {
return type;
}
public Instant instant() {
return instant;
}
public double value() {
return value;
}
@Override
public String toString() {
return "Measurement {" +
"component=" + component() +
", instance=" + instance() +
", type=" + type() +
", instant=" + instant() +
", value=" + value +
'}';
}
public static class ObjMeasurement extends Measurement {
private final Object reference;
public ObjMeasurement(String component, String instance, String metricType, Instant instant, Object value) {
super(component, instance, metricType, instant, 0);
this.reference = value;
}
public Object reference() {
return reference;
}
}
}

Просмотреть файл

@ -0,0 +1,366 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.core;
import tech.tablesaw.api.CategoryColumn;
import tech.tablesaw.api.DoubleColumn;
import tech.tablesaw.api.LongColumn;
import tech.tablesaw.api.Table;
import tech.tablesaw.filtering.Filter;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static tech.tablesaw.api.QueryHelper.both;
import static tech.tablesaw.api.QueryHelper.column;
import static tech.tablesaw.api.QueryHelper.or;
//TODO thread safety
/**
* An ordered collection of {@link Measurement}s. It provides methods to filter, query and aggregate the
* {@link Measurement}s.
*/
public class MeasurementsTable {
private final Table measurements;
private CategoryColumn component;
private CategoryColumn instance;
private CategoryColumn type;
private LongColumn timeStamps;
private DoubleColumn value;
public enum SortKey {
COMPONENT, INSTANCE, TIME_STAMP, TYPE, VALUE
}
private static final String COMPONENT = SortKey.COMPONENT.name();
private static final String INSTANCE = SortKey.INSTANCE.name();
private static final String TIME_STAMP = SortKey.TIME_STAMP.name();
private static final String TYPE = SortKey.TYPE.name();
private static final String VALUE = SortKey.VALUE.name();
private MeasurementsTable() {
component = new CategoryColumn(COMPONENT);
instance = new CategoryColumn(INSTANCE);
type = new CategoryColumn(TYPE);
timeStamps = new LongColumn(TIME_STAMP);
value = new DoubleColumn(VALUE);
measurements = Table.create("Measurements");
measurements.addColumn(component);
measurements.addColumn(instance);
measurements.addColumn(type);
measurements.addColumn(timeStamps);
measurements.addColumn(value);
}
private MeasurementsTable(Table table) {
this.measurements = table;
component = measurements.categoryColumn(COMPONENT);
instance = measurements.categoryColumn(INSTANCE);
type = measurements.categoryColumn(TYPE);
timeStamps = measurements.longColumn(TIME_STAMP);
value = measurements.doubleColumn(VALUE);
}
private void addAll(Collection<Measurement> measurements) {
measurements.forEach(measurement -> {
component.append(measurement.component());
instance.append(measurement.instance());
type.append(measurement.type());
timeStamps.append(measurement.instant().toEpochMilli());
value.append(measurement.value());
});
}
/**
* @param measurements collections of measurements
* @return a {@link MeasurementsTable} holding the input
*/
public static MeasurementsTable of(Collection<Measurement> measurements) {
MeasurementsTable table = new MeasurementsTable();
table.addAll(measurements);
return table;
}
/**
* Retains all {@link Measurement}s with given component names.
*
* @param names of the components, not null
* @return {@link MeasurementsTable} containing filtered {@link Measurement}s
*/
public MeasurementsTable component(Collection<String> names) {
return applyCategoryFilter(names, COMPONENT);
}
/**
* @param name a component name
* @return {@link MeasurementsTable} containing filtered {@link Measurement}s
* @see #component(Collection)
*/
public MeasurementsTable component(String name) {
return component(Collections.singletonList(name));
}
/**
* Retains all {@link Measurement}s with given metric type
*
* @param types names of the metric types, not null
* @return {@link MeasurementsTable} containing filtered {@link Measurement}s
*/
public MeasurementsTable type(Collection<String> types) {
return applyCategoryFilter(types, TYPE);
}
/**
* @param type a metric type
* @return {@link MeasurementsTable} containing filtered {@link Measurement}s
* @see #type(Collection)
*/
public MeasurementsTable type(String type) {
return type(Collections.singletonList(type));
}
/**
* Retains all {@link Measurement}s with given instance names.
*
* @param names of the instances, not null
* @return {@link MeasurementsTable} containing filtered {@link Measurement}s
*/
public MeasurementsTable instance(Collection<String> names) {
return applyCategoryFilter(names, INSTANCE);
}
/**
* @param name instance name
* @return {@link MeasurementsTable} containing filtered {@link Measurement}s
* @see #instance(Collection)
*/
public MeasurementsTable instance(String name) {
return instance(Collections.singletonList(name));
}
private MeasurementsTable applyCategoryFilter(Collection<String> names, String column) {
List<Filter> filters = names.stream().map(name -> column(column).isEqualTo(name)).collect(Collectors.toList());
Table result = measurements.selectWhere(or(filters));
return new MeasurementsTable(result);
}
/**
* Retains all {@link Measurement}s with timestamp in the given range.
*
* @param oldest the oldest timestamp, null to ignore this condition
* @param newest the newest timestamp, null to ignore this condition
* @return {@link MeasurementsTable} containing filtered {@link Measurement}s
*/
public MeasurementsTable between(Instant oldest, Instant newest) {
return new MeasurementsTable(TableUtils.filterTime(measurements, TIME_STAMP, oldest, newest));
}
/**
* Retains all {@link Measurement}s created at the given timestamp.
*
* @param timestamp {@link Measurement} creation time.
* @return {@link MeasurementsTable} containing filtered {@link Measurement}s
*/
public MeasurementsTable instant(Instant timestamp) {
return between(timestamp, timestamp);
}
/**
* Retains only the {@link Measurement}s whose value is between <code>low</code> and <code>high</code>, both
* inclusive.
*
* @param low the lowest value to be retained
* @param high the highest value to be retained
* @return {@link MeasurementsTable} containing filtered {@link Measurement}s
*/
public MeasurementsTable valueBetween(double low, double high) {
Table result = measurements.selectWhere(
both(column(VALUE).isGreaterThanOrEqualTo(low),
column(VALUE).isLessThanOrEqualTo(high)));
return new MeasurementsTable(result);
}
/**
* @return count of {@link Measurement}s in this collection
*/
public int size() {
return measurements.rowCount();
}
/**
* @return max of all the {@link Measurement}s values
*/
public double max() {
return value.max();
}
/**
* @return min of all the {@link Measurement}s values
*/
public double min() {
return value.min();
}
/**
* @return mean of all the {@link Measurement}s values
*/
public double mean() {
return value.mean();
}
/**
* @return median of all the {@link Measurement}s values
*/
public double median() {
return value.median();
}
/**
* @return variance of all the {@link Measurement}s values
*/
public double variance() {
return value.variance();
}
/**
* @return sum of all the {@link Measurement}s values
*/
public double sum() {
return value.sum();
}
/**
* @return unique components names in this collection of {@link Measurement}s
*/
public Collection<String> uniqueComponents() {
return TableUtils.uniqueCategory(component);
}
/**
* @return unique instance names in this collection of {@link Measurement}s
*/
public Collection<String> uniqueInstances() {
return TableUtils.uniqueCategory(instance);
}
/**
* @return unique metric types in this collection of {@link Measurement}s
*/
public Collection<String> uniqueTypes() {
return TableUtils.uniqueCategory(type);
}
/**
* @return unique {@link Instant}s in this collection of {@link Measurement}s
*/
public Collection<Instant> uniqueInstants() {
return TableUtils.uniqueInstants(timeStamps);
}
/**
* Sorts the {@link Measurement}s in this collection in the order of the specified keys
*
* @param descending false for ascending order, true for descending
* @param sortKeys one or more sort keys, e.g. {@link SortKey#COMPONENT}
* @return ordered {@link Measurement}s
*/
public MeasurementsTable sort(boolean descending, SortKey... sortKeys) {
String[] columns = new String[sortKeys.length];
for (int i = 0; i < sortKeys.length; i++) {
columns[i] = sortKeys[i].name();
}
return new MeasurementsTable(TableUtils.sort(measurements, descending, columns));
}
/**
* Retains the {@link Measurement} positioned between <code>first</code> and <code>last</code>, both inclusive,
* positions in this collection
*
* @param first the lowest index {@link Measurement} to be retained
* @param last the highest index {@link Measurement} to be retained
* @return {@link MeasurementsTable} containing specific {@link Measurement}s
*/
public MeasurementsTable slice(int first, int last) {
Table result = measurements.selectRows(first, last);
return new MeasurementsTable(result);
}
/**
* @return the first {@link Measurement}, if present
*/
public Measurement first() {
return get(0);
}
/**
* @return the last {@link Measurement}, if present
*/
public Measurement last() {
return get(measurements.rowCount() - 1);
}
/**
* @return all {@link Measurement}s in this collection
*/
public Collection<Measurement> get() {
ArrayList<Measurement> result = new ArrayList<>();
for (int i = 0; i < measurements.rowCount(); i++) {
result.add(row2Obj(i));
}
return result;
}
/**
* @param index position in the table
* @return {@link Measurement} at the requested position
*/
public Measurement get(int index) {
if (index < 0 || index >= measurements.rowCount() || measurements.isEmpty()) {
return null;
}
return row2Obj(index);
}
private Measurement row2Obj(int index) {
return new Measurement(component.get(index),
instance.get(index),
type.get(index),
Instant.ofEpochMilli(timeStamps.get(index)),
value.get(index));
}
public String toStringForDebugging() {
return measurements.print(measurements.rowCount());
}
/**
* Builds {@link MeasurementsTable} instance and provides ability to update it.
*/
public static class Builder {
private final MeasurementsTable measurementsTable = new MeasurementsTable();
public MeasurementsTable get() {
return measurementsTable;
}
public void addAll(Collection<Measurement> measurements) {
if (measurements == null) {
return;
}
this.measurementsTable.addAll(measurements);
}
}
}

Просмотреть файл

@ -0,0 +1,64 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.core;
import com.microsoft.dhalion.api.IDetector;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicInteger;
/**
* A {@link Outcome} represent result of execution of a Dhalion phase. For e.g. {@link IDetector} phase's results in
* {@link Symptom}s
*/
public abstract class Outcome {
private static final AtomicInteger idGenerator = new AtomicInteger(1);
// unique identifier of this instance
private final int id;
// outcome category identifier
private final String type;
// instant when this outcome was created
private final Instant instant;
// ids of objects to which this outcome can be attributed to, for e.g. slow instance's id
private final Collection<String> assignments = new ArrayList<>();
Outcome(String type, Instant instant, Collection<String> assignments) {
this(idGenerator.incrementAndGet(), type, instant, assignments);
}
Outcome(int id, String type, Instant instant, Collection<String> assignments) {
this.id = id;
this.type = type;
this.instant = instant;
if (assignments != null) {
this.assignments.addAll(assignments);
}
}
public int id() {
return id;
}
public String type() {
return type;
}
public Instant instant() {
return instant;
}
public Collection<String> assignments() {
return assignments;
}
}

Просмотреть файл

@ -0,0 +1,197 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.core;
import tech.tablesaw.api.CategoryColumn;
import tech.tablesaw.api.IntColumn;
import tech.tablesaw.api.LongColumn;
import tech.tablesaw.api.Table;
import tech.tablesaw.filtering.Filter;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import static tech.tablesaw.api.QueryHelper.column;
import static tech.tablesaw.api.QueryHelper.or;
//TODO thread safety
/**
* An ordered collection of {@link Outcome} instances. This class provides methods to filter, query and aggregate the
* {@link Outcome} instances.
*/
public abstract class OutcomeTable<T extends Outcome> {
private final Table table;
final CategoryColumn type;
final IntColumn id;
final CategoryColumn assignment;
final LongColumn timeStamp;
private static final String ID = SortKey.ID.name();
private static final String ASSIGNMENT = SortKey.ASSIGNMENT.name();
private static final String TIME_STAMP = SortKey.TIME_STAMP.name();
private static final String TYPE = SortKey.TYPE.name();
static final Collection<String> EMPTY_ASSIGNMENT = Collections.singletonList(CategoryColumn.MISSING_VALUE);
public enum SortKey {
ID, ASSIGNMENT, TIME_STAMP, TYPE
}
OutcomeTable(String name) {
id = new IntColumn(ID);
assignment = new CategoryColumn(ASSIGNMENT);
type = new CategoryColumn(TYPE);
timeStamp = new LongColumn(TIME_STAMP);
table = Table.create(name);
table.addColumn(id);
table.addColumn(assignment);
table.addColumn(type);
table.addColumn(timeStamp);
}
OutcomeTable(Table table) {
this.table = table;
id = this.table.intColumn(ID);
assignment = this.table.categoryColumn(ASSIGNMENT);
type = this.table.categoryColumn(TYPE);
timeStamp = this.table.longColumn(TIME_STAMP);
}
protected final void add(Outcome outcome) {
Collection<String> assignments = outcome.assignments().isEmpty() ? EMPTY_ASSIGNMENT : outcome.assignments();
assignments.forEach(assignedComponent -> {
id.append(outcome.id());
assignment.append(assignedComponent);
type.append(outcome.type());
timeStamp.append(outcome.instant().toEpochMilli());
});
}
Table filterId(int id) {
return table.selectWhere(column(ID).isEqualTo(id));
}
Table filterType(Collection<String> types) {
return applyCategoryFilter(types, TYPE);
}
Table filterType(String type) {
return filterType(Collections.singletonList(type));
}
Table filterAssignment(Collection<String> assignments) {
return applyCategoryFilter(assignments, ASSIGNMENT);
}
Table filterAssignment(String assignment) {
return filterAssignment(Collections.singletonList(assignment));
}
private Table applyCategoryFilter(Collection<String> names, String column) {
List<Filter> filters = names.stream().map(name -> column(column).isEqualTo(name)).collect(Collectors.toList());
return table.selectWhere(or(filters));
}
Table filterTime(Instant oldest, Instant newest) {
return TableUtils.filterTime(table, TIME_STAMP, oldest, newest);
}
/**
* @return count of {@link Outcome} rows in this collection
*/
public int size() {
return table.rowCount();
}
/**
* @return unique ids in this collection
*/
public Collection<Integer> uniqueIds() {
ArrayList<Integer> result = new ArrayList<>();
IntColumn uniqueColumn = id.unique();
for (int id : uniqueColumn) {
result.add(id);
}
return result;
}
/**
* @return unique {@link Outcome} types in this collection
*/
public Collection<String> uniqueTypes() {
return TableUtils.uniqueCategory(type);
}
/**
* @return unique {@link Instant}s at which {@link Outcome} objects were created
*/
public Collection<Instant> uniqueInstants() {
return TableUtils.uniqueInstants(timeStamp);
}
Table sortTable(boolean descending, SortKey... sortKeys) {
String[] columns = new String[sortKeys.length];
for (int i = 0; i < sortKeys.length; i++) {
columns[i] = sortKeys[i].name();
}
return TableUtils.sort(table, descending, columns);
}
Table sliceTable(int first, int last) {
return table.selectRows(first, last);
}
/**
* @return the first {@link Outcome} in this collection, if present
*/
public T first() {
return get(0);
}
/**
* @return the last {@link Outcome} in this collection, if present
*/
public T last() {
return get(table.rowCount() - 1);
}
/**
* @return all {@link Outcome} objects in this collection
*/
public Collection<T> get() {
ArrayList<T> result = new ArrayList<>();
for (int i = 0; i < table.rowCount(); i++) {
result.add(row2Obj(i));
}
return result;
}
/**
* @param index position in the table
* @return {@link Outcome} at the requested position
*/
public T get(int index) {
if (index < 0 || index >= table.rowCount() || table.isEmpty()) {
return null;
}
return row2Obj(index);
}
abstract T row2Obj(int index);
public String toStringForDebugging() {
return table.print(table.rowCount());
}
}

Просмотреть файл

@ -0,0 +1,63 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.core;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
/**
* A {@link Symptom} identifies an anomaly or a potential health issue in a specific component of a
* distributed application. For e.g. identification of irregular processing latency.
*/
public class Symptom extends Outcome {
// measurements referred to create this instance
private final Collection<Measurement> measurements = new ArrayList<>();
public Symptom(String type, Instant instant, Collection<String> assignments) {
this(type, instant, assignments, null);
}
public Symptom(String symptomType,
Instant instant,
Collection<String> assignments,
Collection<Measurement> measurements) {
super(symptomType, instant, assignments);
if (measurements != null) {
this.measurements.addAll(measurements);
}
}
Symptom(int id,
String symptomType,
Instant instant,
Collection<String> assignments,
Collection<Measurement> measurements) {
super(id, symptomType, instant, assignments);
if (measurements != null) {
this.measurements.addAll(measurements);
}
}
/**
* @return {@link Measurement}s referred to when this {@link Symptom} was created
*/
public Collection<Measurement> measurements() {
return measurements;
}
@Override
public String toString() {
return "Symptom{" +
"type=" + type() +
", id=" + id() +
", instant=" + instant() +
", assignments=" + assignments() +
'}';
}
}

Просмотреть файл

@ -0,0 +1,152 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.core;
import tech.tablesaw.api.Table;
import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
//TODO thread safety
/**
* An ordered collection of {@link Symptom}s. It provides methods to filter, query and aggregate the
* {@link Symptom}s.
*/
public class SymptomsTable extends OutcomeTable<Symptom> {
private SymptomsTable() {
super("Symptoms");
}
private SymptomsTable(Table table) {
super(table);
}
/**
* @param symptoms collections of symptoms
* @return a {@link SymptomsTable} holding the input
*/
public static SymptomsTable of(Collection<Symptom> symptoms) {
SymptomsTable table = new SymptomsTable();
table.addAll(symptoms);
return table;
}
private void addAll(Collection<Symptom> symptoms) {
symptoms.forEach(this::add);
}
/**
* @param id unique symptom id
* @return {@link Symptom}s with the given id
*/
public SymptomsTable id(int id) {
Table result = filterId(id);
return new SymptomsTable(result);
}
/**
* Retains all {@link Symptom}s with given symptom type
*
* @param types names of the symptom types, not null
* @return {@link SymptomsTable} containing filtered {@link Symptom}s
*/
public SymptomsTable type(Collection<String> types) {
return new SymptomsTable(filterType(types));
}
/**
* @param type a symptom type
* @return {@link SymptomsTable} containing filtered {@link Symptom}s
* @see #type(Collection)
*/
public SymptomsTable type(String type) {
return type(Collections.singletonList(type));
}
/**
* Retains all {@link Symptom}s with given assignment ids.
*
* @param assignments assignment ids, not null
* @return {@link SymptomsTable} containing filtered {@link Symptom}s
*/
public SymptomsTable assignment(Collection<String> assignments) {
return new SymptomsTable(filterAssignment(assignments));
}
/**
* @param assignment assignment id
* @return {@link SymptomsTable} containing filtered {@link Symptom}s
* @see #assignment(Collection)
*/
public SymptomsTable assignment(String assignment) {
return assignment(Collections.singletonList(assignment));
}
/**
* Retains all {@link Symptom}s with timestamp in the given range.
*
* @param oldest the oldest timestamp, null to ignore this condition
* @param newest the newest timestamp, null to ignore this condition
* @return {@link SymptomsTable} containing filtered {@link Symptom}s
*/
public SymptomsTable between(Instant oldest, Instant newest) {
return new SymptomsTable(filterTime(oldest, newest));
}
/**
* Sorts the {@link Symptom}s in this collection in the order of the specified keys
*
* @param descending false for ascending order, true for descending
* @param sortKeys one or more sort keys, e.g. {@link SortKey#ID}
* @return ordered {@link Symptom}s
*/
public SymptomsTable sort(boolean descending, SortKey... sortKeys) {
return new SymptomsTable(sortTable(descending, sortKeys));
}
/**
* Retains the {@link Symptom} positioned between <code>first</code> and <code>last</code>, both inclusive,
* positions in this collection
*
* @param first the lowest index {@link Symptom} to be retained
* @param last the highest index {@link Symptom} to be retained
* @return {@link SymptomsTable} containing specific {@link Symptom}s
*/
public SymptomsTable slice(int first, int last) {
return new SymptomsTable(sliceTable(first, last));
}
Symptom row2Obj(int index) {
return new Symptom(id.get(index),
type.get(index),
Instant.ofEpochMilli(timeStamp.get(index)),
Collections.singletonList(assignment.get(index)),
null);
}
/**
* Builds {@link SymptomsTable} instance and provides ability to update it.
*/
public static class Builder {
private final SymptomsTable symptomsTable = new SymptomsTable();
public SymptomsTable get() {
return symptomsTable;
}
public void addAll(Collection<Symptom> symptoms) {
if (symptoms == null) {
return;
}
this.symptomsTable.addAll(symptoms);
}
}
}

Просмотреть файл

@ -0,0 +1,68 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.core;
import tech.tablesaw.api.CategoryColumn;
import tech.tablesaw.api.LongColumn;
import tech.tablesaw.api.Table;
import tech.tablesaw.columns.ColumnReference;
import tech.tablesaw.filtering.Filter;
import tech.tablesaw.filtering.LongGreaterThanOrEqualTo;
import tech.tablesaw.filtering.LongLessThanOrEqualTo;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import static tech.tablesaw.api.QueryHelper.allOf;
class TableUtils {
static Table sort(Table table, boolean descending, String[] columns) {
Table result;
if (descending) {
result = table.sortDescendingOn(columns);
} else {
result = table.sortAscendingOn(columns);
}
return result;
}
static Table filterTime(Table table, String column, Instant oldest, Instant newest) {
List<Filter> filters = new ArrayList<>();
if (oldest != null) {
filters.add(new LongGreaterThanOrEqualTo(new ColumnReference(column), oldest.toEpochMilli()));
}
if (newest != null) {
filters.add(new LongLessThanOrEqualTo(new ColumnReference(column), newest.toEpochMilli()));
}
if (filters.isEmpty()) {
return table;
}
return table.selectWhere(allOf(filters));
}
static Collection<Instant> uniqueInstants(LongColumn timeStamps) {
ArrayList<Instant> result = new ArrayList<>();
LongColumn uniqueColumn = timeStamps.unique();
for (Long ts : uniqueColumn) {
result.add(Instant.ofEpochMilli(ts));
}
return result;
}
static Collection<String> uniqueCategory(CategoryColumn column) {
ArrayList<String> result = new ArrayList<>();
CategoryColumn uniqueColumn = column.unique();
uniqueColumn.forEach(result::add);
return result;
}
}

Просмотреть файл

@ -1,67 +0,0 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.detector;
import com.microsoft.dhalion.metrics.ComponentMetrics;
import com.microsoft.dhalion.metrics.MetricsStats;
import java.util.HashMap;
import java.util.Map;
/**
* A {@link Symptom} identifies an anomaly or a potential health issue in a specific component of a
* distributed application. For e.g. identification of irregular processing latency.
*/
public class Symptom {
private String symptomName;
private ComponentMetrics metrics = new ComponentMetrics();
private Map<String, MetricsStats> stats = new HashMap<>();
public Symptom(String symptomName) {
this(symptomName, null);
}
public Symptom(String symptomName, ComponentMetrics metrics) {
this(symptomName, metrics, null);
}
public Symptom(String symptomName, ComponentMetrics metrics, MetricsStats stats) {
this.symptomName = symptomName;
this.metrics = metrics;
// TODO optimize stats structure like ComponentMetrics
// addStats(metrics.getComponentName(), stats);
}
public synchronized void addComponentMetrics(ComponentMetrics metrics) {
this.metrics = ComponentMetrics.merge(metrics, this.metrics);
}
public synchronized void addStats(String componentName, MetricsStats componentStats) {
this.stats.put(componentName, componentStats);
}
public String getSymptomName() {
return symptomName;
}
public ComponentMetrics getComponentMetrics() {
return metrics;
}
public Map<String, MetricsStats> getStats() {
return stats;
}
@Override
public String toString() {
return "Symptom{" +
"symptomName='" + symptomName + '\'' +
", metrics=" + metrics +
", stats=" + stats +
'}';
}
}

Просмотреть файл

@ -1,52 +0,0 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.diagnoser;
import java.util.HashMap;
import java.util.Map;
import com.microsoft.dhalion.detector.Symptom;
/**
* A {@link Diagnosis} instance is a representation of a possible causes of one or more
* {@link Symptom}s. A {@link Symptom} could result in creation of one or more {@link Diagnosis}.
* Similarly, correlated {@link Symptom}s can result in generation of a {@link Diagnosis} instance.
*/
public class Diagnosis {
private String name;
private Map<String, Symptom> symptoms;
public Diagnosis(String diagnosisName) {
this(diagnosisName, new HashMap<>());
}
public Diagnosis(String diagnosisName, Symptom symptom) {
this(diagnosisName, new HashMap<>());
symptoms.put(symptom.getSymptomName(), symptom);
}
public Diagnosis(String diagnosisName, Map<String, Symptom> correlatedSymptoms) {
this.name = diagnosisName;
this.symptoms = correlatedSymptoms;
}
public String getName() {
return name;
}
public Map<String, Symptom> getSymptoms() {
return symptoms;
}
@Override
public String toString() {
return "Diagnosis{" +
"name='" + name + '\'' +
", symptoms=" + symptoms +
'}';
}
}

Просмотреть файл

@ -1,286 +0,0 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.metrics;
import com.microsoft.dhalion.common.DuplicateMetricException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
/**
* {@link ComponentMetrics} is a collection of {@link InstanceMetrics} objects organized in a 2D space. This class holds
* metric information for all instances of all components. The dimensions are metric name and component name. This
* class provides methods to filter {@link InstanceMetrics} objects by along either of the two dimensions.
*/
public class ComponentMetrics {
//Map for the component name dimension
private HashMap<String, Set<InstanceMetricWrapper>> componentDim = new HashMap<>();
//Map for the metric name dimension
private HashMap<String, Set<InstanceMetricWrapper>> metricsDim = new HashMap<>();
//Set of all metrics managed by this object
private Set<InstanceMetricWrapper> allMetric = new HashSet<>();
public synchronized void addAll(Collection<InstanceMetrics> metrics) {
metrics.forEach(this::add);
}
public synchronized void add(InstanceMetrics metric) {
InstanceMetricWrapper wrappedMetric = new InstanceMetricWrapper(metric);
if (allMetric.contains(wrappedMetric)) {
throw new DuplicateMetricException(metric.getComponentName(), metric.getInstanceName(), metric.getMetricName());
}
allMetric.add(wrappedMetric);
componentDim.computeIfAbsent(metric.getComponentName(), k -> new HashSet<>()).add(wrappedMetric);
metricsDim.computeIfAbsent(metric.getMetricName(), k -> new HashSet<>()).add(wrappedMetric);
}
public void addMetric(String component, String instance, String metricName, double value) {
InstanceMetrics metric = new InstanceMetrics(component, instance, metricName);
metric.addValue(value);
add(metric);
}
public void addMetric(String component, String instance, String metricName, Instant time, double value) {
InstanceMetrics metric = new InstanceMetrics(component, instance, metricName);
metric.addValue(time, value);
add(metric);
}
/**
* @param componentName component name to be used for filtering metrics
* @return a new {@link ComponentMetrics} instance containing all {@link InstanceMetrics}s belonging to {@code
* componentName} only.
*/
public ComponentMetrics filterByComponent(String componentName) {
final ComponentMetrics result = new ComponentMetrics();
Set<InstanceMetricWrapper> metrics = componentDim.get(componentName);
if (metrics != null) {
metrics.forEach(wrapper -> result.add(wrapper.metric));
}
return result;
}
/**
* @param metricName metric name to be used for filtering metrics
* @return a new {@link ComponentMetrics} instance containing all {@link InstanceMetrics}s belonging to {@code
* metricName} only.
*/
public ComponentMetrics filterByMetric(String metricName) {
final ComponentMetrics result = new ComponentMetrics();
Set<InstanceMetricWrapper> metrics = metricsDim.get(metricName);
if (metrics != null) {
metrics.forEach(wrapper -> result.add(wrapper.metric));
}
return result;
}
/**
* @param componentName component name to be used for filtering metrics
* @param instanceName instance name to be used for filtering metrics
* @return a new {@link ComponentMetrics} instance containing all {@link InstanceMetrics}s belonging to {@code
* componentName/instanceName} only.
*/
public ComponentMetrics filterByInstance(String componentName, String instanceName) {
final ComponentMetrics result = new ComponentMetrics();
Collection<InstanceMetrics> metrics = filterByComponent(componentName).getMetrics();
metrics.stream()
.filter(metric -> metric.getInstanceName().equals(instanceName))
.forEach(result::add);
return result;
}
/**
* @param instanceName instance name to be used for filtering metrics
* @return a new {@link ComponentMetrics} instance containing all {@link InstanceMetrics}s belonging to {@code
* componentName/instanceName} only.
*/
public ComponentMetrics filterByInstance(String instanceName) {
final ComponentMetrics result = new ComponentMetrics();
allMetric.stream()
.map(wrapper -> wrapper.metric)
.filter(metric -> metric.getInstanceName().equals(instanceName))
.forEach(result::add);
return result;
}
/**
* @param componentName name of the component
* @param instanceName name of the instance
* @param metricName name of the metric
* @return a unique {@link InstanceMetrics} if exists
*/
public Optional<InstanceMetrics> getMetrics(String componentName, String instanceName, String metricName) {
Collection<InstanceMetrics> metrics =
filterByInstance(componentName, instanceName).filterByMetric(metricName).getMetrics();
if (metrics.size() > 1) {
throw new DuplicateMetricException(componentName, instanceName, metricName);
}
if (metrics.isEmpty()) {
return Optional.empty();
}
return Optional.of(metrics.iterator().next());
}
/**
* Searches for a {@link InstanceMetrics} with the same name-metric-component as the provided instance.
*
* @return a unique {@link InstanceMetrics} if exists
*/
public Optional<InstanceMetrics> getMetrics(InstanceMetrics instance) {
return getMetrics(instance.getComponentName(), instance.getInstanceName(), instance.getMetricName());
}
/**
* @return all {@link InstanceMetrics} managed by this {@link ComponentMetrics} object
*/
public Collection<InstanceMetrics> getMetrics() {
final Collection<InstanceMetrics> result = new ArrayList<>();
allMetric.forEach(wrapper -> result.add(wrapper.metric));
return result;
}
/**
* @return count of metrics
*/
public int size() {
return allMetric.size();
}
/**
* @return true if no {@link InstanceMetrics} are present
*/
public boolean isEmpty() {
return size() == 0;
}
/**
* @return returns the only {@link InstanceMetrics} managed by this {@link ComponentMetrics}
*/
public Optional<InstanceMetrics> getLoneInstanceMetrics() {
if (allMetric.size() > 1) {
throw new IllegalArgumentException("More than 1 metrics available, count = " + allMetric.size());
}
if (allMetric.isEmpty()) {
return Optional.empty();
}
return Optional.of(allMetric.iterator().next().metric);
}
/**
* @return unique names of all metrics present in this {@link ComponentMetrics}
*/
public Collection<String> getMetricNames() {
final Collection<String> result = new ArrayList<>();
result.addAll(metricsDim.keySet());
return result;
}
/**
* @return unique names of all components present in this {@link ComponentMetrics}
*/
public Collection<String> getComponentNames() {
final Collection<String> result = new ArrayList<>();
result.addAll(componentDim.keySet());
return result;
}
/**
* @return count of components
*/
public int getComponentCount() {
return componentDim.size();
}
public MetricsStats computeStats(String metric) {
double metricMax = 0;
double metricMin = Double.MAX_VALUE;
double sum = 0;
double metricAvg = 0;
Collection<InstanceMetrics> metrics = filterByMetric(metric).getMetrics();
for (InstanceMetrics instance : metrics) {
Double metricValue = instance.getValueSum();
if (metricValue == null) {
continue;
}
metricMax = metricMax < metricValue ? metricValue : metricMax;
metricMin = metricMin > metricValue ? metricValue : metricMin;
sum += metricValue;
}
metricAvg = sum / metrics.size();
return new MetricsStats(metric, metricMin, metricMax, metricAvg);
}
/**
* Merges {@link InstanceMetrics}s in two different {@link ComponentMetrics}. Input objects are not modified. This
* is a utility method two merge two different metrics. The method will fail if both the input objects contain
* metrics for the same {@link InstanceMetrics}.
*
* @return A new {@link ComponentMetrics} instance
*/
public static ComponentMetrics merge(ComponentMetrics data1, ComponentMetrics data2) {
ComponentMetrics mergedData = new ComponentMetrics();
if (data1 != null) {
mergedData.addAll(data1.getMetrics());
}
if (data2 != null) {
mergedData.addAll(data2.getMetrics());
}
return mergedData;
}
private class InstanceMetricWrapper {
private final InstanceMetrics metric;
InstanceMetricWrapper(InstanceMetrics metric) {
this.metric = metric;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InstanceMetricWrapper that = (InstanceMetricWrapper) o;
return metric.getComponentName().equals(that.metric.getComponentName())
&& metric.getInstanceName().equals(that.metric.getInstanceName())
&& metric.getMetricName().equals(that.metric.getMetricName());
}
@Override
public int hashCode() {
int result = metric.getComponentName().hashCode();
result = 31 * result + metric.getInstanceName().hashCode();
result = 31 * result + metric.getMetricName().hashCode();
return result;
}
}
@Override
public String toString() {
return "ComponentMetrics{" +
"allMetric=" + allMetric +
'}';
}
}

Просмотреть файл

@ -1,148 +0,0 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.metrics;
import com.microsoft.dhalion.common.DuplicateMetricException;
import com.microsoft.dhalion.common.InstanceInfo;
import java.time.Instant;
import java.util.Collection;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* An {@link InstanceMetrics} holds metric information of a specific metric for instance of a component.
*/
public class InstanceMetrics extends InstanceInfo {
// id of the component
private final String metricName;
//metric values at different times
private Map<Instant, Double> metrics = new TreeMap<>();
/**
* @param componentName name/id of a component, not null
* @param instanceName name/id of a instance, not null
* @param metricName name/id of a metric, not null
*/
public InstanceMetrics(String componentName, String instanceName, String metricName) {
super(componentName, instanceName);
this.metricName = metricName;
}
/**
* Adds multiple instant-value pairs. The operation fails if a instant to be added already exists.
*
* @param values values to be added, not null
*/
public void addValues(Map<Instant, Double> values) {
values.entrySet().stream()
.filter(entry -> metrics.containsKey(entry.getKey())).findAny()
.ifPresent(x -> {
throw new DuplicateMetricException(componentName, instanceName, metricName);
});
metrics.putAll(values);
}
/**
* Adds a metric and its value for the instance. This is a shorthand method for
* {@link InstanceMetrics#addValues} method. The assumption is that the metric will have only one
* value.
*
* @param value metric value
*/
public void addValue(double value) {
metrics.put(Instant.now(), value);
}
/**
* Adds a instant-value pair. The operation fails if a instant to be added already exists.
*
* @param time instant at which metric was recorded
* @param value value of the metric
*/
public void addValue(Instant time, double value) {
if (metrics.containsKey(time)) {
throw new DuplicateMetricException(componentName, instanceName, metricName);
}
metrics.put(time, value);
}
/**
* Reads metric values from the other instance and adds it to this instance. The operation will fail if the instances
* have different metricn, component or instance name. It will also fail if both instances have a value for the
* same instant value.
*
* @param o other instance of {@link InstanceMetrics}, not null
*/
public void merge(InstanceMetrics o) {
if (!metricName.equals(o.metricName)) {
throw new IllegalArgumentException(String.format("Metric name mismatch: %s vs %s", metricName, o.metricName));
}
if (!componentName.equals(o.componentName)) {
throw new IllegalArgumentException(String.format("Component name mismatch: %s vs %s", metricName, o.metricName));
}
if (!instanceName.equals(o.instanceName)) {
throw new IllegalArgumentException(String.format("Instance name mismatch: %s vs %s", metricName, o.metricName));
}
o.getValues().entrySet().stream()
.filter(entry -> metrics.containsKey(entry.getKey())).findAny()
.ifPresent(x -> {
throw new DuplicateMetricException(componentName, instanceName, metricName);
});
metrics.putAll(o.metrics);
}
public Map<Instant, Double> getMostRecentValues(int noRecentValues) {
Map<Instant, Double> recentValues;
recentValues = (Map<Instant, Double>) ((TreeMap) metrics).descendingMap().keySet().stream().limit(noRecentValues)
.collect(Collectors.toMap(Function.identity(), instant -> metrics.get(instant)));
return recentValues;
}
public TreeSet<Instant> getMostRecentTimestamps(int noRecentValues) {
TreeSet<Instant> recentTimestamps;
recentTimestamps = new TreeSet((Collection) ((TreeMap) metrics).descendingMap().keySet().stream()
.limit(noRecentValues).collect(Collectors.toSet()));
return recentTimestamps;
}
public Map<Instant, Double> getValues() {
return metrics;
}
public String getMetricName() {
return metricName;
}
public Double getValueSum() {
return metrics.values().stream().mapToDouble(x -> x.doubleValue()).sum();
}
public Double getValueSum(int noRecentValues) {
return getMostRecentValues(noRecentValues).values().stream().mapToDouble(x -> x.doubleValue()).sum();
}
public boolean hasValueAboveLimit(double limit) {
return metrics.values().stream().anyMatch(x -> x > limit);
}
@Override
public String toString() {
return "InstanceMetrics{" +
"metricName='" + metricName + '\'' +
", metrics=" + metrics +
", componentName='" + componentName + '\'' +
", instanceName='" + instanceName + '\'' +
'}';
}
}

Просмотреть файл

@ -1,58 +0,0 @@
// Copyright 2016 Twitter. All rights reserved.
//
// 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.microsoft.dhalion.metrics;
public class MetricsStats {
private String metricName;
private double metricMax = 0;
private double metricMin = Double.MAX_VALUE;
private double metricAvg = 0;
public MetricsStats(String metricName, double metricMin, double metricMax, double metricSum) {
this.metricName = metricName;
this.metricMax = metricMax;
this.metricMin = metricMin;
this.metricAvg = metricSum;
}
public double getMetricAvg() {
return metricAvg;
}
public double getMetricMax() {
return metricMax;
}
public double getMetricMin() {
return metricMin;
}
public void setMetricAvg(double metricSum) {
this.metricAvg = metricSum;
}
public void setMetricMax(double metricMax) {
this.metricMax = metricMax;
}
public void setMetricMin(double metricMin) {
this.metricMin = metricMin;
}
public String getMetricName() {
return metricName;
}
}

Просмотреть файл

@ -13,130 +13,139 @@ import com.microsoft.dhalion.api.IDiagnoser;
import com.microsoft.dhalion.api.IHealthPolicy;
import com.microsoft.dhalion.api.IResolver;
import com.microsoft.dhalion.api.ISensor;
import com.microsoft.dhalion.detector.Symptom;
import com.microsoft.dhalion.diagnoser.Diagnosis;
import com.microsoft.dhalion.resolver.Action;
import com.microsoft.dhalion.state.MetricsState;
import com.microsoft.dhalion.core.Action;
import com.microsoft.dhalion.core.Diagnosis;
import com.microsoft.dhalion.core.Measurement;
import com.microsoft.dhalion.core.Symptom;
import com.microsoft.dhalion.policy.PoliciesExecutor.ExecutionContext;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.Objects;
public class HealthPolicyImpl implements IHealthPolicy {
protected List<ISensor> sensors = new ArrayList<>();
protected List<IDetector> detectors = new ArrayList<>();
protected List<IDiagnoser> diagnosers = new ArrayList<>();
protected List<IResolver> resolvers = new ArrayList<>();
protected Collection<ISensor> sensors = new ArrayList<>();
protected Collection<IDetector> detectors = new ArrayList<>();
protected Collection<IDiagnoser> diagnosers = new ArrayList<>();
protected Collection<IResolver> resolvers = new ArrayList<>();
protected Duration interval = Duration.ofMinutes(1);
private Instant lastExecutionTimestamp;
private Instant oneTimeDelay = null;
private ExecutionContext executionContext;
protected long intervalMillis = TimeUnit.MINUTES.toMillis(1);
@VisibleForTesting
ClockTimeProvider clock = new ClockTimeProvider();
private long lastExecutionTimeMills = 0;
private long oneTimeDelayTimestamp = 0;
@Override
public void initialize(List<ISensor> sensors, List<IDetector> detectors, List<IDiagnoser>
diagnosers, List<IResolver> resolvers) {
this.sensors = sensors;
this.detectors = detectors;
this.diagnosers = diagnosers;
this.resolvers = resolvers;
public void initialize(ExecutionContext context) {
this.executionContext = context;
sensors.forEach(sensor -> sensor.initialize(executionContext));
detectors.forEach(detector -> detector.initialize(executionContext));
diagnosers.forEach(diagnoser -> diagnoser.initialize(executionContext));
resolvers.forEach(resolver -> resolver.initialize(executionContext));
}
public void registerSensors(ISensor... sensors) {
if (sensors == null) {
throw new IllegalArgumentException("Null instance cannot be added.");
}
Arrays.stream(sensors).forEach(sensor -> this.sensors.add(sensor));
this.sensors.addAll(Arrays.asList(sensors));
}
public void registerDetectors(IDetector... detectors) {
if (detectors == null) {
throw new IllegalArgumentException("Null instance cannot be added.");
}
Arrays.stream(detectors).forEach(detector -> this.detectors.add(detector));
this.detectors.addAll(Arrays.asList(detectors));
}
public void registerDiagnosers(IDiagnoser... diagnosers) {
if (diagnosers == null) {
throw new IllegalArgumentException("Null instance cannot be added.");
}
Arrays.stream(diagnosers).forEach(diagnoser -> this.diagnosers.add(diagnoser));
this.diagnosers.addAll(Arrays.asList(diagnosers));
}
public void registerResolvers(IResolver... resolvers) {
if (resolvers == null) {
throw new IllegalArgumentException("Null instance cannot be added.");
}
Arrays.stream(resolvers).forEach(resolver -> this.resolvers.add(resolver));
this.resolvers.addAll(Arrays.asList(resolvers));
}
/**
* @param unit the delay unit
* @param value the delay after which this policy will be re-executed. For a one-time policy, the value will be 0 or
* negative
* @param value the delay after which this policy will be re-executed. For a one-time policy
*/
public void setPolicyExecutionInterval(TimeUnit unit, long value) {
value = unit.toMillis(value);
if (value <= 0) {
value = Long.MAX_VALUE;
}
this.intervalMillis = unit.toMillis(value);
public void setPolicyExecutionInterval(Duration value) {
this.interval = value;
}
/**
* Sets the delay before next execution of this policy. This one time delay value overrides the original policy
* execution interval, set using {@link HealthPolicyImpl#setPolicyExecutionInterval}. All subsequent policy
* execution will take place using the original delay value.
* One-time delay defers policy execution. One-time delay is used when the system is expected to be unstable,
* typically after an {@link Action}. One-time delay value overrides the original policy execution interval. Policy
* execution will resume after the set delay has elapsed.
*
* @param unit the delay unit
* @param value the delay value
* @see HealthPolicyImpl#setPolicyExecutionInterval
*/
public void setOneTimeDelay(TimeUnit unit, long value) {
oneTimeDelayTimestamp = clock.currentTimeMillis() + unit.toMillis(value);
public void setOneTimeDelay(Duration value) {
oneTimeDelay = clock.now().plus(value);
}
@Override
public void executeSensors(MetricsState metricsState) {
public Collection<Measurement> executeSensors() {
Collection<Measurement> measurements = new ArrayList<>();
if (sensors == null) {
return;
return measurements;
}
sensors.stream().forEach(sensor -> metricsState.addMetricsAndStats(sensor.fetchMetrics(), sensor.readStats()));
sensors.stream().map(ISensor::fetch)
.filter(Objects::nonNull)
.forEach(measurements::addAll);
return measurements;
}
@Override
public List<Symptom> executeDetectors() {
public Collection<Symptom> executeDetectors(Collection<Measurement> measurements) {
List<Symptom> symptoms = new ArrayList<>();
if (detectors == null) {
return symptoms;
}
symptoms = detectors.stream().map(detector -> detector.detect())
.filter(detectedSymptoms -> detectedSymptoms != null)
.flatMap(List::stream).collect(Collectors.toList());
detectors.stream().map(detector -> detector.detect(measurements))
.filter(Objects::nonNull)
.forEach(symptoms::addAll);
return symptoms;
}
@Override
public List<Diagnosis> executeDiagnosers(List<Symptom> symptoms) {
public Collection<Diagnosis> executeDiagnosers(Collection<Symptom> symptoms) {
List<Diagnosis> diagnosis = new ArrayList<>();
if (diagnosers == null) {
return diagnosis;
}
diagnosis = diagnosers.stream().map(diagnoser -> diagnoser.diagnose(symptoms))
.filter(diagnoses -> diagnoses != null).flatMap(x -> x.stream())
.collect(Collectors.toList());
diagnosers.stream().map(diagnoser -> diagnoser.diagnose(symptoms))
.filter(Objects::nonNull)
.forEach(diagnosis::addAll);
return diagnosis;
}
@Override
public IResolver selectResolver(List<Diagnosis> diagnosis) {
protected IResolver selectResolver(Collection<Diagnosis> diagnosis) {
if (resolvers == null) {
return null;
}
@ -145,72 +154,51 @@ public class HealthPolicyImpl implements IHealthPolicy {
}
@Override
public List<Action> executeResolver(IResolver resolver, List<Diagnosis> diagnosis) {
if (oneTimeDelayTimestamp > 0 && oneTimeDelayTimestamp <= clock.currentTimeMillis()) {
public Collection<Action> executeResolvers(Collection<Diagnosis> diagnosis) {
if (oneTimeDelay != null && !oneTimeDelay.isAfter(clock.now())) {
// reset one time delay timestamp
oneTimeDelayTimestamp = 0;
oneTimeDelay = null;
}
List<Action> actions = new ArrayList<>();
IResolver resolver = selectResolver(diagnosis);
Collection<Action> actions = new ArrayList<>();
if (resolver != null) {
actions = resolver.resolve(diagnosis);
}
lastExecutionTimeMills = clock.currentTimeMillis();
lastExecutionTimestamp = clock.now();
return actions;
}
@Override
public long getDelay(TimeUnit unit) {
public Duration getDelay() {
long delay;
if (lastExecutionTimeMills <= 0) {
if (lastExecutionTimestamp == null) {
// first time execution of the policy will start immediately.
delay = 0;
} else if (oneTimeDelayTimestamp > 0) {
delay = oneTimeDelayTimestamp - clock.currentTimeMillis();
} else if (oneTimeDelay != null) {
delay = oneTimeDelay.toEpochMilli() - clock.now().toEpochMilli();
} else {
delay = lastExecutionTimeMills + intervalMillis - clock.currentTimeMillis();
delay = lastExecutionTimestamp.plus(interval).toEpochMilli() - clock.now().toEpochMilli();
}
delay = delay < 0 ? 0 : delay;
return unit.convert(delay, TimeUnit.MILLISECONDS);
return Duration.ofMillis(delay);
}
@Override
public void close() {
if (sensors != null) {
sensors.forEach(value -> value.close());
}
if (detectors != null) {
detectors.forEach(value -> value.close());
}
if (diagnosers != null) {
diagnosers.forEach(value -> value.close());
}
if (resolvers != null) {
resolvers.forEach(value -> value.close());
}
}
@Override
public List<IDetector> getDetectors() {
return this.detectors;
}
@Override
public List<IDiagnoser> getDiagnosers() {
return this.diagnosers;
}
@Override
public List<IResolver> getResolvers() {
return this.resolvers;
sensors.forEach(ISensor::close);
detectors.forEach(IDetector::close);
diagnosers.forEach(IDiagnoser::close);
resolvers.forEach(IResolver::close);
}
@VisibleForTesting
static class ClockTimeProvider {
long currentTimeMillis() {
return System.currentTimeMillis();
Instant now() {
return Instant.now();
}
}
}

Просмотреть файл

@ -7,66 +7,170 @@
package com.microsoft.dhalion.policy;
import com.microsoft.dhalion.api.IHealthPolicy;
import com.microsoft.dhalion.core.Action;
import com.microsoft.dhalion.core.ActionTable;
import com.microsoft.dhalion.core.Diagnosis;
import com.microsoft.dhalion.core.DiagnosisTable;
import com.microsoft.dhalion.core.Measurement;
import com.microsoft.dhalion.core.MeasurementsTable;
import com.microsoft.dhalion.core.Outcome;
import com.microsoft.dhalion.core.Symptom;
import com.microsoft.dhalion.core.SymptomsTable;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import com.microsoft.dhalion.api.IHealthPolicy;
import com.microsoft.dhalion.api.IResolver;
import com.microsoft.dhalion.detector.Symptom;
import com.microsoft.dhalion.diagnoser.Diagnosis;
import com.microsoft.dhalion.state.MetricsState;
public class PoliciesExecutor {
private static final Logger LOG = Logger.getLogger(PoliciesExecutor.class.getName());
private final List<IHealthPolicy> policies;
private final Map<IHealthPolicy, ExecutionContext> policyContextMap = new HashMap<>();
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
public PoliciesExecutor(List<IHealthPolicy> policies) {
this.policies = policies;
public PoliciesExecutor(Collection<IHealthPolicy> policies) {
this.policies = new ArrayList<>(policies);
for (IHealthPolicy policy : this.policies) {
ExecutionContext ctx = new ExecutionContext();
policy.initialize(ctx);
policyContextMap.put(policy, ctx);
}
}
public ScheduledFuture<?> start() {
MetricsState metricsState = new MetricsState();
ScheduledFuture<?> future = executor.scheduleWithFixedDelay(() -> {
// schedule the next execution cycle
Long nextScheduleDelay = policies.stream()
.map(x -> x.getDelay(TimeUnit.MILLISECONDS)).min(Comparator.naturalOrder()).orElse(10000l);
if (nextScheduleDelay > 0) {
Duration nextScheduleDelay = policies.stream()
.map(IHealthPolicy::getDelay)
.min(Comparator.naturalOrder())
.orElse(Duration.ofSeconds(10));
if (nextScheduleDelay.toMillis() > 0) {
try {
LOG.info("Sleep (millis) before next policy execution cycle: " + nextScheduleDelay);
TimeUnit.MILLISECONDS.sleep(nextScheduleDelay);
TimeUnit.MILLISECONDS.sleep(nextScheduleDelay.toMillis());
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting for next policy execution cycle");
}
}
for (IHealthPolicy policy : policies) {
if (policy.getDelay(TimeUnit.MILLISECONDS) > 0) {
if (policy.getDelay().toMillis() > 0) {
continue;
}
LOG.info("Executing Policy: " + policy.getClass().getSimpleName());
policy.executeSensors(metricsState);
List<Symptom> symptoms = policy.executeDetectors();
List<Diagnosis> diagnosis = policy.executeDiagnosers(symptoms);
IResolver resolver = policy.selectResolver(diagnosis);
policy.executeResolver(resolver, diagnosis);
ExecutionContext context = policyContextMap.get(policy);
context.captureCheckpoint();
Instant previous = context.previousCheckpoint;
Instant current = context.checkpoint;
// The policy execution is complete. Retain the stats, flush the metrics
metricsState.clearMetrics();
LOG.info(String.format("Executing Policy: %s, checkpoint: %s",
policy.getClass().getSimpleName(),
context.checkpoint));
Collection<Measurement> measurements = policy.executeSensors();
measurements.stream()
.filter(m -> m.instant().isAfter(current) || m.instant().isBefore(previous))
.forEach(m -> LOG.info(m.toString() + "is outside checkpoint window"));
context.measurementsTableBuilder.addAll(measurements);
Collection<Symptom> symptoms = policy.executeDetectors(measurements);
identifyOutliers(previous, current, symptoms);
context.symptomsTableBuilder.addAll(symptoms);
Collection<Diagnosis> diagnosis = policy.executeDiagnosers(symptoms);
identifyOutliers(previous, current, diagnosis);
context.diagnosisTableBuilder.addAll(diagnosis);
Collection<Action> actions = policy.executeResolvers(diagnosis);
identifyOutliers(previous, current, actions);
context.actionTableBuilder.addAll(actions);
// TODO pretty print
LOG.info(actions.toString());
// TODO delete expired objects from state store
}
}, 1, 1, TimeUnit.MILLISECONDS);
return future;
}
private void identifyOutliers(Instant previous, Instant current, Collection<? extends Outcome> outcomes) {
outcomes.stream()
.filter(m -> m.instant().isAfter(current) || m.instant().isBefore(previous))
.forEach(m -> LOG.warning(m.toString() + "is outside checkpoint window"));
}
public void destroy() {
this.executor.shutdownNow();
}
public static class ExecutionContext {
private final MeasurementsTable.Builder measurementsTableBuilder;
private final SymptomsTable.Builder symptomsTableBuilder;
private final DiagnosisTable.Builder diagnosisTableBuilder;
private final ActionTable.Builder actionTableBuilder;
private Instant checkpoint;
private Instant previousCheckpoint;
private ExecutionContext() {
measurementsTableBuilder = new MeasurementsTable.Builder();
symptomsTableBuilder = new SymptomsTable.Builder();
diagnosisTableBuilder = new DiagnosisTable.Builder();
actionTableBuilder = new ActionTable.Builder();
}
private void captureCheckpoint() {
if (checkpoint != null) {
previousCheckpoint = checkpoint;
}
checkpoint = Instant.now();
}
public MeasurementsTable measurements() {
return measurementsTableBuilder.get();
}
public SymptomsTable symptoms() {
return symptomsTableBuilder.get();
}
public DiagnosisTable diagnosis() {
return diagnosisTableBuilder.get();
}
public ActionTable actions() {
return actionTableBuilder.get();
}
/**
* A checkpoint is a timestamp at which policy execution begins. This value can be used to identify outcomes
* created during a particular cycle.
*
* @return the timestamp(checkpoint) at which this policy's current execution started
*/
public Instant checkpoint() {
return checkpoint;
}
/**
* @return the timestamp(checkpoint) at which this policy's previous execution had started
*/
public Instant previousCheckpoint() {
return previousCheckpoint;
}
}
}

Просмотреть файл

@ -1,26 +0,0 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.resolver;
import com.microsoft.dhalion.api.IResolver;
import com.microsoft.dhalion.diagnoser.Diagnosis;
/**
* {@link Action} is a representation of a action taken by {@link IResolver} to fix a
* {@link Diagnosis}
*/
public class Action {
private String name;
public Action(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

Просмотреть файл

@ -1,45 +0,0 @@
package com.microsoft.dhalion.sensor;
import com.microsoft.dhalion.api.ISensor;
import com.microsoft.dhalion.metrics.ComponentMetrics;
import com.microsoft.dhalion.metrics.MetricsStats;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
public abstract class SensorImpl implements ISensor {
private final String metricName;
protected ComponentMetrics metrics = new ComponentMetrics();
protected Map<String, MetricsStats> stats = new HashMap<>();
public SensorImpl(String metricName) {
this.metricName = metricName;
}
@Override
abstract public ComponentMetrics fetchMetrics();
@Override
public ComponentMetrics readMetrics() {
return metrics;
}
@Override
public Map<String, MetricsStats> readStats() {
return stats;
}
@Override
public Optional<MetricsStats> readStats(String component) {
if (stats == null) {
return Optional.empty();
}
return Optional.ofNullable(stats.get(component));
}
@Override
public String getMetricName() {
return metricName;
}
}

Просмотреть файл

@ -1,70 +0,0 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.state;
import com.microsoft.dhalion.common.DuplicateMetricException;
import com.microsoft.dhalion.metrics.ComponentMetrics;
import com.microsoft.dhalion.metrics.MetricsStats;
import java.util.HashMap;
import java.util.Map;
public class MetricsState {
private ComponentMetrics metricsSnapshot = new ComponentMetrics();
//Map from component name to (metric name, metric stats) for the component
private HashMap<String, HashMap<String, MetricsStats>> stats = new HashMap<>();
public void addMetricsAndStats(ComponentMetrics metrics,
Map<String, MetricsStats> componentStats) {
addMetrics(metrics);
addStats(componentStats);
}
void addMetrics(ComponentMetrics metrics) {
metricsSnapshot = ComponentMetrics.merge(metricsSnapshot, metrics);
}
private void addStats(Map<String, MetricsStats> stats) {
if (stats == null) {
return;
}
stats.forEach(this::addStats);
}
private void addStats(String component, MetricsStats inputStats) {
HashMap<String, MetricsStats> componentStats = stats.computeIfAbsent(component, k -> new HashMap<>());
String metric = inputStats.getMetricName();
if (componentStats.get(inputStats.getMetricName()) != null) {
throw new DuplicateMetricException(component, "", metric);
}
componentStats.put(metric, inputStats);
}
public ComponentMetrics getMetrics() {
return metricsSnapshot;
}
public HashMap<String, HashMap<String, MetricsStats>> getStats() {
return stats;
}
public MetricsStats getStats(String component, String metricName) {
if (stats.get(component) != null) {
return stats.get(component).get(metricName);
}
return null;
}
public void clearMetrics() {
metricsSnapshot = new ComponentMetrics();
}
}

Просмотреть файл

@ -0,0 +1,203 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.core;
import com.microsoft.dhalion.core.MeasurementsTable.Builder;
import com.microsoft.dhalion.core.MeasurementsTable.SortKey;
import org.junit.Before;
import org.junit.Test;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class MeasurementsTableTest {
private MeasurementsTable resultTable;
private MeasurementsTable testTable;
@Before
public void createTestTable() {
String[] components = {"c1", "c2", "c3"};
String[] instances = {"i1", "i2"};
String[] metrics = {"m1", "m2"};
Collection<Measurement> measurements = new ArrayList<>();
int value = 10;
for (String component : components) {
for (String instance : instances) {
for (String metric : metrics) {
measurements.add(new Measurement(component, instance, metric, Instant.ofEpochMilli(value), value));
value += 10;
measurements.add(new Measurement(component, instance, metric, Instant.ofEpochMilli(value), value));
value += 10;
}
}
}
Builder builder = new Builder();
builder.addAll(measurements);
testTable = builder.get();
}
@Test
public void component() {
resultTable = testTable.component("c1");
assertEquals(8, resultTable.size());
resultTable.get().forEach(m -> assertEquals("c1", m.component()));
resultTable = testTable.component("c2");
assertEquals(8, resultTable.size());
resultTable.get().forEach(m -> assertEquals("c2", m.component()));
resultTable = testTable.component(Arrays.asList("c2", "c3"));
assertEquals(16, resultTable.size());
resultTable.get().forEach(m -> assertTrue("c2".equals(m.component()) || "c3".equals(m.component())));
}
@Test
public void metric() {
resultTable = testTable.type("m1");
assertEquals(12, resultTable.size());
resultTable.get().forEach(m -> assertEquals("m1", m.type()));
}
@Test
public void instance() {
resultTable = testTable.instance("i1");
assertEquals(12, resultTable.size());
resultTable.get().forEach(m -> assertEquals("i1", m.instance()));
}
@Test
public void between() {
Instant oldest = Instant.ofEpochMilli(60);
Instant newest = Instant.ofEpochMilli(70);
resultTable = testTable.between(oldest, newest);
assertEquals(2, resultTable.size());
resultTable.get().forEach(m -> assertTrue(60 <= m.instant().toEpochMilli()));
resultTable.get().forEach(m -> assertTrue(70 >= m.instant().toEpochMilli()));
}
@Test
public void max() {
assertEquals(240, testTable.max(), 0.01);
}
@Test
public void min() {
assertEquals(10, testTable.min(), 0.01);
}
@Test
public void mean() {
assertEquals(125, testTable.mean(), 0.01);
}
@Test
public void median() {
assertEquals(125, testTable.median(), 0.01);
}
@Test
public void variance() {
assertEquals(600, testTable.component("c1").variance(), 0.01);
}
@Test
public void get() {
Collection<Measurement> result = testTable.get();
assertEquals(24, result.size());
}
@Test
public void sum() {
assertEquals(3000, testTable.sum(), 0.01);
}
@Test
public void size() {
assertEquals(24, testTable.size());
}
@Test
public void sort() {
resultTable = testTable.valueBetween(80, 90);
assertEquals(2, resultTable.size());
assertEquals("i2", resultTable.first().instance());
assertEquals("i1", resultTable.last().instance());
resultTable = resultTable.sort(false, SortKey.INSTANCE);
assertEquals(2, resultTable.size());
assertEquals("i1", resultTable.first().instance());
assertEquals("i2", resultTable.last().instance());
assertEquals("c2", resultTable.first().component());
assertEquals("c1", resultTable.last().component());
resultTable = resultTable.sort(false, SortKey.COMPONENT);
assertEquals("c1", resultTable.first().component());
assertEquals("c2", resultTable.last().component());
}
@Test
public void first() {
Measurement measurement = testTable.first();
assertEquals("c1", measurement.component());
assertEquals("i1", measurement.instance());
assertEquals(10, measurement.instant().toEpochMilli());
}
@Test
public void last() {
Measurement measurement = testTable.last();
assertEquals("c3", measurement.component());
assertEquals(240, measurement.instant().toEpochMilli());
}
@Test
public void slice() {
assertEquals(24, testTable.size());
Iterator<Measurement> measurements = testTable.get().iterator();
double firstValue = measurements.next().value();
double secondValue = measurements.next().value();
resultTable = testTable.component("c1").slice(0, 1);
assertEquals(2, resultTable.size());
measurements = resultTable.get().iterator();
assertEquals(firstValue, measurements.next().value(), 0.01);
assertEquals(secondValue, measurements.next().value(), 0.01);
resultTable = testTable.component("c3").slice(7, 7);
assertEquals(1, resultTable.size());
measurements = resultTable.get().iterator();
assertEquals(240, measurements.next().value(), 0.01);
}
@Test
public void valueBetween() {
resultTable = testTable.valueBetween(45, 65);
assertEquals(2, resultTable.size());
resultTable.get().forEach(m -> assertTrue(45 <= m.value()));
resultTable.get().forEach(m -> assertTrue(65 >= m.value()));
}
@Test
public void uniqueComponents() {
assertTrue(testTable.component(Arrays.asList("c1", "c2")).size() > 10);
Collection<String> components = testTable.component(Arrays.asList("c1", "c2")).uniqueComponents();
assertEquals(2, components.size());
assertTrue(components.contains("c1"));
assertTrue(components.contains("c2"));
}
}

Просмотреть файл

@ -0,0 +1,184 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.core;
import com.microsoft.dhalion.core.OutcomeTable.SortKey;
import com.microsoft.dhalion.core.SymptomsTable.Builder;
import org.junit.Before;
import org.junit.Test;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class SymptomsTableTest {
private SymptomsTable testTable;
private SymptomsTable resultTable;
@Before
public void createTestTable() {
int[] ids = {1, 2, 3};
String[] types = {"s1", "s2"};
List<String> attributions = Arrays.asList("c1", "c2", "c3");
Collection<Symptom> symptoms = new ArrayList<>();
int value = 10;
for (int id : ids) {
for (String type : types) {
symptoms.add(new Symptom(id, type, Instant.ofEpochMilli(value), attributions, null));
value += 10;
}
}
Builder builder = new Builder();
builder.addAll(symptoms);
testTable = builder.get();
}
@Test
public void id() {
resultTable = testTable.id(1);
assertEquals(6, resultTable.size());
resultTable.get().forEach(s -> assertEquals(1, s.id()));
resultTable = testTable.id(2);
assertEquals(6, resultTable.size());
resultTable.get().forEach(symptom -> assertEquals(2, symptom.id()));
}
@Test
public void type() {
resultTable = testTable.type("s1");
assertEquals(9, resultTable.size());
resultTable.get().forEach(s -> assertEquals("s1", s.type()));
resultTable = testTable.type(Arrays.asList("s1", "s2"));
assertEquals(18, resultTable.size());
}
@Test
public void assignment() {
resultTable = testTable.assignment("c1");
assertEquals(6, resultTable.size());
resultTable.get().forEach(s -> assertEquals(1, s.assignments().size()));
resultTable.get().forEach(s -> assertEquals("c1", s.assignments().iterator().next()));
resultTable = testTable.assignment(Arrays.asList("c1", "c2"));
assertEquals(12, resultTable.size());
}
@Test
public void between() {
Instant oldest = Instant.ofEpochMilli(20);
Instant newest = Instant.ofEpochMilli(30);
resultTable = testTable.between(oldest, newest);
assertEquals(6, resultTable.size());
resultTable.get().forEach(s -> assertTrue(20 <= s.instant().toEpochMilli()));
resultTable.get().forEach(s -> assertTrue(30 >= s.instant().toEpochMilli()));
}
@Test
public void size() {
assertEquals(18, testTable.size());
}
@Test
public void uniqueIds() {
assertTrue(testTable.id(1).size() > 1);
Collection<Integer> ids = testTable.id(1).uniqueIds();
assertEquals(1, ids.size());
assertTrue(ids.contains(1));
ids = testTable.uniqueIds();
assertEquals(3, ids.size());
assertTrue(ids.contains(1));
assertTrue(ids.contains(2));
assertTrue(ids.contains(3));
}
@Test
public void uniqueTypes() {
assertTrue(testTable.type("s1").size() > 1);
Collection<String> types = testTable.type("s1").uniqueTypes();
assertEquals(1, types.size());
assertTrue(types.contains("s1"));
types = testTable.uniqueTypes();
assertEquals(2, types.size());
assertTrue(types.contains("s1"));
assertTrue(types.contains("s2"));
}
@Test
public void sort() {
resultTable = testTable.assignment("c3").between(Instant.ofEpochMilli(20), Instant.ofEpochMilli(30));
assertEquals(2, resultTable.size());
assertEquals("s2", resultTable.first().type());
assertEquals("s1", resultTable.last().type());
resultTable = resultTable.sort(false, SortKey.TYPE);
assertEquals(2, resultTable.size());
assertEquals("s1", resultTable.first().type());
assertEquals("s2", resultTable.last().type());
assertEquals(2, resultTable.first().id());
assertEquals(1, resultTable.last().id());
resultTable = resultTable.sort(false, SortKey.ID);
assertEquals(1, resultTable.first().id());
assertEquals(2, resultTable.last().id());
resultTable = resultTable.sort(true, SortKey.ID);
assertEquals(2, resultTable.first().id());
assertEquals(1, resultTable.last().id());
}
@Test
public void slice() {
resultTable = testTable.id(1);
assertEquals(6, resultTable.size());
Iterator<Symptom> symptoms = testTable.get().iterator();
String assignment1 = symptoms.next().assignments().iterator().next();
String assignment2 = symptoms.next().assignments().iterator().next();
resultTable = resultTable.slice(0, 1);
assertEquals(2, resultTable.size());
symptoms = resultTable.get().iterator();
assertEquals(assignment1, symptoms.next().assignments().iterator().next());
assertEquals(assignment2, symptoms.next().assignments().iterator().next());
}
@Test
public void first() {
Symptom symptom = testTable.first();
assertEquals("c1", symptom.assignments().iterator().next());
assertEquals(1, symptom.id());
assertEquals(10, symptom.instant().toEpochMilli());
}
@Test
public void last() {
Symptom symptom = testTable.last();
assertEquals("c3", symptom.assignments().iterator().next());
assertEquals(3, symptom.id());
assertEquals(60, symptom.instant().toEpochMilli());
}
@Test
public void get() {
Collection<Symptom> result = testTable.get();
assertEquals(18, result.size());
}
}

Просмотреть файл

@ -1,126 +0,0 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.metrics;
import com.microsoft.dhalion.common.DuplicateMetricException;
import org.junit.Test;
import java.util.Collection;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class ComponentMetricsTest {
@Test
public void testFilters() {
InstanceMetrics c1i2m1 = new InstanceMetrics("c1", "i2", "m1");
InstanceMetrics c2i3m1 = new InstanceMetrics("c2", "i3", "m1");
InstanceMetrics c2i4m3 = new InstanceMetrics("c2", "i4", "m3");
ComponentMetrics metrics = new ComponentMetrics();
metrics.add(new InstanceMetrics("c1", "i1", "m1"));
metrics.add(new InstanceMetrics("c1", "i1", "m2"));
metrics.add(new InstanceMetrics("c1", "i2", "m3"));
metrics.add(c1i2m1);
metrics.add(c2i3m1);
metrics.add(c2i4m3);
assertEquals(6, metrics.getMetrics().size());
ComponentMetrics result = metrics.filterByComponent("c2");
assertEquals(2, result.getMetrics().size());
assertTrue(result.getMetrics().contains(c2i3m1));
assertTrue(result.getMetrics().contains(c2i4m3));
result = metrics.filterByComponent("c1");
assertEquals(4, result.getMetrics().size());
result = metrics.filterByMetric("m1");
assertEquals(3, result.getMetrics().size());
assertTrue(result.getMetrics().contains(c1i2m1));
assertTrue(result.getMetrics().contains(c2i3m1));
// test filter chaining
result = metrics.filterByComponent("c1");
assertEquals(4, result.getMetrics().size());
result = result.filterByMetric("m1");
assertEquals(2, result.getMetrics().size());
result = result.filterByInstance("c1", "i1");
assertEquals(1, result.getMetrics().size());
}
@Test
public void testGetMetricNames() {
ComponentMetrics componentMetrics = new ComponentMetrics();
componentMetrics.add(new InstanceMetrics("c1", "i1", "m1"));
componentMetrics.add(new InstanceMetrics("c1", "i2", "m2"));
componentMetrics.add(new InstanceMetrics("c2", "i3", "m1"));
componentMetrics.add(new InstanceMetrics("c2", "i4", "m3"));
assertEquals(4, componentMetrics.getMetrics().size());
Collection<String> names = componentMetrics.getMetricNames();
assertEquals(3, names.size());
assertTrue(names.contains("m1"));
assertTrue(names.contains("m2"));
assertTrue(names.contains("m3"));
}
@Test
public void testGetCompNames() {
ComponentMetrics componentMetrics = new ComponentMetrics();
componentMetrics.add(new InstanceMetrics("c1", "i1", "m1"));
componentMetrics.add(new InstanceMetrics("c1", "i2", "m2"));
componentMetrics.add(new InstanceMetrics("c2", "i3", "m1"));
componentMetrics.add(new InstanceMetrics("c2", "i4", "m3"));
assertEquals(4, componentMetrics.getMetrics().size());
Collection<String> names = componentMetrics.getComponentNames();
assertEquals(2, names.size());
assertTrue(names.contains("c1"));
assertTrue(names.contains("c2"));
}
@Test(expected = DuplicateMetricException.class)
public void testDuplicateErrors() {
ComponentMetrics componentMetrics = new ComponentMetrics();
componentMetrics.add(new InstanceMetrics("c1", "i1", "m1"));
componentMetrics.add(new InstanceMetrics("c1", "i1", "m1"));
}
@Test
public void testMerge() {
ComponentMetrics componentMetrics1 = new ComponentMetrics();
componentMetrics1.add(new InstanceMetrics("c1", "i1", "m1"));
componentMetrics1.add(new InstanceMetrics("c1", "i1", "m2"));
componentMetrics1.add(new InstanceMetrics("c1", "i2", "m2"));
assertEquals(1, componentMetrics1.getComponentNames().size());
assertEquals(2, componentMetrics1.getMetricNames().size());
assertEquals(3, componentMetrics1.filterByComponent("c1").getMetrics().size());
assertEquals(1, componentMetrics1.filterByMetric("m1").getMetrics().size());
ComponentMetrics componentMetrics2 = new ComponentMetrics();
componentMetrics2.add(new InstanceMetrics("c1", "i1", "m3"));
componentMetrics2.add(new InstanceMetrics("c2", "i3", "m2"));
componentMetrics2.add(new InstanceMetrics("c3", "i4", "m2"));
assertEquals(3, componentMetrics2.getComponentNames().size());
assertEquals(2, componentMetrics2.getMetricNames().size());
assertEquals(1, componentMetrics2.filterByComponent("c1").getMetrics().size());
assertEquals(1, componentMetrics2.filterByComponent("c2").getMetrics().size());
assertEquals(1, componentMetrics2.filterByComponent("c3").getMetrics().size());
assertEquals(1, componentMetrics2.filterByMetric("m3").getMetrics().size());
ComponentMetrics result = ComponentMetrics.merge(componentMetrics1, componentMetrics2);
assertEquals(3, result.getComponentNames().size());
assertEquals(3, result.getMetricNames().size());
assertEquals(4, result.filterByComponent("c1").getMetrics().size());
assertEquals(1, result.filterByComponent("c2").getMetrics().size());
assertEquals(1, result.filterByComponent("c3").getMetrics().size());
assertEquals(1, result.filterByMetric("m1").getMetrics().size());
assertEquals(4, result.filterByMetric("m2").getMetrics().size());
assertEquals(1, result.filterByMetric("m3").getMetrics().size());
}
}

Просмотреть файл

@ -1,137 +0,0 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.metrics;
import org.junit.Test;
import java.time.Instant;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class InstanceMetricsTest {
@Test
public void testConstruction() {
InstanceMetrics instanceMetric = new InstanceMetrics("comp", "inst", "met");
assertEquals("comp", instanceMetric.getComponentName());
assertEquals("inst", instanceMetric.getInstanceName());
assertEquals("met", instanceMetric.getMetricName());
}
@Test
public void testAddValue() {
InstanceMetrics instanceMetric = new InstanceMetrics("comp", "inst", "met");
assertEquals(0, instanceMetric.getValues().size());
Instant before = Instant.now();
instanceMetric.addValue(13.0);
Instant after = Instant.now();
assertEquals(1, instanceMetric.getValues().size());
assertEquals(13.0, instanceMetric.getValues().values().iterator().next(), 0.0);
Instant instant = instanceMetric.getValues().keySet().iterator().next();
assertTrue(instant.toEpochMilli() >= before.toEpochMilli());
assertTrue(instant.toEpochMilli() <= after.toEpochMilli());
}
@Test
public void testAddValues() {
HashMap<Instant, Double> values = new HashMap<>();
InstanceMetrics instanceMetric = new InstanceMetrics("comp", "inst", "met");
assertEquals(0, instanceMetric.getValues().size());
instanceMetric.addValues(values);
assertEquals(0, instanceMetric.getValues().size());
values.put(Instant.now(), 10.0);
values.put(Instant.now().plusSeconds(10), 20.0);
values.put(Instant.now().plusSeconds(20), 30.0);
instanceMetric.addValues(values);
assertEquals(3, instanceMetric.getValues().size());
assertEquals(60, instanceMetric.getValueSum(), 0.0);
assertTrue(instanceMetric.hasValueAboveLimit(20));
assertFalse(instanceMetric.hasValueAboveLimit(40));
}
@Test
public void testGetRecentValues() {
HashMap<Instant, Double> values = new HashMap<>();
InstanceMetrics instanceMetric = new InstanceMetrics("comp", "inst", "met");
assertEquals(0, instanceMetric.getValues().size());
instanceMetric.addValues(values);
assertEquals(0, instanceMetric.getValues().size());
Instant t = Instant.now();
values.put(t, 10.0);
values.put(t.plusSeconds(10), 20.0);
values.put(t.plusSeconds(20), 30.0);
instanceMetric.addValues(values);
assertEquals(3, instanceMetric.getValues().size());
assertEquals(60, instanceMetric.getValueSum(), 0.0);
Map<Instant, Double> recentValues1 = instanceMetric.getMostRecentValues(1);
assertEquals(1, recentValues1.size());
assertTrue(recentValues1.containsKey(t.plusSeconds(20)));
assertTrue(recentValues1.containsValue(30.0));
Map<Instant, Double> recentValues2 = instanceMetric.getMostRecentValues(2);
assertEquals(2, recentValues2.size());
assertTrue(recentValues2.containsKey(t.plusSeconds(20)));
assertTrue(recentValues2.containsKey(t.plusSeconds(10)));
assertTrue(recentValues2.containsValue(30.0));
assertTrue(recentValues2.containsValue(20.0));
Map<Instant, Double> recentValues3 = instanceMetric.getMostRecentValues(3);
assertEquals(3, recentValues3.size());
assertEquals(instanceMetric.getValues(), recentValues3);
}
@Test
public void testGetRecentTimestamps() {
HashMap<Instant, Double> values = new HashMap<>();
InstanceMetrics instanceMetric = new InstanceMetrics("comp", "inst", "met");
assertEquals(0, instanceMetric.getValues().size());
instanceMetric.addValues(values);
assertEquals(0, instanceMetric.getValues().size());
Instant t = Instant.now();
values.put(t, 10.0);
values.put(t.plusSeconds(10), 20.0);
values.put(t.plusSeconds(20), 30.0);
instanceMetric.addValues(values);
assertEquals(3, instanceMetric.getValues().size());
assertEquals(60, instanceMetric.getValueSum(), 0.0);
Set<Instant> recentTimestamps1 = instanceMetric.getMostRecentTimestamps(1);
assertEquals(1, recentTimestamps1.size());
assertTrue(recentTimestamps1.contains(t.plusSeconds(20)));
Set<Instant> recentTimestamps2 = instanceMetric.getMostRecentTimestamps(2);
assertEquals(2, recentTimestamps2.size());
assertTrue(recentTimestamps2.contains(t.plusSeconds(20)));
assertTrue(recentTimestamps2.contains(t.plusSeconds(10)));
Set<Instant> recentTimestamps3 = instanceMetric.getMostRecentTimestamps(3);
assertEquals(3, recentTimestamps3.size());
}
}

Просмотреть файл

@ -7,109 +7,65 @@
package com.microsoft.dhalion.policy;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import com.microsoft.dhalion.api.IDetector;
import com.microsoft.dhalion.api.IDiagnoser;
import com.microsoft.dhalion.api.IResolver;
import com.microsoft.dhalion.api.ISensor;
import com.microsoft.dhalion.policy.HealthPolicyImpl.ClockTimeProvider;
import com.microsoft.dhalion.state.MetricsState;
import org.junit.Assert;
import org.junit.Test;
import static org.mockito.Matchers.anyList;
import java.time.Duration;
import java.time.Instant;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
public class HealthPolicyImplTest {
@Test
public void testRegisterStages() {
IDetector detector = mock(IDetector.class);
IResolver resolver = mock(IResolver.class);
IDiagnoser diagnoser = mock(IDiagnoser.class);
HealthPolicyImpl policy = new HealthPolicyImpl();
policy.registerDetectors(detector);
policy.registerDiagnosers(diagnoser);
policy.registerResolvers(resolver);
policy.executeDetectors();
policy.executeDiagnosers(new ArrayList<>());
policy.executeResolver(resolver, new ArrayList<>());
verify(detector, times(1)).detect();
verify(diagnoser, times(1)).diagnose(anyList());
verify(resolver, times(1)).resolve(anyList());
}
@Test
public void testInitialize() {
ArrayList<ISensor> sensors = new ArrayList<>();
ISensor sensor = mock(ISensor.class);
sensors.add(sensor);
ArrayList<IDetector> detectors = new ArrayList<>();
IDetector detector = mock(IDetector.class);
detectors.add(detector);
ArrayList<IDiagnoser> diagnosers = new ArrayList<>();
IDiagnoser diagnoser = mock(IDiagnoser.class);
diagnosers.add(diagnoser);
ArrayList<IResolver> resolvers = new ArrayList<>();
IResolver resolver = mock(IResolver.class);
resolvers.add(resolver);
HealthPolicyImpl policy = new HealthPolicyImpl();
policy.initialize(sensors, detectors, diagnosers, resolvers);
createTestPolicy(sensor, detector, diagnoser, resolver);
MetricsState metricsState = new MetricsState();
policy.executeSensors(metricsState);
policy.executeDetectors();
policy.executeDiagnosers(new ArrayList<>());
policy.executeResolver(resolver, new ArrayList<>());
verify(sensor, times(1)).fetchMetrics();
verify(detector, times(1)).detect();
verify(diagnoser, times(1)).diagnose(anyList());
verify(resolver, times(1)).resolve(anyList());
verify(sensor, times(1)).initialize(null);
verify(detector, times(1)).initialize(null);
verify(diagnoser, times(1)).initialize(null);
verify(resolver, times(1)).initialize(null);
}
@Test
public void testGetDelay() {
HealthPolicyImpl policy = new HealthPolicyImpl();
policy.setPolicyExecutionInterval(TimeUnit.MILLISECONDS, 100);
policy.setPolicyExecutionInterval(Duration.ofMillis(100));
TestClock testClock = new TestClock();
policy.clock = testClock;
testClock.timestamp = 12345;
// first execution should start with 0 delay
long delay = policy.getDelay(TimeUnit.MILLISECONDS);
Assert.assertEquals(0, delay);
Duration delay = policy.getDelay();
assertTrue(delay.isZero());
policy.executeResolver(null, null);
delay = policy.getDelay(TimeUnit.MILLISECONDS);
Assert.assertEquals(100, delay);
delay = policy.getDelay(TimeUnit.MILLISECONDS);
Assert.assertEquals(100, delay);
policy.executeResolvers(null);
delay = policy.getDelay();
assertEquals(100, delay.toMillis());
// one time delay overrides original
policy.setOneTimeDelay(TimeUnit.MILLISECONDS, 10);
delay = policy.getDelay(TimeUnit.MILLISECONDS);
Assert.assertEquals(10, delay);
policy.setOneTimeDelay(Duration.ofMillis(10));
delay = policy.getDelay();
assertEquals(10, delay.toMillis());
testClock.timestamp += 10;
// new cycle should reset one time delay
policy.executeResolver(null, null);
delay = policy.getDelay(TimeUnit.MILLISECONDS);
Assert.assertEquals(100, delay);
policy.executeResolvers(null);
delay = policy.getDelay();
assertEquals(100, delay.toMillis());
}
@Test
@ -119,11 +75,7 @@ public class HealthPolicyImplTest {
IResolver resolver = mock(IResolver.class);
IDiagnoser diagnoser = mock(IDiagnoser.class);
HealthPolicyImpl policy = new HealthPolicyImpl();
policy.registerSensors(sensor);
policy.registerDetectors(detector);
policy.registerDiagnosers(diagnoser);
policy.registerResolvers(resolver);
HealthPolicyImpl policy = createTestPolicy(sensor, detector, diagnoser, resolver);
policy.close();
@ -133,12 +85,22 @@ public class HealthPolicyImplTest {
verify(resolver, times(1)).close();
}
private HealthPolicyImpl createTestPolicy(ISensor s, IDetector d, IDiagnoser diagnoser, IResolver r) {
HealthPolicyImpl policy = new HealthPolicyImpl();
policy.registerSensors(s);
policy.registerDetectors(d);
policy.registerDiagnosers(diagnoser);
policy.registerResolvers(r);
policy.initialize(null);
return policy;
}
class TestClock extends ClockTimeProvider {
long timestamp = -1;
@Override
long currentTimeMillis() {
return timestamp < 0 ? System.currentTimeMillis() : timestamp;
Instant now() {
return timestamp < 0 ? Instant.now() : Instant.ofEpochMilli(timestamp);
}
}
}

Просмотреть файл

@ -8,61 +8,79 @@
package com.microsoft.dhalion.policy;
import com.microsoft.dhalion.api.IHealthPolicy;
import com.microsoft.dhalion.api.IResolver;
import com.microsoft.dhalion.core.Action;
import com.microsoft.dhalion.core.Diagnosis;
import com.microsoft.dhalion.core.Measurement;
import com.microsoft.dhalion.core.Symptom;
import org.junit.Test;
import org.mockito.InOrder;
import org.mockito.Mockito;
import org.mockito.exceptions.verification.WantedButNotInvoked;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ScheduledFuture;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.anyList;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class PoliciesExecutorTest {
@Test
public void verifyPeriodicPolicyInvocation() throws Exception {
HealthPolicyImpl policy1 = spy(new HealthPolicyImpl());
policy1.setPolicyExecutionInterval(TimeUnit.MILLISECONDS, 20);
policy1.setPolicyExecutionInterval(Duration.ofMillis(20));
HealthPolicyImpl policy2 = spy(new HealthPolicyImpl());
policy2.setPolicyExecutionInterval(TimeUnit.MILLISECONDS, 50);
policy2.setPolicyExecutionInterval(Duration.ofMillis(50));
List<IHealthPolicy> policies = new ArrayList<>();
policies.add(policy1);
policies.add(policy2);
List<IHealthPolicy> policies = Arrays.asList(policy1, policy2);
PoliciesExecutor executor = new PoliciesExecutor(policies);
executor.start();
verify(policy1, timeout(1000l).atLeast(5)).executeResolver(any(), anyList());
verify(policy2, timeout(1000l).atLeast(2)).executeResolver(any(), anyList());
verify(policy1, timeout(1000l).atLeast(5)).executeResolvers(anyList());
verify(policy2, timeout(1000l).atLeast(2)).executeResolvers(anyList());
executor.destroy();
}
@Test
public void verifyPolicyExecutionOrder() throws Exception {
List symptoms = new ArrayList<>();
List diagnosis = new ArrayList<>();
IResolver resolver = mock(IResolver.class);
List actions = new ArrayList<>();
List<Measurement> measurements = new ArrayList<>();
List<Symptom> symptoms = new ArrayList<>();
List<Diagnosis> diagnosis = new ArrayList<>();
List<Action> actions = new ArrayList<>();
IHealthPolicy mockPolicy = mock(IHealthPolicy.class);
when(mockPolicy.executeDetectors()).thenReturn(symptoms);
when(mockPolicy.getDelay()).thenReturn(Duration.ZERO);
when(mockPolicy.executeSensors()).thenReturn(measurements);
when(mockPolicy.executeDetectors(measurements)).thenReturn(symptoms);
when(mockPolicy.executeDiagnosers(symptoms)).thenReturn(diagnosis);
when(mockPolicy.selectResolver(diagnosis)).thenReturn(resolver);
when(mockPolicy.executeResolver(resolver, diagnosis)).thenReturn(actions);
when(mockPolicy.executeResolvers(diagnosis)).thenReturn(actions);
List<IHealthPolicy> policies = new ArrayList<>();
policies.add(mockPolicy);
List<IHealthPolicy> policies = Collections.singletonList(mockPolicy);
PoliciesExecutor executor = new PoliciesExecutor(policies);
executor.start();
ScheduledFuture<?> future = executor.start();
try {
verify(mockPolicy, timeout(50l).atLeastOnce()).executeResolvers(diagnosis);
} catch (WantedButNotInvoked e) {
if (future.isDone()) {
System.out.println(future.get());
}
throw e;
}
verify(mockPolicy, timeout(50l).atLeastOnce()).executeResolver(resolver, diagnosis);
InOrder order = Mockito.inOrder(mockPolicy);
order.verify(mockPolicy).executeDetectors();
order.verify(mockPolicy).executeSensors();
order.verify(mockPolicy).executeDetectors(measurements);
order.verify(mockPolicy).executeDiagnosers(symptoms);
order.verify(mockPolicy).selectResolver(diagnosis);
order.verify(mockPolicy).executeResolver(resolver, diagnosis);
order.verify(mockPolicy).executeResolvers(diagnosis);
executor.destroy();
}

Просмотреть файл

@ -1,32 +0,0 @@
package com.microsoft.dhalion.sensor;
import com.microsoft.dhalion.api.ISensor;
import com.microsoft.dhalion.metrics.ComponentMetrics;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class SensorImplTest {
@Test
public void testSensorImplGet() {
ISensor sensor = new SensorImpl("m") {
@Override
public ComponentMetrics fetchMetrics() {
metrics.addMetric("c1", "i1", "m", 123);
metrics.addMetric("c1", "i2", "m", 123);
metrics.addMetric("c2", "i3", "m", 133);
metrics.addMetric("c2", "i4", "m", 143);
return metrics;
}
};
assertEquals("m", sensor.getMetricName());
sensor.fetchMetrics();
ComponentMetrics componentData = sensor.readMetrics();
assertEquals(2, componentData.getComponentNames().size());
assertEquals(2, componentData.filterByComponent("c1").getMetrics().size());
assertEquals(1, componentData.filterByInstance("c1", "i1").getMetrics().size());
}
}

Просмотреть файл

@ -1,63 +0,0 @@
/*
* Copyright (c) Microsoft Corporation. All rights reserved.
*
* This program is made available under the terms of the MIT License.
* See the LICENSE file in the project root for more information.
*/
package com.microsoft.dhalion.state;
import com.microsoft.dhalion.metrics.ComponentMetrics;
import com.microsoft.dhalion.metrics.MetricsStats;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
public class MetricsStateTest {
@Test
public void testSnapshotConstructionWithStats() {
ComponentMetrics metrics = new ComponentMetrics();
metrics.addMetric("c1", "i1", "m1", 100);
metrics.addMetric("c2", "i2", "m1", 200);
Map<String, MetricsStats> stats = new HashMap<>();
stats.put("c1", new MetricsStats("m1", 100, 100, 100));
stats.put("c2", new MetricsStats("m1", 200, 200, 200));
MetricsState snapshot = new MetricsState();
snapshot.addMetricsAndStats(metrics, stats);
assertEquals(2, snapshot.getMetrics().getMetrics().size());
assertEquals(1, snapshot.getMetrics().filterByComponent("c1").getMetrics().size());
assertEquals(1, snapshot.getMetrics().filterByComponent("c2").getMetrics().size());
assertEquals(100, (int) snapshot.getStats("c1", "m1").getMetricAvg());
assertEquals(200, (int) snapshot.getStats("c2", "m1").getMetricAvg());
assertNull(snapshot.getStats("c1", "m2"));
assertNull(snapshot.getStats("c2", "m2"));
assertNull(snapshot.getStats("c1", "m3"));
assertNull(snapshot.getStats("c2", "m3"));
metrics = new ComponentMetrics();
metrics.addMetric("c1", "i1", "m2", 300);
metrics.addMetric("c2", "i2", "m2", 400);
Map<String, MetricsStats> stats2 = new HashMap<>();
stats2.put("c1", new MetricsStats("m2", 300, 300, 300));
stats2.put("c2", new MetricsStats("m2", 400, 400, 400));
snapshot.addMetricsAndStats(metrics, stats2);
assertEquals(100, (int) snapshot.getStats("c1", "m1").getMetricAvg());
assertEquals(200, (int) snapshot.getStats("c2", "m1").getMetricAvg());
assertEquals(300, (int) snapshot.getStats("c1", "m2").getMetricAvg());
assertEquals(400, (int) snapshot.getStats("c2", "m2").getMetricAvg());
assertNull(snapshot.getStats("c1", "m3"));
assertNull(snapshot.getStats("c2", "m3"));
}
}