killbill-memoizeit

analytics: force a full refresh for account and tag events Account

4/30/2013 11:09:36 AM

Details

diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/AnalyticsListener.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/AnalyticsListener.java
index 3a18bb0..5e2afc3 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/AnalyticsListener.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/AnalyticsListener.java
@@ -27,6 +27,7 @@ import com.ning.billing.beatrix.bus.api.ExtBusEvent;
 import com.ning.billing.commons.locker.GlobalLock;
 import com.ning.billing.commons.locker.GlobalLocker;
 import com.ning.billing.commons.locker.mysql.MySqlGlobalLocker;
+import com.ning.billing.osgi.bundles.analytics.dao.AllBusinessObjectsDao;
 import com.ning.billing.osgi.bundles.analytics.dao.BusinessAccountDao;
 import com.ning.billing.osgi.bundles.analytics.dao.BusinessFieldDao;
 import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceAndInvoicePaymentDao;
@@ -53,6 +54,7 @@ public class AnalyticsListener implements OSGIKillbillEventHandler {
     private final BusinessOverdueStatusDao bosDao;
     private final BusinessFieldDao bFieldDao;
     private final BusinessTagDao bTagDao;
+    private final AllBusinessObjectsDao allBusinessObjectsDao;
     private final GlobalLocker locker;
 
     public AnalyticsListener(final OSGIKillbillLogService logService,
@@ -66,7 +68,9 @@ public class AnalyticsListener implements OSGIKillbillEventHandler {
         this.bosDao = new BusinessOverdueStatusDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
         this.bFieldDao = new BusinessFieldDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
         this.bTagDao = new BusinessTagDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
+        this.allBusinessObjectsDao = new AllBusinessObjectsDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
 
+        // TODO Do we still need it?
         this.locker = new MySqlGlobalLocker(osgiKillbillDataSource.getDataSource());
     }
 
@@ -77,7 +81,9 @@ public class AnalyticsListener implements OSGIKillbillEventHandler {
         switch (killbillEvent.getEventType()) {
             case ACCOUNT_CREATION:
             case ACCOUNT_CHANGE:
-                handleAccountEvent(killbillEvent, callContext);
+                // Note: account information is denormalized across all tables, we pretty much
+                // have to refresh all objects
+                handleFullRefreshEvent(killbillEvent, callContext);
                 break;
             case SUBSCRIPTION_CREATION:
             case SUBSCRIPTION_CHANGE:
@@ -97,7 +103,9 @@ public class AnalyticsListener implements OSGIKillbillEventHandler {
                 break;
             case TAG_CREATION:
             case TAG_DELETION:
-                handleTagEvent(killbillEvent, callContext);
+                // Note: tags determine the report group. Since it is denormalized across all tables, we pretty much
+                // have to refresh all objects
+                handleFullRefreshEvent(killbillEvent, callContext);
                 break;
             case CUSTOM_FIELD_CREATION:
             case CUSTOM_FIELD_DELETION:
@@ -178,6 +186,16 @@ public class AnalyticsListener implements OSGIKillbillEventHandler {
         });
     }
 
+    private void handleFullRefreshEvent(final ExtBusEvent killbillEvent, final CallContext callContext) {
+        updateWithAccountLock(killbillEvent, new Callable<Void>() {
+            @Override
+            public Void call() throws Exception {
+                allBusinessObjectsDao.update(killbillEvent.getAccountId(), callContext);
+                return null;
+            }
+        });
+    }
+
     private static final class AnalyticsCallContext implements CallContext {
 
         private static final String USER_NAME = AnalyticsListener.class.getName();
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/user/AnalyticsUserApi.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/user/AnalyticsUserApi.java
index 1c53388..b2ac8b4 100644
--- a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/user/AnalyticsUserApi.java
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/api/user/AnalyticsUserApi.java
@@ -19,9 +19,6 @@ package com.ning.billing.osgi.bundles.analytics.api.user;
 import java.util.Collection;
 import java.util.UUID;
 
-import org.osgi.service.log.LogService;
-
-import com.ning.billing.ObjectType;
 import com.ning.billing.osgi.bundles.analytics.AnalyticsRefreshException;
 import com.ning.billing.osgi.bundles.analytics.api.BusinessAccount;
 import com.ning.billing.osgi.bundles.analytics.api.BusinessField;
@@ -31,13 +28,8 @@ import com.ning.billing.osgi.bundles.analytics.api.BusinessOverdueStatus;
 import com.ning.billing.osgi.bundles.analytics.api.BusinessSnapshot;
 import com.ning.billing.osgi.bundles.analytics.api.BusinessSubscriptionTransition;
 import com.ning.billing.osgi.bundles.analytics.api.BusinessTag;
+import com.ning.billing.osgi.bundles.analytics.dao.AllBusinessObjectsDao;
 import com.ning.billing.osgi.bundles.analytics.dao.AnalyticsDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessAccountDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessFieldDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessInvoiceAndInvoicePaymentDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessOverdueStatusDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessSubscriptionTransitionDao;
-import com.ning.billing.osgi.bundles.analytics.dao.BusinessTagDao;
 import com.ning.billing.util.callcontext.CallContext;
 import com.ning.billing.util.callcontext.TenantContext;
 import com.ning.killbill.osgi.libs.killbill.OSGIKillbillAPI;
@@ -46,26 +38,14 @@ import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
 
 public class AnalyticsUserApi {
 
-    private final LogService logService;
     private final AnalyticsDao analyticsDao;
-    private final BusinessSubscriptionTransitionDao bstDao;
-    private final BusinessInvoiceAndInvoicePaymentDao binAndBipDao;
-    private final BusinessOverdueStatusDao bosDao;
-    private final BusinessFieldDao bFieldDao;
-    private final BusinessTagDao bTagDao;
+    private final AllBusinessObjectsDao allBusinessObjectsDao;
 
     public AnalyticsUserApi(final OSGIKillbillLogService logService,
                             final OSGIKillbillAPI osgiKillbillAPI,
                             final OSGIKillbillDataSource osgiKillbillDataSource) {
-        this.logService = logService;
         this.analyticsDao = new AnalyticsDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
-
-        final BusinessAccountDao bacDao = new BusinessAccountDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
-        this.bstDao = new BusinessSubscriptionTransitionDao(logService, osgiKillbillAPI, osgiKillbillDataSource, bacDao);
-        this.binAndBipDao = new BusinessInvoiceAndInvoicePaymentDao(logService, osgiKillbillAPI, osgiKillbillDataSource, bacDao);
-        this.bosDao = new BusinessOverdueStatusDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
-        this.bFieldDao = new BusinessFieldDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
-        this.bTagDao = new BusinessTagDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
+        this.allBusinessObjectsDao = new AllBusinessObjectsDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
     }
 
     public BusinessSnapshot getBusinessSnapshot(final UUID accountId, final TenantContext context) {
@@ -98,23 +78,7 @@ public class AnalyticsUserApi {
     }
 
     public void rebuildAnalyticsForAccount(final UUID accountId, final CallContext context) throws AnalyticsRefreshException {
-        logService.log(LogService.LOG_INFO, "Starting rebuild of Analytics for account " + accountId);
-
-        // Refresh invoices and payments. This will automatically trigger a refresh of account
-        binAndBipDao.update(accountId, context);
-
-        // Refresh BST
-        bstDao.update(accountId, context);
-
-        // Refresh tags
-        bTagDao.update(accountId, context);
-
-        // Refresh fields
-        bFieldDao.update(accountId, context);
-
-        // Refresh BOS (bundles only for now)
-        bosDao.update(accountId, ObjectType.BUNDLE, context);
-
-        logService.log(LogService.LOG_INFO, "Finished rebuild of Analytics for account " + accountId);
+        // TODO Should we take the account lock?
+        allBusinessObjectsDao.update(accountId, context);
     }
 }
diff --git a/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/AllBusinessObjectsDao.java b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/AllBusinessObjectsDao.java
new file mode 100644
index 0000000..49f95d8
--- /dev/null
+++ b/osgi-bundles/bundles/analytics/src/main/java/com/ning/billing/osgi/bundles/analytics/dao/AllBusinessObjectsDao.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2010-2013 Ning, Inc.
+ *
+ * Ning 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 com.ning.billing.osgi.bundles.analytics.dao;
+
+import java.util.UUID;
+
+import org.osgi.service.log.LogService;
+
+import com.ning.billing.ObjectType;
+import com.ning.billing.osgi.bundles.analytics.AnalyticsRefreshException;
+import com.ning.billing.util.callcontext.CallContext;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillAPI;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillDataSource;
+import com.ning.killbill.osgi.libs.killbill.OSGIKillbillLogService;
+
+public class AllBusinessObjectsDao {
+
+    private final LogService logService;
+    private final BusinessSubscriptionTransitionDao bstDao;
+    private final BusinessInvoiceAndInvoicePaymentDao binAndBipDao;
+    private final BusinessOverdueStatusDao bosDao;
+    private final BusinessFieldDao bFieldDao;
+    private final BusinessTagDao bTagDao;
+
+    public AllBusinessObjectsDao(final OSGIKillbillLogService logService,
+                                 final OSGIKillbillAPI osgiKillbillAPI,
+                                 final OSGIKillbillDataSource osgiKillbillDataSource) {
+        this.logService = logService;
+
+        final BusinessAccountDao bacDao = new BusinessAccountDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
+        this.bstDao = new BusinessSubscriptionTransitionDao(logService, osgiKillbillAPI, osgiKillbillDataSource, bacDao);
+        this.binAndBipDao = new BusinessInvoiceAndInvoicePaymentDao(logService, osgiKillbillAPI, osgiKillbillDataSource, bacDao);
+        this.bosDao = new BusinessOverdueStatusDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
+        this.bFieldDao = new BusinessFieldDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
+        this.bTagDao = new BusinessTagDao(logService, osgiKillbillAPI, osgiKillbillDataSource);
+    }
+
+    // TODO: each refresh is done in a transaction - do we want to share a long running transaction across all refreshes?
+    public void update(final UUID accountId, final CallContext context) throws AnalyticsRefreshException {
+        logService.log(LogService.LOG_INFO, "Starting rebuild of Analytics for account " + accountId);
+
+        // Refresh invoices and payments. This will automatically trigger a refresh of account
+        binAndBipDao.update(accountId, context);
+
+        // Refresh BST
+        bstDao.update(accountId, context);
+
+        // Refresh tags
+        bTagDao.update(accountId, context);
+
+        // Refresh fields
+        bFieldDao.update(accountId, context);
+
+        // Refresh BOS (bundles only for now)
+        bosDao.update(accountId, ObjectType.BUNDLE, context);
+
+        logService.log(LogService.LOG_INFO, "Finished rebuild of Analytics for account " + accountId);
+    }
+}