Skip to content

Commit

Permalink
fix aria flowto performance
Browse files Browse the repository at this point in the history
  • Loading branch information
oatkiller committed Jul 18, 2020
1 parent e309652 commit 9d4d985
Show file tree
Hide file tree
Showing 8 changed files with 312 additions and 198 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/

/* eslint-disable no-shadow */

import { uniquePidForProcess, uniqueParentPidForProcess, orderByTime } from '../process_event';
import { IndexedProcessTree } from '../../types';
import { ResolverEvent } from '../../../../common/endpoint/types';
Expand All @@ -27,12 +29,12 @@ export function factory(
const uniqueParentPid = uniqueParentPidForProcess(process);
// if its defined and not ''
if (uniqueParentPid) {
let siblings = idToChildren.get(uniqueParentPid);
if (!siblings) {
siblings = [];
idToChildren.set(uniqueParentPid, siblings);
let childrenWithTheSameParent = idToChildren.get(uniqueParentPid);
if (!childrenWithTheSameParent) {
childrenWithTheSameParent = [];
idToChildren.set(uniqueParentPid, childrenWithTheSameParent);
}
siblings.push(process);
childrenWithTheSameParent.push(process);
}
}

Expand All @@ -50,9 +52,8 @@ export function factory(
/**
* Returns an array with any children `ProcessEvent`s of the passed in `process`
*/
export function children(tree: IndexedProcessTree, process: ResolverEvent): ResolverEvent[] {
const id = uniquePidForProcess(process);
const currentProcessSiblings = tree.idToChildren.get(id);
export function children(tree: IndexedProcessTree, parentID: string | undefined): ResolverEvent[] {
const currentProcessSiblings = tree.idToChildren.get(parentID);
return currentProcessSiblings === undefined ? [] : currentProcessSiblings;
}

Expand Down Expand Up @@ -81,26 +82,21 @@ export function parent(
/**
* Returns the following sibling
*/
export function nextSibling(
tree: IndexedProcessTree,
sibling: ResolverEvent
): ResolverEvent | undefined {
const parentNode = parent(tree, sibling);
if (parentNode) {
// The siblings of `sibling` are the children of its parent.
const siblings = children(tree, parentNode);
export function siblings(tree: IndexedProcessTree, node: ResolverEvent): ResolverEvent[] {
// this can be undefined, since a node may have no parent.
const parentID: string | undefined = uniqueParentPidForProcess(node);

// Find the sibling
const index = siblings.indexOf(sibling);
// nodes with the same parent ID.
// if `node` has no parent ID, this is nodes with no parent ID.
const childrenWithTheSameParent: undefined | ResolverEvent[] = tree.idToChildren.get(parentID);

// if the sibling wasn't found, or if it was the last element in the array, return undefined
if (index === -1 || index === siblings.length - 1) {
return undefined;
}

// return the next sibling
return siblings[index + 1];
// this shouldn't happen if the node was in `tree`.
if (!childrenWithTheSameParent) {
return [];
}

// Return all children with the same parent as `node`, except `node` itself.
return [...childrenWithTheSameParent.filter((child) => child !== node)];
}

/**
Expand Down Expand Up @@ -133,6 +129,8 @@ export function root(tree: IndexedProcessTree) {
export function* levelOrder(tree: IndexedProcessTree) {
const rootNode = root(tree);
if (rootNode !== null) {
yield* baseLevelOrder(rootNode, children.bind(null, tree));
yield* baseLevelOrder(rootNode, (parentNode: ResolverEvent): ResolverEvent[] =>
children(tree, uniquePidForProcess(parentNode))
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import * as event from '../../../../common/endpoint/models/event';
import { ResolverEvent } from '../../../../common/endpoint/types';
import * as model from './index';
import { getFriendlyElapsedTime as elapsedTime } from '../../lib/date';
import { uniquePidForProcess } from '../process_event';

/**
* Graph the process tree
Expand Down Expand Up @@ -146,10 +147,12 @@ function widthsOfProcessSubtrees(indexedProcessTree: IndexedProcessTree): Proces
return widths;
}

const processesInReverseLevelOrder = [...model.levelOrder(indexedProcessTree)].reverse();
const processesInReverseLevelOrder: ResolverEvent[] = [
...model.levelOrder(indexedProcessTree),
].reverse();

for (const process of processesInReverseLevelOrder) {
const children = model.children(indexedProcessTree, process);
const children = model.children(indexedProcessTree, uniquePidForProcess(process));

const sumOfWidthOfChildren = function sumOfWidthOfChildren() {
return children.reduce(function sum(currentValue, child) {
Expand Down Expand Up @@ -226,7 +229,7 @@ function processEdgeLineSegments(
metadata: edgeLineMetadata,
};

const siblings = model.children(indexedProcessTree, parent);
const siblings = model.children(indexedProcessTree, uniquePidForProcess(parent));
const isFirstChild = process === siblings[0];

if (metadata.isOnlyChild) {
Expand Down Expand Up @@ -420,7 +423,7 @@ function* levelOrderWithWidths(
parentWidth,
};

const siblings = model.children(tree, parent);
const siblings = model.children(tree, uniquePidForProcess(parent));
if (siblings.length === 1) {
metadata.isOnlyChild = true;
metadata.lastChildWidth = width;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ import { DataState } from '../../types';
import { dataReducer } from './reducer';
import { DataAction } from './action';
import { createStore } from 'redux';
import {
mockTreeWithNoAncestorsAnd2Children,
mockTreeWith2AncestorsAndNoChildren,
} from '../mocks/resolver_tree';
describe('data state', () => {
let actions: DataAction[] = [];

Expand Down Expand Up @@ -263,4 +267,56 @@ describe('data state', () => {
});
});
});
describe('with a tree with no descendants and 2 ancestors', () => {
const originID = 'c';
const firstAncestorID = 'b';
const secondAncestorID = 'a';
beforeEach(() => {
actions.push({
type: 'serverReturnedResolverData',
payload: {
result: mockTreeWith2AncestorsAndNoChildren({
originID,
firstAncestorID,
secondAncestorID,
}),
// this value doesn't matter
databaseDocumentID: '',
},
});
});
it('should have no flowto candidate for the origin', () => {
expect(selectors.ariFlowtoCandidate(state())(originID)).toBe(null);
});
it('should have no flowto candidate for the first ancestor', () => {
expect(selectors.ariFlowtoCandidate(state())(firstAncestorID)).toBe(null);
});
it('should have no flowto candidate for the second ancestor ancestor', () => {
expect(selectors.ariFlowtoCandidate(state())(secondAncestorID)).toBe(null);
});
});
describe('with a tree with 2 children and no ancestors', () => {
const originID = 'c';
const firstChildID = 'd';
const secondChildID = 'e';
beforeEach(() => {
actions.push({
type: 'serverReturnedResolverData',
payload: {
result: mockTreeWithNoAncestorsAnd2Children({ originID, firstChildID, secondChildID }),
// this value doesn't matter
databaseDocumentID: '',
},
});
});
it('should have no flowto candidate for the origin', () => {
expect(selectors.ariFlowtoCandidate(state())(originID)).toBe(null);
});
it('should use the second child as the flowto candidate for the first child', () => {
expect(selectors.ariFlowtoCandidate(state())(firstChildID)).toBe(secondChildID);
});
it('should have no flowto candidate for the second child', () => {
expect(selectors.ariFlowtoCandidate(state())(secondChildID)).toBe(null);
});
});
});
Loading

0 comments on commit 9d4d985

Please sign in to comment.