adaptive-monitoring-framework

Details

diff --git a/app-example/pom.xml b/app-example/pom.xml
index e08c131..5a0dc51 100644
--- a/app-example/pom.xml
+++ b/app-example/pom.xml
@@ -219,7 +219,7 @@
         <dependency>
             <groupId>br.ufrgs.inf.prosoft</groupId>
             <artifactId>tigris</artifactId>
-            <version>0.11.0-SNAPSHOT</version>
+            <version>0.12.0-SNAPSHOT</version>
         </dependency>
     </dependencies>
 
diff --git a/app-example/src/main/java/org/springframework/samples/petclinic/ApplicationInitializer.java b/app-example/src/main/java/org/springframework/samples/petclinic/ApplicationInitializer.java
index 8e43593..1de44d4 100644
--- a/app-example/src/main/java/org/springframework/samples/petclinic/ApplicationInitializer.java
+++ b/app-example/src/main/java/org/springframework/samples/petclinic/ApplicationInitializer.java
@@ -10,7 +10,9 @@ import br.ufrgs.inf.prosoft.tigris.sampling.GranularityType;
 @TigrisConfiguration(
     logRepository = RepositoryType.MEMORY,
     staticMetricFile = "petclinic.csv",
-    samplingPercentage = 0.5)
+    samplingPercentage = 0.5, adaptiveSamplingRate = true) // adaptive sampling
+//    samplingPercentage = 1, adaptiveSamplingRate = false) // full monitoring
+//    samplingPercentage = 0, adaptiveSamplingRate = false) // no monitoring
 @TigrisCriteria(
     criteria = "more frequent '∪' more expensive",
     granularity = GranularityType.METHOD,

tigris/pom.xml 2(+1 -1)

diff --git a/tigris/pom.xml b/tigris/pom.xml
index a972e98..2c274d4 100644
--- a/tigris/pom.xml
+++ b/tigris/pom.xml
@@ -6,7 +6,7 @@
 
     <groupId>br.ufrgs.inf.prosoft</groupId>
     <artifactId>tigris</artifactId>
-    <version>0.11.0-SNAPSHOT</version>
+    <version>0.12.0-SNAPSHOT</version>
 
     <properties>
         <aspectj.version>1.8.9</aspectj.version>
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/annotation/TigrisConfiguration.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/annotation/TigrisConfiguration.java
index c59e982..f748de0 100644
--- a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/annotation/TigrisConfiguration.java
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/annotation/TigrisConfiguration.java
@@ -46,4 +46,6 @@ public @interface TigrisConfiguration {
      * @return the int
      */
     long cycleTimeInMilliseconds() default 3600000; //3600000 milliseconds = 1h
+
+    boolean adaptiveSamplingRate() default false;
 }
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/TigrisCoordinator.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/TigrisCoordinator.java
index e388f8f..c2bfacd 100644
--- a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/TigrisCoordinator.java
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/TigrisCoordinator.java
@@ -92,7 +92,7 @@ public class TigrisCoordinator implements Runnable {
         deniedPattern = Pattern.compile(componentScanConfig.denied());
         logger.info("@TigrisConfiguration will trace and cache methods into {} and deny {} package.", allowedPattern.pattern(), deniedPattern.pattern());
 
-        sampling = new Sampling(tigrisConfiguration.samplingPercentage(), tigrisConfiguration.cycleTimeInMilliseconds());
+        sampling = new Sampling(tigrisConfiguration.samplingPercentage(), tigrisConfiguration.cycleTimeInMilliseconds(), tigrisConfiguration.adaptiveSamplingRate());
         //TODO when to run it?
         samplingAdaptationExecutor.scheduleWithFixedDelay(
                 sampling, 120000, 450000, TimeUnit.MILLISECONDS);
@@ -189,7 +189,7 @@ public class TigrisCoordinator implements Runnable {
 
         Granularity granularity = new Granularity(tigrisCriteria.granularity(), signature);
 
-        if (sampling.isPerformanceBaselineEnabled()) {
+        if (tigrisConfiguration.adaptiveSamplingRate() && sampling.isPerformanceBaselineEnabled()) {
             sampling.addPerformanceBaselineItem(granularity, endTime - startTime);
         }
 
@@ -255,7 +255,13 @@ public class TigrisCoordinator implements Runnable {
             logger.info("Selected methods for fine-grained ({}): {}", allowedFineGrained.size(), allowedFineGrained);
 
             //analyze once
-//            coarseMonitoringEnabled = false;
+            coarseMonitoringEnabled = false;
+        }
+        if (enabled) {
+            logger.info("Adaptive Sampling Status - Sample traces ({}): {}", sampling.getSample().getTotalItems(),
+                    sampling.getSample().getTraceFrequency());
+            logger.info("Adaptive Sampling Status - Population traces ({}): {}", sampling.getPopulation().getTotalItems(),
+                    sampling.getPopulation().getTraceFrequency());
         }
     }
 }
\ No newline at end of file
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 71a1fa4..ad3677d 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
@@ -43,4 +43,8 @@ public class FrequencyDataSet {
         }
         return summaryStatistics;
     }
+
+    public Map<Granularity, Integer> getTraceFrequency() {
+        return granularityPopulation;
+    }
 }
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 f0029df..bb9aa55 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
@@ -49,6 +49,9 @@ public class PerformanceBaselineDataSet {
     public Apdex getApdexResults(Map<Granularity, DescriptiveStatistics> sampledDataSet){
         long satisfied = 0, tolerated = 0, n = 0;
         for (Granularity granularity : sampledDataSet.keySet()){
+            //TODO is it ok to compare with the overall?
+            //some methods may be really fast and some really huge
+            //should we use getApdexResultsPerEvent?
             double meanPlusStd = getOverallAvg() + getOverallStd();
             for (double value: sampledDataSet.get(granularity).getValues()) {
                 if (value < meanPlusStd){
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 25ee90a..b4b79b3 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
@@ -17,6 +17,7 @@ import java.util.concurrent.ConcurrentHashMap;
  */
 public class Sampling implements Runnable {
 
+    private final boolean adaptiveSamplingRate;
     private boolean samplingEnabled = true;
     private boolean performanceBaselineEnabled = false;
     private double samplingRate; // in percentage, 0 to 1
@@ -36,9 +37,10 @@ public class Sampling implements Runnable {
      */
     private double z = 1.96, p = 0.5, e = 0.05;
 
-    public Sampling(double initialSamplingRate, long cycleLengthInMilliseconds) {
+    public Sampling(double initialSamplingRate, long cycleLengthInMilliseconds, boolean adaptiveSamplingRate) {
         samplingRate = initialSamplingRate;
         decayingPrecision = new ExponentialDecayFunction(1, 0.00001, cycleLengthInMilliseconds);
+        this.adaptiveSamplingRate = adaptiveSamplingRate;
         startMonitoringCycle();
     }
 
@@ -53,9 +55,15 @@ public class Sampling implements Runnable {
             return false;
         }
 
-        boolean decision = samplingEnabled
-                && simpleSamplingDecision() // sampling rate evaluation
-                && population.getProportion(granularity) >= sample.getProportion(granularity); // sample has not enough items of that granularity compared to the population
+        boolean simpleSamplingDecision = simpleSamplingDecision();
+        boolean decision;
+        if (adaptiveSamplingRate) {
+            decision = samplingEnabled
+                    && simpleSamplingDecision // sampling rate evaluation
+                    && population.getProportion(granularity) >= sample.getProportion(granularity); // sample has not enough items of that granularity compared to the population
+        } else {
+            decision = simpleSamplingDecision;
+        }
 
         if (decision)
             sample.addItem(granularity);
@@ -125,29 +133,28 @@ public class Sampling implements Runnable {
         return n_inf / (1 + ((n_inf - 1) / n));
     }
 
-    public double getSampleSizeErrorMargin() {
-        return getSampleSizeErrorMargin(z);
-    }
-
     public double getSampleSizeErrorMargin(double precision) {
         double e_n_inf = Math.sqrt((Math.pow(precision, 2) * p * (1 - p)) / sample.getTotalItems());
         return e_n_inf * Math.sqrt((population.getTotalItems() - sample.getTotalItems()) / (population.getTotalItems() - 1));
     }
 
-    public void startMonitoringCycle() {
-        startTime = System.currentTimeMillis();
-    }
-
     public long getMonitoringCycleTime(){
         return (System.currentTimeMillis() - startTime);
     }
 
     public void endMonitoringCycle() {
         this.sampledDataSet = new ConcurrentHashMap<>();
-        releaseForAnalysis();
+        logger.info("Adaptive Sampling Monitoring Cycle Finished - Sample traces ({}): {}", getSample().getTotalItems(),
+                getSample().getTraceFrequency());
+        logger.info("Adaptive Sampling Monitoring Cycle Finished - Population traces ({}): {}", getPopulation().getTotalItems(),
+                getPopulation().getTraceFrequency());
         startMonitoringCycle();
     }
 
+    private void startMonitoringCycle() {
+        startTime = System.currentTimeMillis();
+    }
+
     public boolean shouldCollectPerformanceBaseline() {
         return new BinomialDistribution(1, 0.1).sample() == 1;
     }
@@ -174,6 +181,10 @@ public class Sampling implements Runnable {
     public void run() {
         //TODO this is supposed to run from time to time based on triggers? Or every new trace collected?
 
+        //this method deals with sampling rate adaptation procedures
+        if (!adaptiveSamplingRate)
+            return;
+
         if (isReady()) {
             logger.info("Sample is ready, releasing for analysis and resetting...");
             endMonitoringCycle();
@@ -191,9 +202,6 @@ public class Sampling implements Runnable {
         performanceBaselineEnabled = true;
     }
 
-    public void releaseForAnalysis() {
-    }
-
     public boolean isPerformanceBaselineEnabled() {
         return performanceBaselineEnabled;
     }
@@ -231,4 +239,12 @@ public class Sampling implements Runnable {
         //TODO run it every new trace collected?
         run();
     }
+
+    public FrequencyDataSet getSample() {
+        return sample;
+    }
+
+    public FrequencyDataSet getPopulation() {
+        return population;
+    }
 }
diff --git a/tigris/src/test/java/br/ufrgs/inf/prosoft/tigris/SamplingTest.java b/tigris/src/test/java/br/ufrgs/inf/prosoft/tigris/SamplingTest.java
index 26ee2ce..b290bfc 100644
--- a/tigris/src/test/java/br/ufrgs/inf/prosoft/tigris/SamplingTest.java
+++ b/tigris/src/test/java/br/ufrgs/inf/prosoft/tigris/SamplingTest.java
@@ -3,14 +3,28 @@ package br.ufrgs.inf.prosoft.tigris;
 import br.ufrgs.inf.prosoft.tigris.sampling.Granularity;
 import br.ufrgs.inf.prosoft.tigris.sampling.GranularityType;
 import br.ufrgs.inf.prosoft.tigris.sampling.Sampling;
+import org.junit.Assert;
 import org.junit.Test;
 
 public class SamplingTest {
 
     @Test
-    public void samplingDecision(){
-        Sampling sampling = new Sampling(0.5, 100);
-        sampling.samplingDecision(new Granularity(GranularityType.METHOD, "function"));
-        sampling.samplingDecision(new Granularity(GranularityType.METHOD, "function"));
+    public void samplingDecisionWithNoChance(){
+        Sampling sampling = new Sampling(0, 100, false);
+        for (int i = 0; i < 2000; i++) {
+            Assert.assertFalse(sampling.samplingDecision(new Granularity(GranularityType.METHOD, "function")));
+        }
+        Assert.assertEquals(0, sampling.getSample().getTotalItems());
+        Assert.assertEquals(2000, sampling.getPopulation().getTotalItems());
+    }
+
+    @Test
+    public void samplingDecisionWith100Chance(){
+        Sampling sampling = new Sampling(1, 100, false);
+        for (int i = 0; i < 2000; i++) {
+            Assert.assertTrue(sampling.samplingDecision(new Granularity(GranularityType.METHOD, "function")));
+        }
+        Assert.assertEquals(2000, sampling.getSample().getTotalItems());
+        Assert.assertEquals(2000, sampling.getPopulation().getTotalItems());
     }
 }