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:
parent
c06f57a6e7
commit
6e25eaaa99
1 changed files with 51 additions and 16 deletions
|
@ -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;
|
||||
|
|
Reference in a new issue