You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by sh...@apache.org on 2017/05/01 19:09:02 UTC

incubator-unomi git commit: UNOMI-93 Add rule execution statistics to rule service endpoint - Initial commit

Repository: incubator-unomi
Updated Branches:
  refs/heads/master 28b63e665 -> df6acb3f1


UNOMI-93 Add rule execution statistics to rule service endpoint
- Initial commit

Signed-off-by: Serge Huber <sh...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/incubator-unomi/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-unomi/commit/df6acb3f
Tree: http://git-wip-us.apache.org/repos/asf/incubator-unomi/tree/df6acb3f
Diff: http://git-wip-us.apache.org/repos/asf/incubator-unomi/diff/df6acb3f

Branch: refs/heads/master
Commit: df6acb3f1ca5c526cebddc3b66c5fd32d88b6974
Parents: 28b63e6
Author: Serge Huber <sh...@apache.org>
Authored: Mon May 1 21:08:54 2017 +0200
Committer: Serge Huber <sh...@apache.org>
Committed: Mon May 1 21:08:54 2017 +0200

----------------------------------------------------------------------
 .../apache/unomi/api/rules/RuleStatistics.java  | 107 +++++++++++++++++
 .../apache/unomi/api/services/RulesService.java |   8 ++
 itests/pom.xml                                  |   4 +-
 .../apache/unomi/rest/RulesServiceEndPoint.java |  13 +++
 .../services/services/RulesServiceImpl.java     | 117 ++++++++++++++++++-
 5 files changed, 244 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/df6acb3f/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java b/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java
