more tweaks to the request code

- delay briefly before retrying a timeout, and a bit longer before other errors
- also delay briefly before running the queue after a request, to give a chance of more than one request's requeued entries being handled atr once
- add a min tiles per request, to avoid sending multiple requests when only a few queued items remain
these are attempts to be nicer to the niantic servers, particularly in error cases, with some based on my best guess of the stock site behaviour, and others based on what seems reasonable to me
This commit is contained in:
Jon Atkins 2013-09-06 22:32:57 +01:00
parent 960f3ea29d
commit b83cf459c8
2 changed files with 80 additions and 23 deletions

View File

@ -34,8 +34,8 @@ window.RenderDebugTiles.prototype.setColour = function(id,bordercol,fillcol) {
}
window.RenderDebugTiles.prototype.setState = function(id,state) {
var col = '#666';
var fill = '#666';
var col = '#f0f';
var fill = '#f0f';
switch(state) {
case 'ok': col='#0f0'; fill='#0f0'; break;
case 'error': col='#f00'; fill='#f00'; break;
@ -43,6 +43,9 @@ window.RenderDebugTiles.prototype.setState = function(id,state) {
case 'cache-stale': col='#f00'; fill='#ff0'; break;
case 'requested': col='#66f'; fill='#66f'; break;
case 'retrying': col='#666'; fill='#666'; break;
case 'request-fail': col='#a00'; fill='#666'; break;
case 'tile-fail': col='#f00'; fill='#666'; break;
case 'tile-timeout': col='#ff0'; fill='#666'; break;
}
this.setColour (id, col, fill);
}

View File

@ -20,8 +20,11 @@ window.MapDataRequest = function() {
// (the stock site seems to have no limit - i've seen ~100 for L3+ portals and a maximised browser window)
this.MAX_TILES_PER_REQUEST = 32;
// try to maintain at least this may tiles in each request, by reducing the number of requests as needed
this.MIN_TILES_PER_REQUEST = 4;
// number of times to retty a tile after a 'bad' error (i.e. not a timeout)
this.MAX_TILE_RETRIES = 1;
this.MAX_TILE_RETRIES = 3;
// refresh timers
this.MOVE_REFRESH = 0.5; //refresh time to use after a move
@ -32,6 +35,16 @@ window.MapDataRequest = function() {
// processing cache, etc) and actually sending the first network requests
this.DOWNLOAD_DELAY = 3; //delay after preparing the data download before tile requests are sent
// a short delay between one request finishing and the queue being run for the next request
this.RUN_QUEUE_DELAY = 0.5;
// delay before re-queueing tiles
this.TILE_TIMEOUT_REQUEUE_DELAY = 0.5; // short delay before retrying a 'error==TIMEOUT' tile - as this is very common
this.BAD_REQUEST_REQUEUE_DELAY = 4; // longer delay before retrying a completely failed request - as in this case the servers are struggling
// additionally, a delay before processing the queue after requeueing tiles
// (this way, if multiple requeue delays finish within a short time of each other, they're all processed in one queue run)
this.RERUN_QUEUE_DELAY = 2;
this.REFRESH_CLOSE = 120; // refresh time to use for close views z>12 when not idle and not moving
this.REFRESH_FAR = 600; // refresh time for far views z <= 12
@ -125,11 +138,6 @@ window.MapDataRequest.prototype.refreshOnTimeout = function(seconds) {
}
//window.MapDataRequest.prototype.clearQueue = function() {
// this.tileBounds = {};
//}
window.MapDataRequest.prototype.setStatus = function(short,long,progress) {
this.status = { short: short, long: long, progress: progress };
window.renderUpdateStatus();
@ -247,11 +255,16 @@ window.MapDataRequest.prototype.refresh = function() {
console.log ('done request preperation (cleared out-of-bounds and invalid for zoom, and rendered cached data)');
// don't start processing the download queue immediately - start it after a short delay
var savedContext = this;
this.timer = setTimeout ( function() { savedContext.timer = undefined; savedContext.processRequestQueue(true); }, this.DOWNLOAD_DELAY*1000 );
this.delayProcessRequestQueue (this.DOWNLOAD_DELAY,true);
}
window.MapDataRequest.prototype.delayProcessRequestQueue = function(seconds,isFirst) {
if (this.timer === undefined) {
var savedContext = this;
this.timer = setTimeout ( function() { savedContext.timer = undefined; savedContext.processRequestQueue(isFirst); }, seconds*1000 );
}
}
window.MapDataRequest.prototype.processRequestQueue = function(isFirstPass) {
@ -259,10 +272,6 @@ window.MapDataRequest.prototype.processRequestQueue = function(isFirstPass) {
// if nothing left in the queue, end the render. otherwise, send network requests
if (Object.keys(this.tileBounds).length == 0) {
// // if map is being dragged, just return without any end of map processing
// if (this.moving)
// return;
this.render.endRenderPass();
var endTime = new Date().getTime();
@ -286,6 +295,7 @@ window.MapDataRequest.prototype.processRequestQueue = function(isFirstPass) {
return;
}
// create a list of tiles that aren't requested over the network
var pendingTiles = [];
for (var id in this.tileBounds) {
@ -300,6 +310,11 @@ window.MapDataRequest.prototype.processRequestQueue = function(isFirstPass) {
var requestBuckets = this.MAX_REQUESTS - this.activeRequestCount;
if (pendingTiles.length > 0 && requestBuckets > 0) {
// the stock site calculates bucket grouping with the simplistic <8 tiles: 1 bucket, otherwise 4 buckets
var maxBuckets = Math.ceil(pendingTiles.length/this.MIN_TILES_PER_REQUEST);
requestBuckets = Math.min (maxBuckets, requestBuckets);
var lastTileIndex = Math.min(requestBuckets*this.MAX_TILES_PER_REQUEST, pendingTiles.length);
for (var bucket=0; bucket<requestBuckets; bucket++) {
@ -413,19 +428,19 @@ window.MapDataRequest.prototype.handleResponse = function (data, tiles, success)
this.activeRequestCount -= 1;
for (var i in tiles) {
var id = tiles[i];
delete this.requestedTiles[id];
}
var successTiles = [];
var errorTiles = [];
var timeoutTiles = [];
if (!success || !data || !data.result) {
console.warn("Request.handleResponse: request failed - requeing...");
//request failed - requeue all the tiles(?)
for (var i in tiles) {
var id = tiles[i];
this.requeueTile(id, true);
errorTiles.push(id);
this.debugTiles.setState (id, 'request-fail');
}
window.runHooks('requestFinished', {success: false});
@ -444,13 +459,16 @@ window.MapDataRequest.prototype.handleResponse = function (data, tiles, success)
if (val.error == "TIMEOUT") {
// TIMEOUT errors for individual tiles are 'expected'(!) - and result in a silent unlimited retries
this.requeueTile(id, false);
timeoutTiles.push (id);
this.debugTiles.setState (id, 'tile-timeout');
} else {
console.warn('map data tile '+id+' failed: error=='+val.error);
this.requeueTile(id, true);
errorTiles.push (id);
this.debugTiles.setState (id, 'tile-fail');
}
} else {
// no error for this data tile - process it
successTiles.push (id);
// store the result in the cache
this.cache && this.cache.store (id, val);
@ -473,5 +491,41 @@ window.MapDataRequest.prototype.handleResponse = function (data, tiles, success)
window.runHooks('requestFinished', {success: true});
}
this.processRequestQueue();
console.log ('getThinnedEntities status: '+tiles.length+' tiles: '+successTiles.length+' successful, '+timeoutTiles.length+' timed out, '+errorTiles.length+' failed');
//setTimeout has no way of passing the 'context' (aka 'this') to it's function
var savedContext = this;
if (timeoutTiles.length > 0) {
setTimeout (function() {
for (var i in timeoutTiles) {
var id = timeoutTiles[i];
delete savedContext.requestedTiles[id];
savedContext.requeueTile(id, false);
}
savedContext.delayProcessRequestQueue(this.RERUN_QUEUE_DELAY);
}, this.TILE_TIMEOUT_REQUEUE_DELAY*1000);
}
if (errorTiles.length > 0) {
setTimeout (function() {
for (var i in errorTiles) {
var id = errorTiles[i];
delete savedContext.requestedTiles[id];
savedContext.requeueTile(id, true);
}
savedContext.delayProcessRequestQueue(this.RERUN_QUEUE_DELAY);
}, this.BAD_REQUEST_REQUEUE_DELAY*1000);
}
for (var i in successTiles) {
var id = successTiles[i];
delete this.requestedTiles[id];
}
//.. should this also be delayed a small amount?
this.delayProcessRequestQueue(this.RUN_QUEUE_DELAY);
}