From e12d4aec1153c77fd39db7c163e159d9d53a93a5 Mon Sep 17 00:00:00 2001 From: Kristian Lyngstol Date: Tue, 31 May 2016 21:24:35 +0200 Subject: api: Fix bad hash-logic, front: Performance and more Fixes #93 Fixes #92 Fixes #91 The hash logic with FreezeThaw wasn't consistent, which broke a lot of optimizations. This might be slower (?) but it's consistent which means we'll make up for it by better cache utilization. To utilize this I also added delayed JSON parsing (#91) with a big description of why we want it. This also means that #90 and #89 is a lot less important. One issue right now is that "time" isn't part of the ETag. This is both a blessing and a curse. If we are going through time we will actually use in-app cache (not browser cache - the URL changes). This makes for nice CPU savings, but ultimately means we can't tell that the data for two distinct times is identical because we only see one. Oh well... On the simpler side, I fixed/improved the clearBox logic to fix the artifacts. Might need to tune this. I also added some optimizations to avoid redrawing identical text. This is what pointed out to me that the hash was broken too: The resizeEvent was triggering all the time, forcing redraws all the time. This happened because nmsData.switches "updated" frequently which should only happen if someone adds/moves/removes a switch or linknet - regardless of time travel. This was caused by the inconsistent hash causing a false cache miss. Anyway, if you are still reading this, you almost got a patch with "kjell magne bondevik uten mellomnavn" as a literal text string... TOO BAD. --- web/js/nms-data.js | 24 ++++++++++++++++--- web/js/nms-map-handlers.js | 12 ++++++---- web/js/nms-map.js | 59 +++++++++++++++++++++++++++++++++++----------- 3 files changed, 74 insertions(+), 21 deletions(-) (limited to 'web/js') diff --git a/web/js/nms-data.js b/web/js/nms-data.js index 9242313..5a219e8 100644 --- a/web/js/nms-data.js +++ b/web/js/nms-data.js @@ -224,13 +224,31 @@ nmsData._genericUpdater = function(name, cacheok) { heads['Cache-Control'] = "max-age=0, no-cache, stale-while-revalidate=0"; } + /* + * Note that we intentionally set dataType: "text" here. + * + * We can be smarter than jQuery here. We know that the ETag can be + * used to evaluate against our cached copy. If the ETag is a + * match, we never have to do the potentially extensive JSON + * parsing. + * + * Also note that we accept weakened ETags too (ETags with W/ + * prefixed). This is typically if the intermediate cache has + * compressed the content for us, so this is fine. + * + * This is particularly important because we poll everything even + * though we _know_ it will hit both browser cache and most likely + * Varnish. JSON.Parse was one of the biggest CPU hogs before this. + */ $.ajax({ type: "GET", headers: heads, url: this._sources[name].target + now, - dataType: "json", - success: function (data, textStatus, jqXHR) { - if (nmsData[name] == undefined || nmsData[name]['hash'] != data['hash']) { + dataType: "text", + success: function (indata, textStatus, jqXHR) { + let etag = jqXHR.getResponseHeader("ETag"); + if (nmsData[name] == undefined || (nmsData[name]['hash'] != etag && nmsData[name]['hash'] != etag.slice(2))) { + var data = JSON.parse(indata); if (name == "ping") { nmsData._last = data['time']; nmsMap.drawNow(); diff --git a/web/js/nms-map-handlers.js b/web/js/nms-map-handlers.js index 38407d1..21ed709 100644 --- a/web/js/nms-map-handlers.js +++ b/web/js/nms-map-handlers.js @@ -329,6 +329,7 @@ function tempInit() setLegend(4,temp_color(30),"30 °C"); setLegend(5,temp_color(35),"35 °C"); nmsData.addHandler("switchstate","mapHandler",tempUpdater); + tempUpdater(); } function pingUpdater() @@ -401,8 +402,7 @@ function pingInit() setLegend(4,nmsColor.getColorStop(1000),"100ms"); setLegend(5,nmsColor.blue,"No response"); nmsData.addHandler("ping","mapHandler",pingUpdater); - nmsData.addHandler("switches","mapHandler",pingUpdater); - nmsData.addHandler("ticker", "mapHandler", pingUpdater); + pingUpdater(); } function getDhcpColor(stop) @@ -474,6 +474,7 @@ function dhcpInit() setLegend(3,getDhcpColor(300),"300 Seconds old"); setLegend(4,getDhcpColor(900),"900 Seconds old"); setLegend(5,getDhcpColor(1200),"1200 Seconds old"); + dhcpUpdater(); } /* @@ -506,6 +507,7 @@ function discoInit() setLegend(3,nmsColor.orange,"C"); setLegend(4,nmsColor.green, "A"); setLegend(5,"white","!"); + discoDo(); } function snmpUpdater() { @@ -576,7 +578,7 @@ function snmpInit() { setLegend(3,nmsColor.red,"No SNMP data"); setLegend(4,nmsColor.green, ""); setLegend(5,nmsColor.green,""); - + snmpUpdater(); } function cpuUpdater() { @@ -645,6 +647,7 @@ function cpuInit() { setLegend(3,getColorStop(600),"60 %"); setLegend(4,getColorStop(1000),"100 %"); setLegend(5,"white","N/A"); + cpuUpdater(); } function healthInfo(sw) { @@ -682,11 +685,12 @@ function healthUpdater() { } function healthInit() { - nmsData.addHandler("ping", "mapHandler", healthUpdater); + nmsData.addHandler("ticker", "mapHandler", healthUpdater); nmsColor.drawGradient([nmsColor.green,nmsColor.lightgreen, nmsColor.orange,nmsColor.red]); setLegend(1,nmsColor.getColorStop(0),"All good"); setLegend(2,nmsColor.getColorStop(250),"Ok-ish"); setLegend(3,nmsColor.getColorStop(600),"Ick-ish"); setLegend(4,nmsColor.getColorStop(800),"Nasty"); setLegend(5,nmsColor.getColorStop(1000),"WTF?"); + healthUpdater(); } diff --git a/web/js/nms-map.js b/web/js/nms-map.js index 26782f8..491916b 100644 --- a/web/js/nms-map.js +++ b/web/js/nms-map.js @@ -26,13 +26,16 @@ var nmsMap = nmsMap || { resizeEvents:0, switchInfoSame:0, switchInfoUpdate:0, - highlightChange:0 + highlightChange:0, + textSwitchDraws:0, + textInfoDraws:0, + textInfoClears:0 }, contexts: ["bg","link","blur","switch","text","textInfo","top","input","hidden"], _info: {}, _settings: { fontLineFactor: 2, - textMargin: 3, + textMargin: 4, xMargin: 10, yMargin: 20, fontSize: 15, @@ -50,7 +53,9 @@ var nmsMap = nmsMap || { _linknets: {} , _highlight: { }, _highlightActive: false, - _c: {} + _c: {}, + _lastName: {}, + _lastInfo: {} }; nmsMap._loadEvent = function(e) { @@ -136,7 +141,17 @@ nmsMap._resizeEvent = function() { var xScale = (width / (nmsMap._orig.width + nmsMap._settings.xMargin)); var yScale = (height / (nmsMap._orig.height + nmsMap._settings.yMargin)); - + + /* + * We need to forget this. Because the rescale will blank the text + * anyway it is safe to set it to undefined here. This is a special + * case, if you set it to 'undefined' anywhere else to "reset" or + * force a redraw of the text you will introduce a bug if a handler + * then tries to _unset_ the text (which you do by setting the text + * to undefined) + */ + nmsMap._lastName = {}; + nmsMap._lastInfo = {}; if (xScale > yScale) { nmsMap.scale = yScale; } else { @@ -150,9 +165,9 @@ nmsMap._resizeEvent = function() { continue; nmsMap._c[a].c.height = nmsMap._canvas.height; nmsMap._c[a].c.width = nmsMap._canvas.width; - if(a == 'bg') { - nmsMap._drawBG(); - } + if(a == 'bg') { + nmsMap._drawBG(); + } } if (nmsMap._init != true) { nmsMap._blurDrawn = false; @@ -296,7 +311,16 @@ nmsMap._drawSwitch = function(sw) this._c.switch.ctx.fillStyle = color; this._drawBox(this._c.switch.ctx, box['x'],box['y'],box['width'],box['height']); this._c.switch.ctx.shadowBlur = 0; - this._drawText(this._c.text.ctx, sw,box); + var switchtext = sw; + var textl = switchtext.length; + if (textl > 10) + switchtext = switchtext.slice(0,5) + ".." + switchtext.slice(textl-2,textl); + + if (this._lastName[sw] != switchtext) { + nmsMap.stats.textSwitchDraws++; + this._drawText(this._c.text.ctx, switchtext,box); + this._lastName[sw] = switchtext; + } if(this._info[sw]) this._drawSwitchInfo(sw); @@ -304,17 +328,24 @@ nmsMap._drawSwitch = function(sw) nmsMap._drawSwitchInfo = function(sw) { var box = this._getBox(sw); - if (this._info[sw] == undefined) { - this._clearBox(this._c.textInfo.ctx, box); - } else { + if (this._lastInfo[sw] == this._info[sw]) + return; + this._clearBox(this._c.textInfo.ctx, box, 3); + nmsMap.stats.textInfoClears++; + if (this._info[sw] != undefined) { this._drawText(this._c.textInfo.ctx, this._info[sw], box, "right"); + nmsMap.stats.textInfoDraws++; } + this._lastInfo[sw] = this._info[sw]; }; -nmsMap._clearBox = function(ctx,box) { +nmsMap._clearBox = function(ctx,box,margin) { + if (margin == undefined) + margin = 0; ctx.save(); + margin = Math.floor(margin * this.scale ); ctx.scale(this.scale,this.scale); - ctx.clearRect(box['x'], box['y'], box['width'], box['height']); + ctx.clearRect(box['x'] - margin, box['y'] - margin, box['width'] + margin*2, box['height'] + margin*2); ctx.restore(); }; @@ -324,7 +355,7 @@ nmsMap._drawText = function(ctx, text, box, align) { if ((box['width'] + 10 )< box['height']) rotate = true; - this._clearBox(ctx,box); + this._clearBox(ctx,box, 1); ctx.save(); ctx.scale(this.scale, this.scale); ctx.font = "bold " + this._settings.fontSize + "px " + this._settings.fontFace; -- cgit v1.2.3