mirror of
https://github.com/weaveworks/scope.git
synced 2026-07-28 09:41:57 +00:00
Fixed the selectors performance leaks.
This commit is contained in:
@@ -5,14 +5,23 @@ import { saveGraph } from '../utils/file-utils';
|
||||
import { modulo } from '../utils/math-utils';
|
||||
import { updateRoute } from '../utils/router-utils';
|
||||
import { parseQuery } from '../utils/search-utils';
|
||||
import { bufferDeltaUpdate, resumeUpdate,
|
||||
resetUpdateBuffer } from '../utils/update-buffer-utils';
|
||||
import { doControlRequest, getAllNodes, getNodesDelta, getNodeDetails,
|
||||
getTopologies, deletePipe } from '../utils/web-api-utils';
|
||||
import { getActiveTopologyOptions,
|
||||
getCurrentTopologyUrl } from '../utils/topology-utils';
|
||||
import {
|
||||
bufferDeltaUpdate,
|
||||
resumeUpdate,
|
||||
resetUpdateBuffer,
|
||||
} from '../utils/update-buffer-utils';
|
||||
import {
|
||||
doControlRequest,
|
||||
getAllNodes,
|
||||
getNodesDelta,
|
||||
getNodeDetails,
|
||||
getTopologies,
|
||||
deletePipe,
|
||||
} from '../utils/web-api-utils';
|
||||
import { getCurrentTopologyUrl } from '../utils/topology-utils';
|
||||
import { storageSet } from '../utils/storage-utils';
|
||||
import { loadTheme } from '../utils/contrast-utils';
|
||||
import { activeTopologyOptionsSelector } from '../selectors/topology';
|
||||
|
||||
const log = debug('scope:app-actions');
|
||||
|
||||
@@ -165,16 +174,16 @@ export function changeTopologyOption(option, value, topologyId) {
|
||||
// update all request workers with new options
|
||||
resetUpdateBuffer();
|
||||
const state = getState();
|
||||
getTopologies(getActiveTopologyOptions(state), dispatch);
|
||||
getTopologies(activeTopologyOptionsSelector(state), dispatch);
|
||||
getNodesDelta(
|
||||
getCurrentTopologyUrl(state),
|
||||
getActiveTopologyOptions(state),
|
||||
activeTopologyOptionsSelector(state),
|
||||
dispatch
|
||||
);
|
||||
getNodeDetails(
|
||||
state.get('topologyUrlsById'),
|
||||
state.get('currentTopologyId'),
|
||||
getActiveTopologyOptions(state),
|
||||
activeTopologyOptionsSelector(state),
|
||||
state.get('nodeDetails'),
|
||||
dispatch
|
||||
);
|
||||
@@ -269,7 +278,7 @@ export function clickNode(nodeId, label, origin) {
|
||||
getNodeDetails(
|
||||
state.get('topologyUrlsById'),
|
||||
state.get('currentTopologyId'),
|
||||
getActiveTopologyOptions(state),
|
||||
activeTopologyOptionsSelector(state),
|
||||
state.get('nodeDetails'),
|
||||
dispatch
|
||||
);
|
||||
@@ -296,7 +305,7 @@ export function clickRelative(nodeId, topologyId, label, origin) {
|
||||
getNodeDetails(
|
||||
state.get('topologyUrlsById'),
|
||||
state.get('currentTopologyId'),
|
||||
getActiveTopologyOptions(state),
|
||||
activeTopologyOptionsSelector(state),
|
||||
state.get('nodeDetails'),
|
||||
dispatch
|
||||
);
|
||||
@@ -325,7 +334,7 @@ export function clickShowTopologyForNode(topologyId, nodeId) {
|
||||
const state = getState();
|
||||
getNodesDelta(
|
||||
getCurrentTopologyUrl(state),
|
||||
getActiveTopologyOptions(state),
|
||||
activeTopologyOptionsSelector(state),
|
||||
dispatch
|
||||
);
|
||||
};
|
||||
@@ -343,7 +352,7 @@ export function clickTopology(topologyId) {
|
||||
const state = getState();
|
||||
getNodesDelta(
|
||||
getCurrentTopologyUrl(state),
|
||||
getActiveTopologyOptions(state),
|
||||
activeTopologyOptionsSelector(state),
|
||||
dispatch
|
||||
);
|
||||
};
|
||||
@@ -562,13 +571,13 @@ export function receiveTopologies(topologies) {
|
||||
const state = getState();
|
||||
getNodesDelta(
|
||||
getCurrentTopologyUrl(state),
|
||||
getActiveTopologyOptions(state),
|
||||
activeTopologyOptionsSelector(state),
|
||||
dispatch
|
||||
);
|
||||
getNodeDetails(
|
||||
state.get('topologyUrlsById'),
|
||||
state.get('currentTopologyId'),
|
||||
getActiveTopologyOptions(state),
|
||||
activeTopologyOptionsSelector(state),
|
||||
state.get('nodeDetails'),
|
||||
dispatch
|
||||
);
|
||||
@@ -678,16 +687,16 @@ export function route(urlState) {
|
||||
});
|
||||
// update all request workers with new options
|
||||
const state = getState();
|
||||
getTopologies(getActiveTopologyOptions(state), dispatch);
|
||||
getTopologies(activeTopologyOptionsSelector(state), dispatch);
|
||||
getNodesDelta(
|
||||
getCurrentTopologyUrl(state),
|
||||
getActiveTopologyOptions(state),
|
||||
activeTopologyOptionsSelector(state),
|
||||
dispatch
|
||||
);
|
||||
getNodeDetails(
|
||||
state.get('topologyUrlsById'),
|
||||
state.get('currentTopologyId'),
|
||||
getActiveTopologyOptions(state),
|
||||
activeTopologyOptionsSelector(state),
|
||||
state.get('nodeDetails'),
|
||||
dispatch
|
||||
);
|
||||
@@ -720,7 +729,7 @@ export function changeInstance() {
|
||||
const state = getState();
|
||||
getNodesDelta(
|
||||
getCurrentTopologyUrl(state),
|
||||
getActiveTopologyOptions(state),
|
||||
activeTopologyOptionsSelector(state),
|
||||
dispatch,
|
||||
true // forces websocket teardown and reconnect to new instance
|
||||
);
|
||||
|
||||
@@ -10,7 +10,7 @@ import Logo from '../components/logo';
|
||||
import NodesChartElements from './nodes-chart-elements';
|
||||
import { clickBackground, cacheZoomState } from '../actions/app-actions';
|
||||
import { activeLayoutZoomSelector } from '../selectors/nodes-chart-zoom';
|
||||
import { activeTopologyZoomCacheKeyPath } from '../utils/topology-utils';
|
||||
import { activeTopologyZoomCacheKeyPathSelector } from '../selectors/topology';
|
||||
import { ZOOM_CACHE_DEBOUNCE_INTERVAL } from '../constants/timer';
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ class NodesChart extends React.Component {
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
layoutZoom: activeLayoutZoomSelector(state),
|
||||
layoutId: JSON.stringify(activeTopologyZoomCacheKeyPath(state)),
|
||||
layoutId: JSON.stringify(activeTopologyZoomCacheKeyPathSelector(state)),
|
||||
selectedNodeId: state.get('selectedNodeId'),
|
||||
forceRelayout: state.get('forceRelayout'),
|
||||
};
|
||||
|
||||
@@ -21,7 +21,7 @@ import MetricSelector from './metric-selector';
|
||||
import NetworkSelector from './networks-selector';
|
||||
import DebugToolbar, { showingDebugToolbar, toggleDebugToolbar } from './debug-toolbar';
|
||||
import { getRouter, getUrlState } from '../utils/router-utils';
|
||||
import { getActiveTopologyOptions } from '../utils/topology-utils';
|
||||
import { activeTopologyOptionsSelector } from '../selectors/topology';
|
||||
import { availableNetworksSelector } from '../selectors/node-networks';
|
||||
|
||||
const BACKSPACE_KEY_CODE = 8;
|
||||
@@ -143,7 +143,7 @@ class App extends React.Component {
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
activeTopologyOptions: getActiveTopologyOptions(state),
|
||||
activeTopologyOptions: activeTopologyOptionsSelector(state),
|
||||
gridMode: state.get('gridMode'),
|
||||
routeSet: state.get('routeSet'),
|
||||
searchFocused: state.get('searchFocused'),
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { getActiveTopologyOptions, getCurrentTopologyOptions } from '../utils/topology-utils';
|
||||
import { getCurrentTopologyOptions } from '../utils/topology-utils';
|
||||
import { activeTopologyOptionsSelector } from '../selectors/topology';
|
||||
import TopologyOptionAction from './topology-option-action';
|
||||
|
||||
class TopologyOptions extends React.Component {
|
||||
@@ -37,7 +38,7 @@ function mapStateToProps(state) {
|
||||
return {
|
||||
options: getCurrentTopologyOptions(state),
|
||||
topologyId: state.get('currentTopologyId'),
|
||||
activeOptions: getActiveTopologyOptions(state)
|
||||
activeOptions: activeTopologyOptionsSelector(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@ describe('RootReducer', () => {
|
||||
const reducer = require('../root').default;
|
||||
const initialState = require('../root').initialState;
|
||||
const topologyUtils = require('../../utils/topology-utils');
|
||||
const topologySelectors = require('../../selectors/topology');
|
||||
// TODO maybe extract those to topology-utils tests?
|
||||
const getActiveTopologyOptions = topologyUtils.getActiveTopologyOptions;
|
||||
const activeTopologyOptionsSelector = topologySelectors.activeTopologyOptionsSelector;
|
||||
const getAdjacentNodes = topologyUtils.getAdjacentNodes;
|
||||
const isTopologyEmpty = topologyUtils.isTopologyEmpty;
|
||||
const getUrlState = require('../../utils/router-utils').getUrlState;
|
||||
@@ -241,28 +242,28 @@ describe('RootReducer', () => {
|
||||
nextState = reducer(nextState, ClickTopologyAction);
|
||||
|
||||
// default options
|
||||
expect(getActiveTopologyOptions(nextState).has('option1')).toBeTruthy();
|
||||
expect(getActiveTopologyOptions(nextState).get('option1')).toBe('off');
|
||||
expect(activeTopologyOptionsSelector(nextState).has('option1')).toBeTruthy();
|
||||
expect(activeTopologyOptionsSelector(nextState).get('option1')).toBe('off');
|
||||
expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('off');
|
||||
|
||||
// turn on
|
||||
nextState = reducer(nextState, ChangeTopologyOptionAction);
|
||||
expect(getActiveTopologyOptions(nextState).get('option1')).toBe('on');
|
||||
expect(activeTopologyOptionsSelector(nextState).get('option1')).toBe('on');
|
||||
expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('on');
|
||||
|
||||
// turn off
|
||||
nextState = reducer(nextState, ChangeTopologyOptionAction2);
|
||||
expect(getActiveTopologyOptions(nextState).get('option1')).toBe('off');
|
||||
expect(activeTopologyOptionsSelector(nextState).get('option1')).toBe('off');
|
||||
expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('off');
|
||||
|
||||
// sub-topology should retain main topo options
|
||||
nextState = reducer(nextState, ClickSubTopologyAction);
|
||||
expect(getActiveTopologyOptions(nextState).get('option1')).toBe('off');
|
||||
expect(activeTopologyOptionsSelector(nextState).get('option1')).toBe('off');
|
||||
expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('off');
|
||||
|
||||
// other topology w/o options dont return options, but keep in app state
|
||||
nextState = reducer(nextState, ClickTopology2Action);
|
||||
expect(getActiveTopologyOptions(nextState)).toBeUndefined();
|
||||
expect(activeTopologyOptionsSelector(nextState)).toBeUndefined();
|
||||
expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('off');
|
||||
});
|
||||
|
||||
@@ -274,13 +275,13 @@ describe('RootReducer', () => {
|
||||
|
||||
let nextState = initialState;
|
||||
nextState = reducer(nextState, RouteAction);
|
||||
expect(getActiveTopologyOptions(nextState).get('option1')).toBe('on');
|
||||
expect(activeTopologyOptionsSelector(nextState).get('option1')).toBe('on');
|
||||
expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('on');
|
||||
|
||||
// stay same after topos have been received
|
||||
nextState = reducer(nextState, ReceiveTopologiesAction);
|
||||
nextState = reducer(nextState, ClickTopologyAction);
|
||||
expect(getActiveTopologyOptions(nextState).get('option1')).toBe('on');
|
||||
expect(activeTopologyOptionsSelector(nextState).get('option1')).toBe('on');
|
||||
expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('on');
|
||||
});
|
||||
|
||||
@@ -293,7 +294,7 @@ describe('RootReducer', () => {
|
||||
nextState = reducer(nextState, RouteAction);
|
||||
nextState = reducer(nextState, ReceiveTopologiesAction);
|
||||
nextState = reducer(nextState, ClickTopologyAction);
|
||||
expect(getActiveTopologyOptions(nextState).get('option1')).toBe('off');
|
||||
expect(activeTopologyOptionsSelector(nextState).get('option1')).toBe('off');
|
||||
expect(getUrlState(nextState).topologyOptions.topo1.option1).toBe('off');
|
||||
});
|
||||
|
||||
|
||||
@@ -6,7 +6,10 @@ import { fromJS, is as isDeepEqual, List as makeList, Map as makeMap,
|
||||
|
||||
import ActionTypes from '../constants/action-types';
|
||||
import { EDGE_ID_SEPARATOR } from '../constants/naming';
|
||||
import { graphExceedsComplexityThreshSelector } from '../selectors/topology';
|
||||
import {
|
||||
graphExceedsComplexityThreshSelector,
|
||||
activeTopologyZoomCacheKeyPathSelector,
|
||||
} from '../selectors/topology';
|
||||
import { applyPinnedSearches } from '../utils/search-utils';
|
||||
import { getNetworkNodes } from '../utils/network-view-utils';
|
||||
import {
|
||||
@@ -17,7 +20,6 @@ import {
|
||||
filterHiddenTopologies,
|
||||
addTopologyFullname,
|
||||
getDefaultTopology,
|
||||
activeTopologyZoomCacheKeyPath,
|
||||
} from '../utils/topology-utils';
|
||||
|
||||
const log = debug('scope:app-store');
|
||||
@@ -210,7 +212,7 @@ export function rootReducer(state = initialState, action) {
|
||||
}
|
||||
|
||||
case ActionTypes.CACHE_ZOOM_STATE: {
|
||||
return state.setIn(activeTopologyZoomCacheKeyPath(state), action.zoomState);
|
||||
return state.setIn(activeTopologyZoomCacheKeyPathSelector(state), action.zoomState);
|
||||
}
|
||||
|
||||
case ActionTypes.CLEAR_CONTROL_ERROR: {
|
||||
@@ -239,7 +241,7 @@ export function rootReducer(state = initialState, action) {
|
||||
case ActionTypes.CLICK_FORCE_RELAYOUT: {
|
||||
if (action.forceRelayout) {
|
||||
// Reset the zoom cache when forcing relayout.
|
||||
state = state.deleteIn(activeTopologyZoomCacheKeyPath(state));
|
||||
state = state.deleteIn(activeTopologyZoomCacheKeyPathSelector(state));
|
||||
}
|
||||
return state.set('forceRelayout', action.forceRelayout);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@ import { createSelector, createStructuredSelector } from 'reselect';
|
||||
import { Map as makeMap } from 'immutable';
|
||||
import timely from 'timely';
|
||||
|
||||
import { getActiveTopologyOptions } from '../utils/topology-utils';
|
||||
import { initEdgesFromNodes, collapseMultiEdges } from '../utils/layouter-utils';
|
||||
import { viewportWidthSelector, viewportHeightSelector } from './canvas-viewport';
|
||||
import { activeTopologyOptionsSelector } from './topology';
|
||||
import { shownNodesSelector } from './node-filters';
|
||||
import { doLayout } from '../charts/nodes-layout';
|
||||
|
||||
@@ -15,7 +15,7 @@ const log = debug('scope:nodes-chart');
|
||||
const layoutOptionsSelector = createStructuredSelector({
|
||||
forceRelayout: state => state.get('forceRelayout'),
|
||||
topologyId: state => state.get('currentTopologyId'),
|
||||
topologyOptions: getActiveTopologyOptions,
|
||||
topologyOptions: activeTopologyOptionsSelector,
|
||||
height: viewportHeightSelector,
|
||||
width: viewportWidthSelector,
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@ const focusedNodesIdsSelector = createSelector(
|
||||
],
|
||||
(selectedNodeId, nodes) => {
|
||||
if (!selectedNodeId || nodes.isEmpty()) {
|
||||
return makeList();
|
||||
return [];
|
||||
}
|
||||
|
||||
// The selected node always goes in focus.
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createSelector } from 'reselect';
|
||||
import { Map as makeMap } from 'immutable';
|
||||
|
||||
import { CANVAS_MARGINS, NODE_BASE_SIZE } from '../constants/styles';
|
||||
import { activeTopologyZoomCacheKeyPath } from '../utils/topology-utils';
|
||||
import { activeTopologyZoomCacheKeyPathSelector } from './topology';
|
||||
import { viewportWidthSelector, viewportHeightSelector } from './canvas-viewport';
|
||||
import { graphNodesSelector } from './nodes-chart-graph';
|
||||
|
||||
@@ -48,7 +48,7 @@ const defaultZoomSelector = createSelector(
|
||||
const activeLayoutCachedZoomSelector = createSelector(
|
||||
[
|
||||
state => state.get('zoomCache'),
|
||||
activeTopologyZoomCacheKeyPath,
|
||||
activeTopologyZoomCacheKeyPathSelector,
|
||||
],
|
||||
(zoomCache, keyPath) => zoomCache.getIn(keyPath.slice(1))
|
||||
);
|
||||
|
||||
@@ -11,3 +11,23 @@ export const graphExceedsComplexityThreshSelector = createSelector(
|
||||
],
|
||||
(nodeCount, edgeCount) => (nodeCount + (2 * edgeCount)) > 1000
|
||||
);
|
||||
|
||||
// Options for current topology, sub-topologies share options with parent
|
||||
export const activeTopologyOptionsSelector = createSelector(
|
||||
[
|
||||
state => state.getIn(['currentTopology', 'parentId']),
|
||||
state => state.get('currentTopologyId'),
|
||||
state => state.get('topologyOptions'),
|
||||
],
|
||||
(parentTopologyId, currentTopologyId, topologyOptions) => (
|
||||
topologyOptions.get(parentTopologyId || currentTopologyId)
|
||||
)
|
||||
);
|
||||
|
||||
export const activeTopologyZoomCacheKeyPathSelector = createSelector(
|
||||
[
|
||||
state => state.get('currentTopologyId'),
|
||||
activeTopologyOptionsSelector,
|
||||
],
|
||||
(topologyId, topologyOptions) => ['zoomCache', topologyId, JSON.stringify(topologyOptions)]
|
||||
);
|
||||
|
||||
@@ -127,15 +127,6 @@ export function filterHiddenTopologies(topologies) {
|
||||
t.stats.filtered_nodes > 0));
|
||||
}
|
||||
|
||||
export function getActiveTopologyOptions(state) {
|
||||
// options for current topology, sub-topologies share options with parent
|
||||
const parentId = state.getIn(['currentTopology', 'parentId']);
|
||||
if (parentId) {
|
||||
return state.getIn(['topologyOptions', parentId]);
|
||||
}
|
||||
return state.getIn(['topologyOptions', state.get('currentTopologyId')]);
|
||||
}
|
||||
|
||||
export function getCurrentTopologyOptions(state) {
|
||||
return state.getIn(['currentTopology', 'options']);
|
||||
}
|
||||
@@ -173,9 +164,3 @@ export function hasSelectedNode(state) {
|
||||
export function getCurrentTopologyUrl(state) {
|
||||
return state.getIn(['currentTopology', 'url']);
|
||||
}
|
||||
|
||||
export function activeTopologyZoomCacheKeyPath(state) {
|
||||
const topologyId = state.get('currentTopologyId');
|
||||
const topologyOptions = JSON.stringify(getActiveTopologyOptions(state));
|
||||
return ['zoomCache', topologyId, topologyOptions];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user