cache the map data tiles for a short time. this can vastly reduce our load on the niantic servers, especially in the case of multiple repeated scroll/zoom actions in the same area

This commit is contained in:
Jon Atkins 2013-06-15 05:34:23 +01:00
parent ea46405cd9
commit 985bf78e47

View File

@ -5,6 +5,51 @@
// action. // action.
// cache for data tiles. indexed by the query key (qk)
window._requestCache = {}
// cache entries older than the fresh age, and younger than the max age, are stale. they're used in the case of an error from the server
window.REQUEST_CACHE_FRESH_AGE = window.REFRESH; // if younger than this, use data in the cache rather than fetching from the server
window.REQUEST_CACHE_MAX_AGE = 5*window.REFRESH; // maximum cache age. entries are deleted from the cache after this time
window.storeDataCache = function(qk,data) {
var d = new Date();
window._requestCache[qk] = { time: d.getTime(), data: data };
}
window.getDataCache = function(qk) {
if (qk in window._requestCache) {
return window._requestCache[qk].data;
}
return undefined;
}
window.isDataCacheFresh = function(qk) {
if (qk in window._requestCache) {
var d = new Date();
var t = d.getTime()-REQUEST_CACHE_FRESH_AGE*1000;
if (window._requestCache[qk].time > t) {
return true;
}
}
return false;
}
window.expireDataCache = function() {
// TODO: add a limit on the maximum number of cache entries, and start expiring them sooner than the expiry time
// (might also make sense to approximate the size of cache entries, and have an overall size limit?)
var d = new Date();
var t = d.getTime()-window.REQUEST_CACHE_MAX_AGE*1000;
for(var qk in window._requestCache) {
if (window._requestCache[qk].time < t) {
delete window._requestCache[qk];
}
}
}
// requests map data for current viewport. For details on how this // requests map data for current viewport. For details on how this
// works, refer to the description in “MAP DATA REQUEST CALCULATORS” // works, refer to the description in “MAP DATA REQUEST CALCULATORS”
window.requestData = function() { window.requestData = function() {
@ -12,6 +57,8 @@ window.requestData = function() {
requests.abort(); requests.abort();
cleanUp(); cleanUp();
expireDataCache();
//a limit on the number of map tiles to be pulled in a single request //a limit on the number of map tiles to be pulled in a single request
var MAX_TILES_PER_BUCKET = 20; var MAX_TILES_PER_BUCKET = 20;
@ -29,40 +76,53 @@ window.requestData = function() {
tiles = {}; tiles = {};
fullBucketCount = 0; fullBucketCount = 0;
var cachedData = { result: { map: {} } };
var requestTileCount = 0;
// walk in x-direction, starts right goes left // walk in x-direction, starts right goes left
for (var x = x1; x <= x2; x++) { for (var x = x1; x <= x2; x++) {
for (var y = y1; y <= y2; y++) { for (var y = y1; y <= y2; y++) {
var tile_id = pointToTileId(z, x, y); var tile_id = pointToTileId(z, x, y);
var bucket = (Math.floor(x/2) % 2) + ":" + (Math.floor(y/2) % 2); if (isDataCacheFresh(tile_id)) {
if (!tiles[bucket]) { cachedData.result.map[tile_id] = getDataCache(tile_id);
//create empty bucket } else {
tiles[bucket] = []; var bucket = (Math.floor(x/2) % 2) + ":" + (Math.floor(y/2) % 2);
} if (!tiles[bucket]) {
else if(tiles[bucket].length >= MAX_TILES_PER_BUCKET) { //create empty bucket
//too many items in bucket. rename it, and create a new empty one tiles[bucket] = [];
tiles[bucket+'_'+fullBucketCount] = tiles[bucket]; }
fullBucketCount++; else if(tiles[bucket].length >= MAX_TILES_PER_BUCKET) {
tiles[bucket] = []; //too many items in bucket. rename it, and create a new empty one
} tiles[bucket+'_'+fullBucketCount] = tiles[bucket];
fullBucketCount++;
tiles[bucket] = [];
}
tiles[bucket].push(generateBoundsParams( requestTileCount++;
tile_id, tiles[bucket].push(generateBoundsParams(
tileToLat(y + 1, z), tile_id,
tileToLng(x, z), tileToLat(y + 1, z),
tileToLat(y, z), tileToLng(x, z),
tileToLng(x + 1, z) tileToLat(y, z),
)); tileToLng(x + 1, z)
));
}
} }
} }
// Reset previous result of Portal Render Limit handler // Reset previous result of Portal Render Limit handler
portalRenderLimit.init(); portalRenderLimit.init();
// process the requests from the cache
console.log('got '+Object.keys(cachedData.result.map).length+' data tiles from cache');
handleDataResponse(cachedData, true);
// finally send ajax requests // finally send ajax requests
console.log('requesting '+requestTileCount+' tiles in '+Object.keys(tiles).length+' requests');
$.each(tiles, function(ind, tls) { $.each(tiles, function(ind, tls) {
data = { zoom: z }; data = { zoom: z };
data.boundsParamsList = tls; data.boundsParamsList = tls;
window.requests.add(window.postAjax('getThinnedEntitiesV2', data, window.handleDataResponse, window.handleFailedRequest)); window.requests.add(window.postAjax('getThinnedEntitiesV2', data, function(data, textStatus, jqXHR) { window.handleDataResponse(data,false); }, window.handleFailedRequest));
}); });
} }
@ -76,7 +136,7 @@ window.handleFailedRequest = function() {
} }
// works on map data response and ensures entities are drawn/updated. // works on map data response and ensures entities are drawn/updated.
window.handleDataResponse = function(data, textStatus, jqXHR) { window.handleDataResponse = function(data, fromCache) {
// remove from active ajax queries list // remove from active ajax queries list
if(!data || !data.result) { if(!data || !data.result) {
window.failedRequestCount++; window.failedRequestCount++;
@ -96,8 +156,20 @@ window.handleDataResponse = function(data, textStatus, jqXHR) {
$.each(m, function(qk, val) { $.each(m, function(qk, val) {
if('error' in val) { if('error' in val) {
console.log('map data tile '+qk+' response error: '+val.error); console.log('map data tile '+qk+' response error: '+val.error);
// try to use data in the cache, even if it's stale
val = getDataCache(qk);
if (!val) {
// no data in cache for this tile - skip it
return true; // $.each 'continue'
} else {
console.log('(using stale cache entry for map tile)');
}
} }
if (!fromCache) storeDataCache(qk,val);
$.each(val.deletedGameEntityGuids || [], function(ind, guid) { $.each(val.deletedGameEntityGuids || [], function(ind, guid) {
if(getTypeByGuid(guid) === TYPE_FIELD && window.fields[guid] !== undefined) { if(getTypeByGuid(guid) === TYPE_FIELD && window.fields[guid] !== undefined) {
$.each(window.fields[guid].options.vertices, function(ind, vertex) { $.each(window.fields[guid].options.vertices, function(ind, vertex) {