зеркало из https://github.com/microsoft/Dhalion.git
Merge branch 'dhalion-v2'
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:
Коммит
5cc50fea92
7
pom.xml
7
pom.xml
|
@ -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"));
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче