adaptive-monitoring-framework

working version

2/2/2021 12:10:27 PM

Details

diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/Apdex.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/Apdex.java
index fc10a8f..d08ba89 100644
--- a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/Apdex.java
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/Apdex.java
@@ -23,4 +23,8 @@ public class Apdex {
     public long getN() {
         return n;
     }
+
+    public double getApdexMetric(){
+        return 1 - ((getSatisfied() + 0.5 * getTolerated()) / getN());
+    }
 }
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/FrequencyDataSet.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/FrequencyDataSet.java
index 94bcce7..0066458 100644
--- a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/FrequencyDataSet.java
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/FrequencyDataSet.java
@@ -11,13 +11,19 @@ import java.util.concurrent.ConcurrentHashMap;
 public class FrequencyDataSet {
 
     private Map<Granularity, Integer> granularityPopulation = new ConcurrentHashMap<>();
+    private SummaryStatistics statistics = new SummaryStatistics();
     private long n;
 
-    public void addItem(Granularity item) {
+    public void addItem(Granularity item, double executionTime) {
         granularityPopulation.put(item, granularityPopulation.getOrDefault(item, 0) + 1);
+        statistics.addValue(executionTime);
         n++;
     }
 
+    public double getMeanExecutionTime(){
+        return statistics.getMean();
+    }
+
     public long getTotalItems(){
         return n;
     }
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/MonitoringCycle.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/MonitoringCycle.java
new file mode 100644
index 0000000..c49e89b
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/MonitoringCycle.java
@@ -0,0 +1,25 @@
+package br.ufrgs.inf.prosoft.tigris.sampling;
+
+public class MonitoringCycle {
+    private double averageProcTimesSample;
+    private double averageProcTimesPopulation;
+    private double sampleSize;
+    private double populationSize;
+
+    public MonitoringCycle(double averageProcTimesSample, double averageProcTimesPopulation, double sampleSize, double populationSize) {
+        this.averageProcTimesSample = averageProcTimesSample;
+        this.averageProcTimesPopulation = averageProcTimesPopulation;
+        this.sampleSize = sampleSize;
+        this.populationSize = populationSize;
+    }
+
+    @Override
+    public String toString() {
+        return "MonitoringCycle{" +
+                "averageProcTimesSample=" + averageProcTimesSample +
+                ", averageProcTimesPopulation=" + averageProcTimesPopulation +
+                ", sampleSize=" + sampleSize +
+                ", populationSize=" + populationSize +
+                '}';
+    }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/PerformanceBaselineDataSet.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/PerformanceBaselineDataSet.java
index 2ab10b1..eb40cde 100644
--- a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/PerformanceBaselineDataSet.java
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/PerformanceBaselineDataSet.java
@@ -18,6 +18,7 @@ public class PerformanceBaselineDataSet {
 
     private Map<Granularity, DescriptiveStatistics> granularityBaseline = new ConcurrentHashMap<>();
     private DescriptiveStatistics overallBaseline = new DescriptiveStatistics(1200);
+    private DescriptiveStatistics historicOfBaselines = new DescriptiveStatistics(1200);
     private int n = 0;
 
     public void addItem(Granularity item, long executionTime) {
@@ -29,40 +30,32 @@ public class PerformanceBaselineDataSet {
         overallBaseline.addValue(executionTime);
     }
 
-    //TODO compare baseline againts baseline, not against sampled items
-    //Solução 1: Guarda o baseline com a média mais baixa de todas - em teoria esse é o mais próximo da aplicação "real", sem influencia de monitoração ou falta de recursos - mínimo global
-        //compara o sample com o minimo global
-
-    //need to detect global max and min to avoid "getting used" to bad things
-    //diminuir as rodadas de baseline (espaços maiores), guardar max and min, olhar pra quartis talvez?
-    //se o q1 ta ficando maior, significa que a carga e overhead estão subindo, se está menor, está caindo
-    //questões: a aplicação está ok sem monitoração? essa é a melhor performance dela? é a pior? o quao distante dos topos está
     //não comparar baseline com sample, não faz sentido - ou é sample com sample, ou é baseline com sample
-    //baseline com sample, diz o overhead do sampling
-    //baseline com baseline diz a carga da aplicação
+    //comparar baseline com sample, diz o overhead do sampling
+    //comparar baseline com baseline histórico diz a carga da aplicação
+    public boolean isAppStruggling() {
+        historicOfBaselines.addValue(overallBaseline.getMean());
+
+        return historicOfBaselines.getPercentile(50) < overallBaseline.getMean();
+    }
+
+
     public Apdex getApdexResultsPerEvent(Map<Granularity, DescriptiveStatistics> sampledDataSet) {
         long satisfied = 0, tolerated = 0, n = 0;
         for (Map.Entry<Granularity, DescriptiveStatistics> baselineEntry : granularityBaseline.entrySet()) {
             DescriptiveStatistics stats = baselineEntry.getValue();
             double mean = stats.getMean();
             double std = stats.getStandardDeviation();
-            if (stats.getN() == stats.getWindowSize()) {
-                mean = new Mean().evaluate(stats.getValues(), weights);
-                std = FastMath.sqrt(new Variance().evaluate(stats.getValues(), weights));
-            }
+//            if (stats.getN() == stats.getWindowSize()) {
+//                mean = new Mean().evaluate(stats.getValues(), weights);
+//                std = FastMath.sqrt(new Variance().evaluate(stats.getValues(), weights));
+//            }
 
             double meanPlusStd = mean + std;
             DescriptiveStatistics descriptiveStatistics = sampledDataSet.get(baselineEntry.getKey());
             if (descriptiveStatistics == null)
                 continue;
             for (double value : descriptiveStatistics.getValues()) {
-//                if (value <= mean) {
-//                    satisfied++;
-//                }
-//                if (value > mean &&
-//                        value < meanPlusStd) {
-//                    tolerated++;
-//                }
                 if (value <= meanPlusStd) {
                     satisfied++;
                 }
@@ -74,6 +67,41 @@ public class PerformanceBaselineDataSet {
             }
         }
         return new Apdex(satisfied, tolerated, n);
+
+
+//        long satisfied = 0, tolerated = 0, n = 0;
+//        for (Map.Entry<Granularity, DescriptiveStatistics> baselineEntry : granularityBaseline.entrySet()) {
+//            DescriptiveStatistics stats = baselineEntry.getValue();
+//            double mean = stats.getMean();
+//            double std = stats.getStandardDeviation();
+////            if (stats.getN() == stats.getWindowSize()) {
+////                mean = new Mean().evaluate(stats.getValues(), weights);
+////                std = FastMath.sqrt(new Variance().evaluate(stats.getValues(), weights));
+////            }
+//
+//            double meanPlusStd = mean + std;
+//            DescriptiveStatistics descriptiveStatistics = sampledDataSet.get(baselineEntry.getKey());
+//            if (descriptiveStatistics == null)
+//                continue;
+//            for (double value : descriptiveStatistics.getValues()) {
+////                if (value <= mean) {
+////                    satisfied++;
+////                }
+////                if (value > mean &&
+////                        value < meanPlusStd) {
+////                    tolerated++;
+////                }
+//                if (value <= meanPlusStd) {
+//                    satisfied++;
+//                }
+//                if (value > meanPlusStd &&
+//                        value < mean + (2 * std)) {
+//                    tolerated++;
+//                }
+//                n++;
+//            }
+//        }
+//        return new Apdex(satisfied, tolerated, n);
     }
 
     /**
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/Sampling.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/Sampling.java
index 3a5b3c0..c2ec0a9 100644
--- a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/Sampling.java
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/Sampling.java
@@ -31,7 +31,6 @@ public class Sampling {
     private ExponentialDecayFunction decayingPrecision;
     private FrequencyDataSet population = new FrequencyDataSet(), sample = new FrequencyDataSet();
     private PerformanceBaselineDataSet performanceBaselineDataSet = new PerformanceBaselineDataSet();
-    private Map<Granularity, DescriptiveStatistics> lowestPerformanceBaselineDataSet = new ConcurrentHashMap<>();
     private Map<Granularity, DescriptiveStatistics> sampledDataSet = new ConcurrentHashMap<>();
 
     //PBA history
@@ -70,7 +69,7 @@ public class Sampling {
     public boolean samplingDecision(Granularity granularity, long executionTime) {
         if (population.getTotalItems() == 0)
             startTime = System.currentTimeMillis();
-        population.addItem(granularity);
+        population.addItem(granularity, executionTime);
 
         if (performanceBaselineEnabled) {
             return false;
@@ -109,44 +108,51 @@ public class Sampling {
         synchronized (samplingRateLock) {
             if (this.sampledDataSet.isEmpty()) {
                 logger.info("No sampled data, doing nothing...");
-                //if no impact, increase by 1%
-//                samplingRate += 0.01;
-//
-//                if (samplingRate > 1)
-//                    samplingRate = 1;
-//
-//                logger.info("New sampling rate: {}", samplingRate);
-//                this.resetSamplingDistribution();
+//                if no impact, increase by 1%
+                samplingRate += 0.01;
+
+                if (samplingRate > 1)
+                    samplingRate = 1;
+
+                logger.info("New sampling rate: {}", samplingRate);
+                this.resetSamplingDistribution();
                 return;
             }
 
-//            this.performanceBaselineDataSet.
-//
-//            lowestPerformanceBaselineDataSet
 //            Apdex apdex = this.performanceBaselineDataSet.getApdexResults(this.sampledDataSet, this.lastSampledTimes);
+//            double baselineImpact = performanceBaselineDataSet.getBaselineImpactedByWorkload();
             Apdex apdex = this.performanceBaselineDataSet.getApdexResultsPerEvent(this.sampledDataSet);
-            double impact = 1 - ((apdex.getSatisfied() + 0.5 * apdex.getTolerated()) / apdex.getN());
-
-            //if we have just 1 tolerated, the impact will not be zero anymore
-            if (impact <= 0.1) {
-                logger.info("No monitoring impact detected: {}, increasing the sampling rate...", impact);
-                //if no impact, increase by 10%
-                samplingRate += 0.1;
-            } else
-                //otherwise stays the same - not necessary here
-                if (impact > 0.1 && impact <= 0.2) {
-                    logger.info("Minimal monitoring impact detected: {}, keeping it the same...", impact);
-                } else if (impact > 0.2) {
-                    double reduction = impact - 0.2;
-                    logger.info("Monitoring impact detected: {}, decreasing the current sampling rate {} by {}%", impact, samplingRate, reduction);
-//                logger.info("{}, {}, {}", apdex.getSatisfied(), apdex.getTolerated(), apdex.getN());
-//                logger.info("{}", this.performanceBaselineDataSet.getOverallAvg());
-//                logger.info("{}", this.performanceBaselineDataSet.getOverallStd());
-//                logger.info("{}", this.performanceBaselineDataSet.getTotalItems());
-
-                    //reduce by the amount of overhead
-                    samplingRate = samplingRate - (samplingRate * (reduction / 1d));
-                }
+            double impact = (1 - ((apdex.getSatisfied() + 0.5 * apdex.getTolerated()) / apdex.getN()));
+
+            if (!this.performanceBaselineDataSet.isAppStruggling()) {
+
+                logger.info("App is not struggling, increasing the current sampling rate {} by {}%", samplingRate, impact);
+                samplingRate += impact;
+
+//                //if we have just 1 tolerated, the impact will not be zero anymore
+//                if (impact <= 0.1) {
+//                    logger.info("No monitoring impact detected: {}, increasing the sampling rate...", impact);
+//                    //if no impact, increase by 10%
+//                    samplingRate += 0.1;
+//                } else
+//                    //otherwise stays the same - not necessary here
+//                    if (impact > 0.1 && impact <= 0.2) {
+//                        logger.info("Minimal monitoring impact detected: {}, keeping it the same...", impact);
+//                    } else if (impact > 0.2) {
+//                        double reduction = impact - 0.2;
+//                        logger.info("Monitoring impact detected: {}, decreasing the current sampling rate {} by {}%", impact, samplingRate, reduction);
+////                logger.info("{}, {}, {}", apdex.getSatisfied(), apdex.getTolerated(), apdex.getN());
+////                logger.info("{}", this.performanceBaselineDataSet.getOverallAvg());
+////                logger.info("{}", this.performanceBaselineDataSet.getOverallStd());
+////                logger.info("{}", this.performanceBaselineDataSet.getTotalItems());
+//
+//                        //reduce by the amount of overhead
+//                        samplingRate = samplingRate - (samplingRate * (reduction / 1d));
+//                    }
+            } else { //app is struggling
+                logger.info("App is struggling, decreasing the current sampling rate {} by {}%", samplingRate, impact);
+                samplingRate -= impact;
+            }
 
             if (samplingRate < 0)
                 samplingRate = 0;
@@ -166,14 +172,12 @@ public class Sampling {
         }
     }
 
-    DescriptiveStatistics lastSampledTimes = new DescriptiveStatistics(1200);
-    public void addSampledItem(Granularity granularity, long startTime) {
-        sample.addItem(granularity);
+//    DescriptiveStatistics lastSampledTimes = new DescriptiveStatistics(1200);
+    public void addSampledItem(Granularity granularity, long executionTime) {
+        sample.addItem(granularity, executionTime);
 
         DescriptiveStatistics statistics = sampledDataSet.getOrDefault(granularity, new DescriptiveStatistics());
-        long time = System.nanoTime() - startTime;
-        statistics.addValue(time);
-        lastSampledTimes.addValue(time);
+        statistics.addValue(executionTime);
         sampledDataSet.put(granularity, statistics);
     }
 
@@ -262,10 +266,15 @@ public class Sampling {
         logger.info("Monitoring is reset...");
     }
 
-    public void endMonitoringCycle() {
-        logger.info("Adaptive Sampling Monitoring Cycle Finished - Sample traces: {}", getSample().getTotalItems());
-        logger.info("Adaptive Sampling Monitoring Cycle Finished - Population traces: {}", getPopulation().getTotalItems());
+    public MonitoringCycle endMonitoringCycle() {
+        MonitoringCycle monitoringCycle = new MonitoringCycle(
+                getSample().getMeanExecutionTime(),
+                getPopulation().getMeanExecutionTime(),
+                getSample().getTotalItems(),
+                getPopulation().getTotalItems());
+        logger.info("Adaptive Sampling Monitoring Cycle Finished: {}", monitoringCycle);
         startMonitoringCycle();
+        return monitoringCycle;
     }
 
     private Long minimumSampleSize;
@@ -282,7 +291,7 @@ public class Sampling {
             return;
         }
 
-        double chance = new BinomialDistribution(1, 0.3d).sample();
+        double chance = new BinomialDistribution(1, 0.1d).sample();
         if (chance == 1) {
             minimumSampleSize = getMinimumSampleSize(this.population.getTotalItems());
             if (minimumSampleSize > 0) {
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/SamplingAspect.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/SamplingAspect.java
index 32bb6b0..2eaeef6 100644
--- a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/SamplingAspect.java
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/SamplingAspect.java
@@ -86,17 +86,17 @@ public class SamplingAspect implements Runnable {
                 return result;
             }
 
-            boolean decision = sampling.samplingDecision(granularity, 0);
+            boolean decision = sampling.samplingDecision(granularity, endTime - startTime);
 
             if (decision)
-                sampling.addSampledItem(granularity, startTime);
+                sampling.addSampledItem(granularity, nanoTime() - startTime);
         }
 
         return result;
     }
 
     static List<Double> samplingRates = new ArrayList<>();
-    static int numberOfCycles = 0;
+    static List<MonitoringCycle> cycles = new ArrayList<>();
 
     @Override
     public void run() {
@@ -106,14 +106,13 @@ public class SamplingAspect implements Runnable {
 
             if (!sampling.isPerformanceBaselineEnabled() && sampling.isReady()) {
                 logger.info("Sample is ready, releasing for analysis and resetting...");
-                sampling.endMonitoringCycle();
-                numberOfCycles++;
+                cycles.add(sampling.endMonitoringCycle());
             }
         }
     }
 
     public static void printResults(){
         logger.info("Sampling Rates: {}", samplingRates);
-        logger.info("Number of finished cycles: {}", numberOfCycles);
+        logger.info("Finished cycles {}: {}", cycles.size(), cycles);
     }
 }
\ No newline at end of file