killbill-aplcache

Plug multi-node cache invalidation for low level tenant-kv

3/2/2015 8:53:47 PM

Details

diff --git a/api/src/main/java/org/killbill/billing/tenant/api/TenantInternalApi.java b/api/src/main/java/org/killbill/billing/tenant/api/TenantInternalApi.java
index 785d369..a0bb81a 100644
--- a/api/src/main/java/org/killbill/billing/tenant/api/TenantInternalApi.java
+++ b/api/src/main/java/org/killbill/billing/tenant/api/TenantInternalApi.java
@@ -27,7 +27,13 @@ import org.killbill.billing.util.callcontext.TenantContext;
 public interface TenantInternalApi {
 
     public interface CacheInvalidationCallback {
-        public void invalidateCache(InternalTenantContext tenantContext);
+
+        /**
+         * @param key           the TenantKey
+         * @param cookie        the cookie that should be interpreted by the specific implementation of the CacheInvalidationCallback.
+         * @param tenantContext the context containing the tenant info
+         */
+        public void invalidateCache(TenantKey key, Object cookie, InternalTenantContext tenantContext);
     }
 
     public void initializeCacheInvalidationCallback(final TenantKey key, final CacheInvalidationCallback cacheInvalidationCallback);
diff --git a/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCacheInvalidationCallback.java b/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCacheInvalidationCallback.java
index eef2e24..63548dc 100644
--- a/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCacheInvalidationCallback.java
+++ b/catalog/src/main/java/org/killbill/billing/catalog/caching/CatalogCacheInvalidationCallback.java
@@ -21,12 +21,13 @@ import javax.inject.Inject;
 
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
+import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 public class CatalogCacheInvalidationCallback implements CacheInvalidationCallback {
 
-    private final Logger logger = LoggerFactory.getLogger(CatalogCacheInvalidationCallback.class);
+    private final Logger log = LoggerFactory.getLogger(CatalogCacheInvalidationCallback.class);
 
     private final CatalogCache catalogCache;
 
@@ -36,8 +37,8 @@ public class CatalogCacheInvalidationCallback implements CacheInvalidationCallba
     }
 
     @Override
-    public void invalidateCache(final InternalTenantContext tenantContext) {
-        logger.info("Invalidate catalog cache for tenant " + tenantContext.getTenantRecordId());
+    public void invalidateCache(TenantKey key, final Object cookie, final InternalTenantContext tenantContext) {
+        log.info("Invalidate catalog cache for tenant {} ", tenantContext.getTenantRecordId());
         catalogCache.clearCatalog(tenantContext);
     }
 }
diff --git a/overdue/src/main/java/org/killbill/billing/overdue/caching/OverdueCacheInvalidationCallback.java b/overdue/src/main/java/org/killbill/billing/overdue/caching/OverdueCacheInvalidationCallback.java
index c16ae9f..e7fb85e 100644
--- a/overdue/src/main/java/org/killbill/billing/overdue/caching/OverdueCacheInvalidationCallback.java
+++ b/overdue/src/main/java/org/killbill/billing/overdue/caching/OverdueCacheInvalidationCallback.java
@@ -19,6 +19,7 @@ package org.killbill.billing.overdue.caching;
 
 import org.killbill.billing.callcontext.InternalTenantContext;
 import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
+import org.killbill.billing.tenant.api.TenantKV.TenantKey;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -26,7 +27,7 @@ import com.google.inject.Inject;
 
 public class OverdueCacheInvalidationCallback implements CacheInvalidationCallback {
 
-    private static final Logger logger = LoggerFactory.getLogger(OverdueCacheInvalidationCallback.class);
+    private static final Logger log = LoggerFactory.getLogger(OverdueCacheInvalidationCallback.class);
 
     private final OverdueConfigCache overdueConfigCache;
 
@@ -36,8 +37,8 @@ public class OverdueCacheInvalidationCallback implements CacheInvalidationCallba
     }
 
     @Override
-    public void invalidateCache(final InternalTenantContext tenantContext) {
-        logger.info("Invalidate overdue cache for tenant " + tenantContext.getTenantRecordId());
+    public void invalidateCache(TenantKey key, final Object cookie, final InternalTenantContext tenantContext) {
+        log.info("Invalidate overdue cache for tenant {} ", tenantContext.getTenantRecordId());
         overdueConfigCache.clearOverdueConfig(tenantContext);
     }
 }
diff --git a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentRoutingPluginApi.java b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentRoutingPluginApi.java
index 5760da4..4567bbf 100644
--- a/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentRoutingPluginApi.java
+++ b/payment/src/main/java/org/killbill/billing/payment/invoice/InvoicePaymentRoutingPluginApi.java
@@ -94,7 +94,7 @@ public final class InvoicePaymentRoutingPluginApi implements PaymentRoutingPlugi
     private final InternalCallContextFactory internalCallContextFactory;
     private final Clock clock;
 
-    private final Logger logger = LoggerFactory.getLogger(InvoicePaymentRoutingPluginApi.class);
+    private final Logger log = LoggerFactory.getLogger(InvoicePaymentRoutingPluginApi.class);
 
     @Inject
     public InvoicePaymentRoutingPluginApi(final PaymentConfig paymentConfig, final InvoiceInternalApi invoiceApi, final TagUserApi tagApi, final PaymentDao paymentDao,
@@ -148,7 +148,7 @@ public final class InvoicePaymentRoutingPluginApi implements PaymentRoutingPlugi
                     final UUID invoiceId = getInvoiceId(paymentRoutingContext);
                     existingInvoicePayment = invoiceApi.getInvoicePaymentForAttempt(paymentRoutingContext.getPaymentId(), internalContext);
                     if (existingInvoicePayment != null) {
-                        logger.info("onSuccessCall was already completed for payment purchase :" + paymentRoutingContext.getPaymentId());
+                        log.info("onSuccessCall was already completed for payment purchase :" + paymentRoutingContext.getPaymentId());
                     } else {
                         invoiceApi.notifyOfPayment(invoiceId,
                                                    paymentRoutingContext.getAmount(),
@@ -163,7 +163,7 @@ public final class InvoicePaymentRoutingPluginApi implements PaymentRoutingPlugi
                 case REFUND:
                     existingInvoicePayment = invoiceApi.getInvoicePaymentForRefund(paymentRoutingContext.getPaymentId(), internalContext);
                     if (existingInvoicePayment != null) {
-                        logger.info("onSuccessCall was already completed for payment refund :" + paymentRoutingContext.getPaymentId());
+                        log.info("onSuccessCall was already completed for payment refund :" + paymentRoutingContext.getPaymentId());
                     } else {
                         final Map<UUID, BigDecimal> idWithAmount = extractIdsWithAmountFromProperties(paymentRoutingContext.getPluginProperties());
                         final PluginProperty prop = getPluginProperty(paymentRoutingContext.getPluginProperties(), PROP_IPCD_REFUND_WITH_ADJUSTMENTS);
@@ -175,7 +175,7 @@ public final class InvoicePaymentRoutingPluginApi implements PaymentRoutingPlugi
                 case CHARGEBACK:
                     existingInvoicePayment = invoiceApi.getInvoicePaymentForChargeback(paymentRoutingContext.getPaymentId(), internalContext);
                     if (existingInvoicePayment != null) {
-                        logger.info("onSuccessCall was already completed for payment chargeback :" + paymentRoutingContext.getPaymentId());
+                        log.info("onSuccessCall was already completed for payment chargeback :" + paymentRoutingContext.getPaymentId());
                     } else {
                         invoiceApi.createChargeback(paymentRoutingContext.getPaymentId(), paymentRoutingContext.getProcessedAmount(), paymentRoutingContext.getProcessedCurrency(), internalContext);
                     }
@@ -185,7 +185,7 @@ public final class InvoicePaymentRoutingPluginApi implements PaymentRoutingPlugi
                     throw new IllegalStateException("Unexpected transactionType " + transactionType);
             }
         } catch (final InvoiceApiException e) {
-            logger.error("InvoicePaymentRoutingPluginApi onSuccessCall failed for attemptId = " + paymentRoutingContext.getAttemptPaymentId() + ", transactionType  = " + transactionType, e);
+            log.error("InvoicePaymentRoutingPluginApi onSuccessCall failed for attemptId = " + paymentRoutingContext.getAttemptPaymentId() + ", transactionType  = " + transactionType, e);
         }
         return null;
     }
@@ -376,7 +376,7 @@ public final class InvoicePaymentRoutingPluginApi implements PaymentRoutingPlugi
                 retryInDays = retryDays.get(retryCount);
                 result = nextRetryDate.plusDays(retryInDays);
             } catch (final NumberFormatException ex) {
-                logger.error("Could not get retry day for retry count {}", retryCount);
+                log.error("Could not get retry day for retry count {}", retryCount);
             }
         }
         return result;
