From 6e25eaaa99ba660ee973a38163033e5c7e206a36 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Mon, 27 Oct 2014 14:54:10 -0400 Subject: [PATCH] Add a faster and more accurate level size calculation to the tree. This (hopefully) fixes the problems with super wide trees in prod. --- static/js/graphing.js | 67 ++++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 16 deletions(-) diff --git a/static/js/graphing.js b/static/js/graphing.js index 4fa8e7f9c..97459654c 100644 --- a/static/js/graphing.js +++ b/static/js/graphing.js @@ -377,6 +377,23 @@ ImageHistoryTree.prototype.expandCollapsed_ = function(imageNode) { }; +/** + * Returns the level of the node in the tree. Recursively computes and updates + * if necessary. + */ +ImageHistoryTree.prototype.calculateLevel_ = function(node) { + if (node['level'] != null) { + return node['level']; + } + + if (node['parent'] == null) { + return node['level'] = 0; + } + + return node['level'] = (this.calculateLevel_(node['parent']) + 1); +}; + + /** * Builds the root node for the tree. */ @@ -392,11 +409,16 @@ ImageHistoryTree.prototype.buildRoot_ = function() { var imageByDockerId = {}; for (var i = 0; i < this.images_.length; ++i) { var image = this.images_[i]; + + // Skip images that are currently uploading. + if (image.uploading) { continue; } + var imageNode = { "name": image.id.substr(0, 12), "children": [], "image": image, - "tags": image.tags + "tags": image.tags, + "level": null }; imageByDockerId[image.id] = imageNode; } @@ -405,6 +427,7 @@ ImageHistoryTree.prototype.buildRoot_ = function() { // For each node, attach it to its immediate parent. If there is no immediate parent, // then the node is the root. var roots = []; + var nodeCountsByLevel = {}; for (var i = 0; i < this.images_.length; ++i) { var image = this.images_[i]; @@ -420,10 +443,27 @@ ImageHistoryTree.prototype.buildRoot_ = function() { imageNode.parent = parent; parent.children.push(imageNode); } else { + imageNode['level'] = 0; roots.push(imageNode); } } + // Calculate each node's level. + for (var i = 0; i < this.images_.length; ++i) { + var image = this.images_[i]; + + // Skip images that are currently uploading. + if (image.uploading) { continue; } + + var imageNode = imageByDockerId[image.id]; + var level = this.calculateLevel_(imageNode); + if (nodeCountsByLevel[level] == null) { + nodeCountsByLevel[level] = 1; + } else { + nodeCountsByLevel[level]++; + } + } + // If there are multiple root nodes, then there is at least one branch without shared // ancestry and we use the virtual node. Otherwise, we use the root node found. var root = { @@ -438,16 +478,12 @@ ImageHistoryTree.prototype.buildRoot_ = function() { // Determine the maximum number of nodes at a particular level. This is used to size // the width of the tree properly. - var maxChildCount = roots.length; - for (var i = 0; i < this.images_.length; ++i) { - var image = this.images_[i]; - - // Skip images that are currently uploading. - if (image.uploading) { continue; } - - var imageNode = imageByDockerId[image.id]; - maxChildCount = Math.max(maxChildCount, this.determineMaximumChildCount_(imageNode)); - } + var maxChildCount = 0; + var maxChildHeight = 0; + Object.keys(nodeCountsByLevel).forEach(function(key){ + maxChildCount = Math.max(maxChildCount, nodeCountsByLevel[key]); + maxChildHeight = Math.max(maxChildHeight, key); + }); // Compact the graph so that any single chain of three (or more) images becomes a collapsed // section. We only do this if the max width is > 1 (since for a single width tree, no long @@ -456,22 +492,21 @@ ImageHistoryTree.prototype.buildRoot_ = function() { this.collapseNodes_(root); } - // Determine the maximum height of the tree. - var maxHeight = this.determineMaximumHeight_(root); + // Determine the maximum height of the tree, with collapsed nodes. + var maxCollapsedHeight = this.determineMaximumHeight_(root); // Finally, set the root node and return. this.root_ = root; return { 'maxWidth': maxChildCount + 1, - 'maxHeight': maxHeight + 'maxHeight': maxCollapsedHeight }; }; /** - * Collapses long single chains of nodes (3 or more) into single nodes to make the graph more - * compact. + * Determines the height of the tree at its longest chain. */ ImageHistoryTree.prototype.determineMaximumHeight_ = function(node) { var maxHeight = 0;