new file mode 100644
index 0000000..2172461
--- /dev/null
+++ b/api/src/main/java/org/apache/unomi/api/rules/RuleStatistics.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.unomi.api.rules;
+
+import org.apache.unomi.api.Item;
+
+import java.util.Date;
+
+/**
+ * A separate item to track rule statistics, because we will manage the persistence and updating of these seperately
+ * from the rules themselves. This object contains all the relevant statistics concerning the execution of a rule.
+ */
+public class RuleStatistics extends Item {
+
+    /**
+     * The Rule ITEM_TYPE.
+     *
+     * @see Item for a discussion of ITEM_TYPE
+     */
+    public static final String ITEM_TYPE = "rulestats";
+    private static final long serialVersionUID = 1L;
+
+    private long executionCount = 0;
+    private long localExecutionCount = 0;
+    private long conditionsTime = 0;
+    private long localConditionsTime = 0;
+    private long actionsTime = 0;
+    private long localActionsTime = 0;
+    private Date lastSyncDate;
+
+    public RuleStatistics() {
+    }
+
+    public RuleStatistics(String itemId) {
+        super(itemId);
+    }
+
+    public long getExecutionCount() {
+        return executionCount;
+    }
+
+    public void setExecutionCount(long executionCount) {
+        this.executionCount = executionCount;
+    }
+
+    public long getLocalExecutionCount() {
+        return localExecutionCount;
+    }
+
+    public void setLocalExecutionCount(long localExecutionCount) {
+        this.localExecutionCount = localExecutionCount;
+    }
+
+    public long getConditionsTime() {
+        return conditionsTime;
+    }
+
+    public void setConditionsTime(long conditionsTime) {
+        this.conditionsTime = conditionsTime;
+    }
+
+    public long getLocalConditionsTime() {
+        return localConditionsTime;
+    }
+
+    public void setLocalConditionsTime(long localConditionsTime) {
+        this.localConditionsTime = localConditionsTime;
+    }
+
+    public long getActionsTime() {
+        return actionsTime;
+    }
+
+    public void setActionsTime(long actionsTime) {
+        this.actionsTime = actionsTime;
+    }
+
+    public long getLocalActionsTime() {
+        return localActionsTime;
+    }
+
+    public void setLocalActionsTime(long localActionsTime) {
+        this.localActionsTime = localActionsTime;
+    }
+
+    public Date getLastSyncDate() {
+        return lastSyncDate;
+    }
+
+    public void setLastSyncDate(Date lastSyncDate) {
+        this.lastSyncDate = lastSyncDate;
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/df6acb3f/api/src/main/java/org/apache/unomi/api/services/RulesService.java
----------------------------------------------------------------------
diff --git a/api/src/main/java/org/apache/unomi/api/services/RulesService.java b/api/src/main/java/org/apache/unomi/api/services/RulesService.java
index 75d1f07..63f6dda 100644
--- a/api/src/main/java/org/apache/unomi/api/services/RulesService.java
+++ b/api/src/main/java/org/apache/unomi/api/services/RulesService.java
@@ -23,6 +23,7 @@ import org.apache.unomi.api.PartialList;
 import org.apache.unomi.api.conditions.Condition;
 import org.apache.unomi.api.query.Query;
 import org.apache.unomi.api.rules.Rule;
+import org.apache.unomi.api.rules.RuleStatistics;
 
 import java.util.Set;
 
@@ -55,6 +56,13 @@ public interface RulesService {
     Rule getRule(String ruleId);
 
     /**
+     * Retrieves the statistics for a rule
+     * @param ruleId the identifier of the rule
+     * @return a long representing the number of times the rule was matched and executed.
+     */
+    RuleStatistics getRuleStatistics(String ruleId);
+
+    /**
      * Persists the specified rule to the context server.
      *
      * @param rule the rule to be persisted

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/df6acb3f/itests/pom.xml
----------------------------------------------------------------------
diff --git a/itests/pom.xml b/itests/pom.xml
index 3b02af1..868fb1f 100644
--- a/itests/pom.xml
+++ b/itests/pom.xml
@@ -144,12 +144,12 @@
                 <groupId>com.github.alexcojocaru</groupId>
                 <artifactId>elasticsearch-maven-plugin</artifactId>
                 <!-- REPLACE THE FOLLOWING WITH THE PLUGIN VERSION YOU NEED -->
-                <version>5.0</version>
+                <version>5.7</version>
                 <configuration>
                     <clusterName>contextElasticSearch</clusterName>
                     <transportPort>9300</transportPort>
                     <httpPort>9200</httpPort>
-                    <version>5.0.2</version>
+                    <version>${elasticsearch.version}</version>
                 </configuration>
                 <executions>
                     <!--

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/df6acb3f/rest/src/main/java/org/apache/unomi/rest/RulesServiceEndPoint.java
----------------------------------------------------------------------
diff --git a/rest/src/main/java/org/apache/unomi/rest/RulesServiceEndPoint.java b/rest/src/main/java/org/apache/unomi/rest/RulesServiceEndPoint.java
index 8559ed4..d619a7f 100644
--- a/rest/src/main/java/org/apache/unomi/rest/RulesServiceEndPoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/RulesServiceEndPoint.java
@@ -22,6 +22,7 @@ import org.apache.unomi.api.Metadata;
 import org.apache.unomi.api.PartialList;
 import org.apache.unomi.api.query.Query;
 import org.apache.unomi.api.rules.Rule;
+import org.apache.unomi.api.rules.RuleStatistics;
 import org.apache.unomi.api.services.RulesService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -104,6 +105,18 @@ public class RulesServiceEndPoint {
     }
 
     /**
+     * Retrieves the statistics for the rule with the specified identifier
+     *
+     * @param ruleId the identifier of the rule we want to retrieve
+     * @return the statistics for the specified rule or {@code null} if no such rule exists.
+     */
+    @GET
+    @Path("/{ruleId}/statistics")
+    public RuleStatistics getRuleStatistics(@PathParam("ruleId") String ruleId) {
+        return rulesService.getRuleStatistics(ruleId);
+    }
+
+    /**
      * Deletes the rule identified by the specified identifier.
      *
      * @param ruleId the identifier of the rule we want to delete

http://git-wip-us.apache.org/repos/asf/incubator-unomi/blob/df6acb3f/services/src/main/java/org/apache/unomi/services/services/RulesServiceImpl.java
----------------------------------------------------------------------
diff --git a/services/src/main/java/org/apache/unomi/services/services/RulesServiceImpl.java b/services/src/main/java/org/apache/unomi/services/services/RulesServiceImpl.java
index 2df2fff..c289891 100644
--- a/services/src/main/java/org/apache/unomi/services/services/RulesServiceImpl.java
+++ b/services/src/main/java/org/apache/unomi/services/services/RulesServiceImpl.java
@@ -26,6 +26,7 @@ import org.apache.unomi.api.actions.ActionExecutor;
 import org.apache.unomi.api.conditions.Condition;
 import org.apache.unomi.api.query.Query;
 import org.apache.unomi.api.rules.Rule;
+import org.apache.unomi.api.rules.RuleStatistics;
 import org.apache.unomi.api.services.DefinitionsService;
 import org.apache.unomi.api.services.EventListenerService;
 import org.apache.unomi.api.services.EventService;
@@ -57,6 +58,9 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
     private List<Rule> allRules;
 
     private Timer rulesTimer;
+    private Timer ruleStatisticsTimer;
+
+    private Map<String,RuleStatistics> allRuleStatistics = new HashMap<String,RuleStatistics>();
 
     public void setBundleContext(BundleContext bundleContext) {
         this.bundleContext = bundleContext;
@@ -102,7 +106,7 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
 
         bundleContext.addBundleListener(this);
 
-        initializeTimer();
+        initializeTimers();
         logger.info("Rule service initialized.");
     }
 
@@ -116,6 +120,9 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
         if(rulesTimer != null) {
             rulesTimer.cancel();
         }
+        if (ruleStatisticsTimer != null) {
+            ruleStatisticsTimer.cancel();
+        }
         logger.info("Rule purge: Purge unscheduled");
     }
 
@@ -161,41 +168,50 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
         List<Rule> allItems = allRules;
 
         for (Rule rule : allItems) {
+            RuleStatistics ruleStatistics = getLocalRuleStatistics(rule);
+            long ruleConditionStartTime = System.currentTimeMillis();
             String scope = rule.getMetadata().getScope();
             if (scope.equals(Metadata.SYSTEM_SCOPE) || scope.equals(event.getScope())) {
                 Condition eventCondition = definitionsService.extractConditionByTag(rule.getCondition(), "eventCondition");
 
                 if (eventCondition == null) {
+                    updateRuleStatistics(ruleStatistics, ruleConditionStartTime);
                     continue;
                 }
 
                 if (!persistenceService.testMatch(eventCondition, event)) {
+                    updateRuleStatistics(ruleStatistics, ruleConditionStartTime);
                     continue;
                 }
 
                 Condition sourceCondition = definitionsService.extractConditionByTag(rule.getCondition(), "sourceEventCondition");
                 if (sourceCondition != null && !persistenceService.testMatch(sourceCondition, event.getSource())) {
+                    updateRuleStatistics(ruleStatistics, ruleConditionStartTime);
                     continue;
                 }
 
                 if (rule.isRaiseEventOnlyOnceForProfile()) {
                     hasEventAlreadyBeenRaisedForProfile = hasEventAlreadyBeenRaisedForProfile != null ? hasEventAlreadyBeenRaisedForProfile : eventService.hasEventAlreadyBeenRaised(event, false);
                     if (hasEventAlreadyBeenRaisedForProfile) {
+                        updateRuleStatistics(ruleStatistics, ruleConditionStartTime);
                         continue;
                     }
                 } else if (rule.isRaiseEventOnlyOnceForSession()) {
                     hasEventAlreadyBeenRaisedForSession = hasEventAlreadyBeenRaisedForSession != null ? hasEventAlreadyBeenRaisedForSession : eventService.hasEventAlreadyBeenRaised(event, true);
                     if (hasEventAlreadyBeenRaisedForSession) {
+                        updateRuleStatistics(ruleStatistics, ruleConditionStartTime);
                         continue;
                     }
                 }
 
                 Condition profileCondition = definitionsService.extractConditionByTag(rule.getCondition(), "profileCondition");
                 if (profileCondition != null && !persistenceService.testMatch(profileCondition, event.getProfile())) {
+                    updateRuleStatistics(ruleStatistics, ruleConditionStartTime);
                     continue;
                 }
                 Condition sessionCondition = definitionsService.extractConditionByTag(rule.getCondition(), "sessionCondition");
                 if (sessionCondition != null && !persistenceService.testMatch(sessionCondition, event.getSession())) {
+                    updateRuleStatistics(ruleStatistics, ruleConditionStartTime);
                     continue;
                 }
                 matchedRules.add(rule);
@@ -205,6 +221,20 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
         return matchedRules;
     }
 
+    private RuleStatistics getLocalRuleStatistics(Rule rule) {
+        RuleStatistics ruleStatistics = this.allRuleStatistics.get(rule.getItemId());
+        if (ruleStatistics == null) {
+            ruleStatistics = new RuleStatistics(rule.getItemId());
+        }
+        return ruleStatistics;
+    }
+
+    private void updateRuleStatistics(RuleStatistics ruleStatistics, long ruleConditionStartTime) {
+        long totalRuleConditionTime = System.currentTimeMillis() - ruleConditionStartTime;
+        ruleStatistics.setLocalConditionsTime(ruleStatistics.getLocalConditionsTime() + totalRuleConditionTime);
+        allRuleStatistics.put(ruleStatistics.getItemId(), ruleStatistics);
+    }
+
     private List<Rule> getAllRules() {
         List<Rule> allItems = persistenceService.getAllItems(Rule.class, 0, -1, "priority").getList();
         for (Rule rule : allItems) {
@@ -225,18 +255,32 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
         int changes = EventService.NO_CHANGE;
         for (Rule rule : rules) {
             logger.debug("Fired rule " + rule.getMetadata().getId() + " for " + event.getEventType() + " - " + event.getItemId());
+            long actionsStartTime = System.currentTimeMillis();
             for (Action action : rule.getActions()) {
                 changes |= actionExecutorDispatcher.execute(action, event);
             }
-
+            long totalActionsTime = System.currentTimeMillis() - actionsStartTime;
             Event ruleFired = new Event("ruleFired", event.getSession(), event.getProfile(), event.getScope(), event, rule, event.getTimeStamp());
             ruleFired.getAttributes().putAll(event.getAttributes());
             ruleFired.setPersistent(false);
             changes |= eventService.send(ruleFired);
+
+            RuleStatistics ruleStatistics = getLocalRuleStatistics(rule);
+            ruleStatistics.setLocalExecutionCount(ruleStatistics.getLocalExecutionCount()+1);
+            ruleStatistics.setLocalActionsTime(ruleStatistics.getLocalActionsTime() + totalActionsTime);
+            this.allRuleStatistics.put(rule.getItemId(), ruleStatistics);
         }
         return changes;
     }
 
+    @Override
+    public RuleStatistics getRuleStatistics(String ruleId) {
+        if (allRuleStatistics.containsKey(ruleId)) {
+            return allRuleStatistics.get(ruleId);
+        }
+        return persistenceService.load(ruleId, RuleStatistics.class);
+    }
+
     public Set<Metadata> getRuleMetadatas() {
         Set<Metadata> metadatas = new HashSet<Metadata>();
         for (Rule rule : persistenceService.getAllItems(Rule.class, 0, 50, null).getList()) {
@@ -309,7 +353,7 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
         persistenceService.remove(ruleId, Rule.class);
     }
 
-    private void initializeTimer() {
+    private void initializeTimers() {
         rulesTimer = new Timer();
         TimerTask task = new TimerTask() {
             @Override
@@ -318,6 +362,14 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
             }
         };
         rulesTimer.schedule(task, 0, 1000);
+        ruleStatisticsTimer = new Timer();
+        TimerTask statisticsTask = new TimerTask() {
+            @Override
+            public void run() {
+                syncRuleStatistics();
+            }
+        };
+        ruleStatisticsTimer.schedule(statisticsTask, 0, 10000);
     }
 
     public void bundleChanged(BundleEvent event) {
@@ -330,4 +382,63 @@ public class RulesServiceImpl implements RulesService, EventListenerService, Syn
                 break;
         }
     }
+
+    private void syncRuleStatistics() {
+        List<RuleStatistics> allPersistedRuleStatisticsList = persistenceService.getAllItems(RuleStatistics.class);
+        Map<String,RuleStatistics> allPersistedRuleStatistics = new HashMap<>();
+        for (RuleStatistics ruleStatistics : allPersistedRuleStatisticsList) {
+            allPersistedRuleStatistics.put(ruleStatistics.getItemId(), ruleStatistics);
+        }
+        // first we iterate over the rules we have in memory
+        for (RuleStatistics ruleStatistics : allRuleStatistics.values()) {
+            boolean mustPersist = false;
+            if (allPersistedRuleStatistics.containsKey(ruleStatistics.getItemId())) {
+                // we must sync with the data coming from the persistence service.
+                RuleStatistics persistedRuleStatistics = allPersistedRuleStatistics.get(ruleStatistics.getItemId());
+                ruleStatistics.setExecutionCount(persistedRuleStatistics.getExecutionCount() + ruleStatistics.getLocalExecutionCount());
+                if (ruleStatistics.getLocalExecutionCount() > 0) {
+                    ruleStatistics.setLocalExecutionCount(0);
+                    mustPersist = true;
+                }
+                ruleStatistics.setConditionsTime(persistedRuleStatistics.getConditionsTime() + ruleStatistics.getLocalConditionsTime());
+                if (ruleStatistics.getLocalConditionsTime() > 0) {
+                    ruleStatistics.setLocalConditionsTime(0);
+                    mustPersist = true;
+                }
+                ruleStatistics.setActionsTime(persistedRuleStatistics.getActionsTime() + ruleStatistics.getLocalActionsTime());
+                if (ruleStatistics.getLocalActionsTime() > 0) {
+                    ruleStatistics.setLocalActionsTime(0);
+                    mustPersist = true;
+                }
+                ruleStatistics.setLastSyncDate(new Date());
+            } else {
+                ruleStatistics.setExecutionCount(ruleStatistics.getExecutionCount() + ruleStatistics.getLocalExecutionCount());
+                if (ruleStatistics.getLocalExecutionCount() > 0) {
+                    ruleStatistics.setLocalExecutionCount(0);
+                    mustPersist = true;
+                }
+                ruleStatistics.setConditionsTime(ruleStatistics.getConditionsTime() + ruleStatistics.getLocalConditionsTime());
+                if (ruleStatistics.getLocalConditionsTime() > 0) {
+                    ruleStatistics.setLocalConditionsTime(0);
+                    mustPersist = true;
+                }
+                ruleStatistics.setActionsTime(ruleStatistics.getActionsTime() + ruleStatistics.getLocalActionsTime());
+                if (ruleStatistics.getLocalActionsTime() > 0) {
+                    ruleStatistics.setLocalActionsTime(0);
+                    mustPersist = true;
+                }
+                ruleStatistics.setLastSyncDate(new Date());
+            }
+            allRuleStatistics.put(ruleStatistics.getItemId(), ruleStatistics);
+            if (mustPersist) {
+                persistenceService.save(ruleStatistics);
+            }
+        }
+        // now let's iterate over the rules coming from the persistence service, as we may have new ones.
+        for (RuleStatistics ruleStatistics : allPersistedRuleStatistics.values()) {
+            if (!allRuleStatistics.containsKey(ruleStatistics.getItemId())) {
+                allRuleStatistics.put(ruleStatistics.getItemId(), ruleStatistics);
+            }
+        }
+    }
 }