From 99d490e1c33970f4b4760bb9372d67073d1d6b1a Mon Sep 17 00:00:00 2001 From: Thaddee Tyl Date: Fri, 2 May 2014 17:16:07 +0000 Subject: [PATCH] Send cached badge directly if statistically accurate. See rationale here: . --- lru-cache.js | 1 + server.js | 57 ++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/lru-cache.js b/lru-cache.js index 56ef8b5..da9d6da 100644 --- a/lru-cache.js +++ b/lru-cache.js @@ -17,6 +17,7 @@ function addToCache(cacheIndex, cached) { this.order.splice(this.cache[cacheIndex].index, 1); // Put the new element at the end of `order`. this.cache[cacheIndex].index = this.order.length; + this.cache[cacheIndex].content = cached; this.order.push(cacheIndex); } else { // If the cache is full, remove the oldest data diff --git a/server.js b/server.js index 5ef950d..4bcd6b1 100644 --- a/server.js +++ b/server.js @@ -94,8 +94,18 @@ camp.ajax.on('analytics/v1', function(json, end) { end(analytics); }); // Cache -var cacheTimeout = 60000; // 1 minute. -var cacheFromIndex = Object.create(null); +// We avoid calling the vendor's server for computation of the information in a +// number of badges. +var minAccuracy = 0.75; + +// The quotient of (vendor) data change frequency by badge request frequency +// must be lower than this to trigger sending the cached data *before* +// updating our data from the vendor's server. +// Indeed, the accuracy of our badges are: +// A(Δt) = 1 - min(# data change over Δt, # requests over Δt) +// / (# requests over Δt) +// = 1 - max(1, df) / rf +var freqRatioMax = 1 - minAccuracy; // Request cache size of size 1_000_000 (~1GB, 1kB/image). var requestCache = new LruCache(1000000); @@ -108,10 +118,12 @@ function cache(f) { var cacheIndex = match[0] + '?label=' + data.label + '&style=' + data.style; // Should we return the data right away? - var cached = cacheFromIndex[cacheIndex]; - if (cached != null) { - badge(cached.badgeData, makeSend(cached.format, ask.res, end)); - return; + var cached = requestCache.get(cacheIndex); + var cachedVersionSent = false; + if (cached !== undefined + && cached.dataChange / cached.reqs <= freqRatioMax) { + badge(cached.data.badgeData, makeSend(cached.data.format, ask.res, end)); + cachedVersionSent = true; } // In case our vendor servers are unresponsive. @@ -119,24 +131,39 @@ function cache(f) { var serverResponsive = setTimeout(function() { serverUnresponsive = true; if (requestCache.has(cacheIndex)) { - var cached = requestCache.get(cacheIndex); - badge(cached.badgeData, makeSend(cached.format, ask.res, end)); + var cached = requestCache.get(cacheIndex).data; + if (!cachedVersionSent) { + badge(cached.badgeData, makeSend(cached.format, ask.res, end)); + } return; } var badgeData = getBadgeData('vendor', data); badgeData.text[1] = 'unresponsive'; - badge(badgeData, makeSend('svg', ask.res, end)); + if (!cachedVersionSent) { + badge(badgeData, makeSend('svg', ask.res, end)); + } }, 25000); f(data, match, function sendBadge(format, badgeData) { if (serverUnresponsive) { return; } clearTimeout(serverResponsive); - cacheFromIndex[cacheIndex] = { format: format, badgeData: badgeData }; - requestCache.set(cacheIndex, { format: format, badgeData: badgeData }); - setTimeout(function clearCache() { - delete cacheFromIndex[cacheIndex]; - }, cacheTimeout); - badge(badgeData, makeSend(format, ask.res, end)); + // Check for a change in the data. + var dataHasChanged = false; + if (cached !== undefined + && cached.data.badgeData.text[1] !== badgeData.text[1]) { + dataHasChanged = true; + } + // Update information in the cache. + var updatedCache = { + reqs: cached? (cached.reqs + 1): 1, + dataChange: cached? (cached.dataChange + (dataHasChanged? 1: 0)) + : 1, + data: { format: format, badgeData: badgeData } + }; + requestCache.set(cacheIndex, updatedCache); + if (!cachedVersionSent) { + badge(badgeData, makeSend(format, ask.res, end)); + } }); }; }