Add a faster and more accurate level size calculation to the tree. This (hopefully) fixes the problems with super wide trees in prod.

This commit is contained in:
Joseph Schorr 2014-10-27 14:54:10 -04:00
parent c06f57a6e7
commit 6e25eaaa99

View file

@ -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;