@@ -443,13 +443,13 @@ public final class InvoicePaymentRoutingPluginApi implements PaymentRoutingPlugi
     private BigDecimal validateAndComputePaymentAmount(final Invoice invoice, @Nullable final BigDecimal inputAmount, final boolean isApiPayment) {
 
         if (invoice.getBalance().compareTo(BigDecimal.ZERO) <= 0) {
-            logger.info("Invoice " + invoice.getId() + " has already been paid");
+            log.info("Invoice " + invoice.getId() + " has already been paid");
             return BigDecimal.ZERO;
         }
         if (isApiPayment &&
             inputAmount != null &&
             invoice.getBalance().compareTo(inputAmount) < 0) {
-            logger.info("Invoice " + invoice.getId() +
+            log.info("Invoice " + invoice.getId() +
                         " has a balance of " + invoice.getBalance().floatValue() +
                         " less than retry payment amount of " + inputAmount.floatValue());
             return BigDecimal.ZERO;
diff --git a/profiles/killbill/src/main/java/org/killbill/billing/server/filters/ProfilingContainerResponseFilter.java b/profiles/killbill/src/main/java/org/killbill/billing/server/filters/ProfilingContainerResponseFilter.java
index e5d707e..38402b5 100644
--- a/profiles/killbill/src/main/java/org/killbill/billing/server/filters/ProfilingContainerResponseFilter.java
+++ b/profiles/killbill/src/main/java/org/killbill/billing/server/filters/ProfilingContainerResponseFilter.java
@@ -38,7 +38,7 @@ import com.sun.jersey.spi.container.ContainerResponseFilter;
 @Singleton
 public class ProfilingContainerResponseFilter implements ContainerRequestFilter, ContainerResponseFilter {
 
-    private static final Logger logger = LoggerFactory.getLogger(ProfilingContainerResponseFilter.class);
+    private static final Logger log = LoggerFactory.getLogger(ProfilingContainerResponseFilter.class);
 
     private static final String PROFILING_HEADER_REQ = "X-Killbill-Profiling-Req";
     private static final String PROFILING_HEADER_RESP = "X-Killbill-Profiling-Resp";
@@ -63,7 +63,7 @@ public class ProfilingContainerResponseFilter implements ContainerRequestFilter,
                     profilingData.addStart(ProfilingFeatureType.JAXRS, request.getPath());
                 }
             } catch (IllegalArgumentException e) {
-                logger.info("Profiling data output " + profilingHeaderRequest + " is not supported, profiling NOT enabled");
+                log.info("Profiling data output " + profilingHeaderRequest + " is not supported, profiling NOT enabled");
             }
         }
         return request;
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantService.java b/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantService.java
index 0077fb5..58b732d 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantService.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/DefaultTenantService.java
@@ -20,16 +20,20 @@ import javax.inject.Inject;
 
 import org.killbill.billing.platform.api.LifecycleHandlerType;
 import org.killbill.billing.platform.api.LifecycleHandlerType.LifecycleLevel;
+import org.killbill.billing.tenant.api.TenantKV.TenantKey;
+import org.killbill.billing.tenant.api.user.DefaultTenantUserApi;
 
 public class DefaultTenantService implements TenantService {
 
     private static final String TENANT_SERVICE_NAME = "tenant-service";
 
     private final TenantCacheInvalidation tenantCacheInvalidation;
+    private final TenantCacheInvalidationCallback tenantCacheInvalidationCallback;
 
     @Inject
-    public DefaultTenantService(final TenantCacheInvalidation tenantCacheInvalidation) {
+    public DefaultTenantService(final TenantCacheInvalidation tenantCacheInvalidation, final TenantCacheInvalidationCallback tenantCacheInvalidationCallback) {
         this.tenantCacheInvalidation = tenantCacheInvalidation;
+        this.tenantCacheInvalidationCallback = tenantCacheInvalidationCallback;
     }
 
     @Override
@@ -40,6 +44,9 @@ public class DefaultTenantService implements TenantService {
     @LifecycleHandlerType(LifecycleLevel.INIT_SERVICE)
     public void initialize() {
         tenantCacheInvalidation.initialize();
+        for (TenantKey cacheableKey : DefaultTenantUserApi.CACHED_TENANT_KEY) {
+            tenantCacheInvalidation.registerCallback(cacheableKey, tenantCacheInvalidationCallback);
+        }
     }
 
     @LifecycleHandlerType(LifecycleLevel.START_SERVICE)
@@ -48,7 +55,7 @@ public class DefaultTenantService implements TenantService {
     }
 
     @LifecycleHandlerType(LifecycleLevel.STOP_SERVICE)
-    public void stop()  {
+    public void stop() {
         tenantCacheInvalidation.stop();
     }
 }
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java b/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
index d686fa8..1e61e4c 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidation.java
@@ -37,6 +37,10 @@ import org.killbill.billing.util.config.TenantConfig;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
 /**
  * This class manages the callbacks that have been registered when per tenant objects have been inserted into the
  * tenant_kvs store; the flow is the following (for e.g catalog):
@@ -145,21 +149,65 @@ public class TenantCacheInvalidation {
             if (parent.isStopped) {
                 return;
             }
+
             final List<TenantBroadcastModelDao> entries = broadcastDao.getLatestEntriesFrom(parent.getLatestRecordIdProcessed().get());
             for (TenantBroadcastModelDao cur : entries) {
                 if (parent.isStopped()) {
                     return;
                 }
 
-                final CacheInvalidationCallback callback = parent.getCacheInvalidation(TenantKey.valueOf(cur.getType()));
-                if (callback != null) {
-                    final InternalTenantContext tenantContext = new InternalTenantContext(cur.getTenantRecordId(), null);
-                    callback.invalidateCache(tenantContext);
-                } else {
-                    logger.warn("Failed to find CacheInvalidationCallback for " + cur.getType());
+                try {
+                    final TenantKeyAndCookie tenantKeyAndCookie = extractTenantKeyAndCookie(cur.getType());
+                    if (tenantKeyAndCookie != null) {
+                        final CacheInvalidationCallback callback = parent.getCacheInvalidation(tenantKeyAndCookie.getTenantKey());
+                        if (callback != null) {
+                            final InternalTenantContext tenantContext = new InternalTenantContext(cur.getTenantRecordId(), null);
+                            callback.invalidateCache(tenantKeyAndCookie. getTenantKey(), tenantKeyAndCookie.getCookie(), tenantContext);
+                        } else {
+                            logger.warn("Failed to find CacheInvalidationCallback for " + cur.getType());
+                        }
+                    }
+                } finally {
+                    parent.setLatestRecordIdProcessed(cur.getRecordId());
                 }
-                parent.setLatestRecordIdProcessed(cur.getRecordId());
             }
         }
+
+        private TenantKeyAndCookie extractTenantKeyAndCookie(final String key) {
+            final TenantKey tenantKey = Iterables.tryFind(ImmutableList.copyOf(TenantKey.values()), new Predicate<TenantKey>() {
+                @Override
+                public boolean apply(final TenantKey input) {
+                    return key.startsWith(input.toString());
+                }
+            }).orNull();
+            if (tenantKey == null) {
+                return null;
+            }
+
+            final String cookie = !key.equals(tenantKey.toString()) ?
+                                  key.substring(tenantKey.toString().length()) :
+                                  null;
+            return new TenantKeyAndCookie(tenantKey, cookie);
+        }
+
+    }
+
+    private static final class TenantKeyAndCookie {
+
+        private final TenantKey tenantKey;
+        private final Object cookie;
+
+        public TenantKeyAndCookie(final TenantKey tenantKey, final Object cookie) {
+            this.tenantKey = tenantKey;
+            this.cookie = cookie;
+        }
+
+        public TenantKey getTenantKey() {
+            return tenantKey;
+        }
+
+        public Object getCookie() {
+            return cookie;
+        }
     }
 }
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidationCallback.java b/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidationCallback.java
new file mode 100644
index 0000000..f07122f
--- /dev/null
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/TenantCacheInvalidationCallback.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014-2015 Groupon, Inc
+ * Copyright 2014-2015 The Billing Project, LLC
+ *
+ * The Billing Project 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.killbill.billing.tenant.api;
+
+import javax.inject.Inject;
+
+import org.killbill.billing.callcontext.InternalTenantContext;
+import org.killbill.billing.tenant.api.TenantInternalApi.CacheInvalidationCallback;
+import org.killbill.billing.tenant.api.TenantKV.TenantKey;
+import org.killbill.billing.util.cache.Cachable.CacheType;
+import org.killbill.billing.util.cache.CacheController;
+import org.killbill.billing.util.cache.CacheControllerDispatcher;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class TenantCacheInvalidationCallback implements CacheInvalidationCallback {
+
+    private final Logger log = LoggerFactory.getLogger(TenantCacheInvalidationCallback.class);
+
+    private final CacheController<Object, Object> tenantKVCache;
+
+    @Inject
+    public TenantCacheInvalidationCallback(final CacheControllerDispatcher cacheControllerDispatcher) {
+        this.tenantKVCache = cacheControllerDispatcher.getCacheController(CacheType.TENANT_KV);
+
+
+    }
+    @Override
+    public void invalidateCache(TenantKey tenantKey, final Object cookie, final InternalTenantContext tenantContext) {
+
+        final StringBuilder keyBuilder = new StringBuilder(tenantKey.toString());
+        if (cookie instanceof String) {
+            keyBuilder.append((String) cookie);
+        }
+        keyBuilder.append(CacheControllerDispatcher.CACHE_KEY_SEPARATOR);
+        keyBuilder.append(tenantContext.getTenantRecordId());
+        final String key = keyBuilder.toString();
+
+        log.info("Invalidate cache for tenant {} and key {} ", tenantContext.getTenantRecordId(), key);
+        tenantKVCache.remove(key);
+    }
+}
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java b/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java
index 8e72497..d4728f1 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/api/user/DefaultTenantUserApi.java
@@ -46,6 +46,22 @@ import com.google.inject.Inject;
 
 public class DefaultTenantUserApi implements TenantUserApi {
 
+    //
+    // Most System TenantKey are cached in the 'tenant-kv' cache owned by the Tenant module; however
+    // - xml value keys such as OVERDUE_CONFIG, CATALOG are cached at a higher level to avoid reconstruct the objects from xml
+    // - any keys that require multiple values would not be cached (today we only have CATALOG and this is not cached in 'tenant-kv' cache,
+    //   so that means all other TenantKey could be cached at this level.
+    //
+    // CACHED_TENANT_KEY is not exposed in the API and is hardcoded here since this is really a implementation choice.
+    //
+    public static final Iterable<TenantKey> CACHED_TENANT_KEY = ImmutableList.<TenantKey>builder()
+                                                                              .add(TenantKey.CATALOG_TRANSLATION_)
+                                                                              .add(TenantKey.INVOICE_MP_TEMPLATE)
+                                                                              .add(TenantKey.INVOICE_TEMPLATE)
+                                                                              .add(TenantKey.INVOICE_TRANSLATION_)
+                                                                              .add(TenantKey.PLUGIN_CONFIG_)
+                                                                              .add(TenantKey.PUSH_NOTIFICATION_CB).build();
+
     private final TenantDao tenantDao;
     private final InternalCallContextFactory internalCallContextFactory;
     private final CacheController<Object, Object> tenantKVCache;
@@ -138,7 +154,7 @@ public class DefaultTenantUserApi implements TenantUserApi {
 
     private String getCachedTenantValueForKey(final String key, final InternalTenantContext internalContext) {
 
-        if (!isSingleValueKey(key)) {
+        if (!isCachedInTenantKVCache(key)) {
             return null;
         }
         final String tenantKey = getCacheKeyName(key, internalContext);
@@ -160,4 +176,14 @@ public class DefaultTenantUserApi implements TenantUserApi {
             }
         }).orNull() != null;
     }
+
+
+    private boolean isCachedInTenantKVCache(final String key) {
+        return Iterables.tryFind(CACHED_TENANT_KEY, new Predicate<TenantKey>() {
+            @Override
+            public boolean apply(final TenantKey input) {
+                return key.startsWith(input.toString());
+            }
+        }).orNull() != null;
+    }
 }
diff --git a/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java b/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java
index 479cf18..6770c67 100644
--- a/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java
+++ b/tenant/src/main/java/org/killbill/billing/tenant/glue/DefaultTenantModule.java
@@ -25,6 +25,7 @@ import org.killbill.billing.platform.api.KillbillConfigSource;
 import org.killbill.billing.tenant.api.DefaultTenantInternalApi;
 import org.killbill.billing.tenant.api.DefaultTenantService;
 import org.killbill.billing.tenant.api.TenantCacheInvalidation;
+import org.killbill.billing.tenant.api.TenantCacheInvalidationCallback;
 import org.killbill.billing.tenant.api.TenantInternalApi;
 import org.killbill.billing.tenant.api.TenantService;
 import org.killbill.billing.tenant.api.TenantUserApi;
@@ -71,6 +72,7 @@ public class DefaultTenantModule extends KillBillModule implements TenantModule 
 
     public void installTenantService() {
         bind(TenantService.class).to(DefaultTenantService.class).asEagerSingleton();
+        bind(TenantCacheInvalidationCallback.class).asEagerSingleton();;
     }
 
     public void installTenantCacheInvalidation() {
diff --git a/util/src/main/java/org/killbill/billing/util/cache/TenantCatalogCacheLoader.java b/util/src/main/java/org/killbill/billing/util/cache/TenantCatalogCacheLoader.java
index 8da4461..a3a3130 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/TenantCatalogCacheLoader.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/TenantCatalogCacheLoader.java
@@ -32,7 +32,7 @@ import org.slf4j.LoggerFactory;
 @Singleton
 public class TenantCatalogCacheLoader extends BaseCacheLoader {
 
-    private final Logger logger = LoggerFactory.getLogger(TenantCatalogCacheLoader.class);
+    private final Logger log = LoggerFactory.getLogger(TenantCatalogCacheLoader.class);
 
     private final TenantInternalApi tenantApi;
 
@@ -72,7 +72,7 @@ public class TenantCatalogCacheLoader extends BaseCacheLoader {
             return null;
         }
         try {
-            logger.info("Loading catalog cache for tenant " + internalTenantContext.getTenantRecordId());
+            log.info("Loading catalog cache for tenant " + internalTenantContext.getTenantRecordId());
             return callback.loadCatalog(catalogXMLs);
         } catch (final CatalogApiException e) {
             throw new IllegalStateException(String.format("Failed to de-serialize catalog for tenant %s : %s",
diff --git a/util/src/main/java/org/killbill/billing/util/cache/TenantOverdueConfigCacheLoader.java b/util/src/main/java/org/killbill/billing/util/cache/TenantOverdueConfigCacheLoader.java
index 80222ac..d4a9a58 100644
--- a/util/src/main/java/org/killbill/billing/util/cache/TenantOverdueConfigCacheLoader.java
+++ b/util/src/main/java/org/killbill/billing/util/cache/TenantOverdueConfigCacheLoader.java
@@ -30,7 +30,7 @@ import org.slf4j.LoggerFactory;
 @Singleton
 public class TenantOverdueConfigCacheLoader extends BaseCacheLoader {
 
-    private static final Logger logger = LoggerFactory.getLogger(TenantOverdueConfigCacheLoader.class);
+    private static final Logger log = LoggerFactory.getLogger(TenantOverdueConfigCacheLoader.class);
 
     private final TenantInternalApi tenantApi;
 
@@ -70,7 +70,7 @@ public class TenantOverdueConfigCacheLoader extends BaseCacheLoader {
             return null;
         }
         try {
-            logger.info("Loading overdue cache for tenant " + internalTenantContext.getTenantRecordId());
+            log.info("Loading overdue cache for tenant " + internalTenantContext.getTenantRecordId());
 
             return callback.loadOverdueConfig(overdueXML);
         } catch (final OverdueApiException e) {