diff --git a/packages/jaeger-ui/package.json b/packages/jaeger-ui/package.json index e63f5c3b52..11817ac285 100644 --- a/packages/jaeger-ui/package.json +++ b/packages/jaeger-ui/package.json @@ -52,6 +52,7 @@ "json-markup": "^1.1.0", "lodash": "^4.17.4", "logfmt": "^1.2.0", + "memoize-one": "^5.0.0", "moment": "^2.18.1", "prop-types": "^15.5.10", "query-string": "^5.0.0", diff --git a/packages/jaeger-ui/src/components/SearchTracePage/index.js b/packages/jaeger-ui/src/components/SearchTracePage/index.js index 95d12f64f3..3b73ed8cf7 100644 --- a/packages/jaeger-ui/src/components/SearchTracePage/index.js +++ b/packages/jaeger-ui/src/components/SearchTracePage/index.js @@ -21,6 +21,7 @@ import queryString from 'query-string'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import store from 'store'; +import memoizeOne from 'memoize-one'; import SearchForm from './SearchForm'; import SearchResults, { sortFormSelector } from './SearchResults'; @@ -33,7 +34,6 @@ import { getLocation as getTraceLocation } from '../TracePage/url'; import { actions as traceDiffActions } from '../TraceDiff/duck'; import { fetchedState } from '../../constants'; import { sortTraces } from '../../model/search'; -import getLastXformCacher from '../../utils/get-last-xform-cacher'; import { stripEmbeddedState } from '../../utils/embedded-url'; import FileLoader from './FileLoader'; @@ -191,7 +191,7 @@ SearchTracePageImpl.propTypes = { loadJsonTraces: PropTypes.func, }; -const stateTraceXformer = getLastXformCacher(stateTrace => { +const stateTraceXformer = memoizeOne(stateTrace => { const { traces: traceMap, search } = stateTrace; const { query, results, state, error: traceError } = search; @@ -201,19 +201,19 @@ const stateTraceXformer = getLastXformCacher(stateTrace => { return { traces, maxDuration, traceError, loadingTraces, query }; }); -const stateTraceDiffXformer = getLastXformCacher((stateTrace, stateTraceDiff) => { +const stateTraceDiffXformer = memoizeOne((stateTrace, stateTraceDiff) => { const { traces } = stateTrace; const { cohort } = stateTraceDiff; return cohort.map(id => traces[id] || { id }); }); -const sortedTracesXformer = getLastXformCacher((traces, sortBy) => { +const sortedTracesXformer = memoizeOne((traces, sortBy) => { const traceResults = traces.slice(); sortTraces(traceResults, sortBy); return traceResults; }); -const stateServicesXformer = getLastXformCacher(stateServices => { +const stateServicesXformer = memoizeOne(stateServices => { const { loading: loadingServices, services: serviceList, diff --git a/packages/jaeger-ui/src/components/TraceDiff/TraceDiffGraph/traceDiffGraphUtils.js b/packages/jaeger-ui/src/components/TraceDiff/TraceDiffGraph/traceDiffGraphUtils.js index be1a1702a6..f8d1488f54 100644 --- a/packages/jaeger-ui/src/components/TraceDiff/TraceDiffGraph/traceDiffGraphUtils.js +++ b/packages/jaeger-ui/src/components/TraceDiff/TraceDiffGraph/traceDiffGraphUtils.js @@ -14,6 +14,7 @@ import _get from 'lodash/get'; import _map from 'lodash/map'; +import memoizeOne from 'memoize-one'; import convPlexus from '../../../model/trace-dag/convPlexus'; import TraceDag from '../../../model/trace-dag/TraceDag'; @@ -24,41 +25,20 @@ import type { Trace } from '../../../types/trace'; export type TVertexKeys = Set; -let lastUiFind: string; -let lastVertices: PVertex[]; -let uiFindVertexKeys: ?TVertexKeys; - -export function getUiFindVertexKeys(uiFind: string, vertices: PVertex[]): TVertexKeys { +export const getUiFindVertexKeys = memoizeOne((uiFind: string, vertices: PVertex[]): TVertexKeys => { if (!uiFind) return new Set(); - if (uiFind === lastUiFind && vertices === lastVertices && uiFindVertexKeys) { - return uiFindVertexKeys; - } const newVertexKeys: Set = new Set(); vertices.forEach(({ key, data: { members } }) => { if (_get(filterSpans(uiFind, _map(members, 'span')), 'size')) { newVertexKeys.add(key); } }); - lastUiFind = uiFind; - lastVertices = vertices; - uiFindVertexKeys = newVertexKeys; return newVertexKeys; -} - -let lastAData: ?Trace; -let lastBData: ?Trace; -// TODO: use convPlexus type (everett JAG-343) -let edgesAndVertices: ?Object; +}); -export function getEdgesAndVertices(aData: Trace, bData: Trace) { - if (aData === lastAData && bData === lastBData && edgesAndVertices) { - return edgesAndVertices; - } - lastAData = aData; - lastBData = bData; +export const getEdgesAndVertices = memoizeOne((aData: Trace, bData: Trace) => { const aTraceDag = TraceDag.newFromTrace(aData); const bTraceDag = TraceDag.newFromTrace(bData); const diffDag = TraceDag.diff(aTraceDag, bTraceDag); - edgesAndVertices = convPlexus(diffDag.nodesMap); - return edgesAndVertices; -} + return convPlexus(diffDag.nodesMap); +}); diff --git a/packages/jaeger-ui/src/utils/get-last-xform-cacher.js b/packages/jaeger-ui/src/utils/get-last-xform-cacher.js deleted file mode 100644 index 7b34903d12..0000000000 --- a/packages/jaeger-ui/src/utils/get-last-xform-cacher.js +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -/** - * Given a transformer function, returns a function that invokes the - * transformer on the argument and caches the transformation before returning - * it. If the source value changes, the transformation is recalculated, cached - * and returned. - * - * @param {function} xformer The transformer function, the most recent result - * of which is cached. - * @return {function} A wrapper around the transformer function which caches - * the last transformation, returning the cached value if - * the source value is the same. - */ -export default function getLastXformCacher(xformer) { - let lastArgs = null; - let lastXformed = null; - - return function getOrCache(...args) { - const sameArgs = - lastArgs && lastArgs.length === args.length && lastArgs.every((lastArg, i) => lastArg === args[i]); - if (sameArgs) { - return lastXformed; - } - lastArgs = args; - lastXformed = xformer(...args); - return lastXformed; - }; -} diff --git a/packages/jaeger-ui/src/utils/get-last-xform-cacher.test.js b/packages/jaeger-ui/src/utils/get-last-xform-cacher.test.js deleted file mode 100644 index 8ab0245df4..0000000000 --- a/packages/jaeger-ui/src/utils/get-last-xform-cacher.test.js +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2017 Uber Technologies, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -import getLastXformCacher from './get-last-xform-cacher'; - -const xformImpl = value => value + value; -let xformer; -let cacher; - -beforeEach(() => { - xformer = jest.fn(xformImpl); - cacher = getLastXformCacher(xformer); -}); - -it('returns a function', () => { - expect(cacher).toEqual(expect.any(Function)); -}); - -it('handles the first invocation where nothing is cached', () => { - expect(() => cacher('a')).not.toThrow(); -}); - -it('the returned function returns the same results as the transformer function', () => { - expect(cacher('a')).toBe(xformImpl('a')); - expect(cacher('a')).toBe(xformImpl('a')); - expect(cacher(1)).toBe(xformImpl(1)); - expect(cacher('a')).not.toBe(cacher('b')); -}); - -it('the returned function returns a cached value for subsequent invocation with the same argument', () => { - expect(xformer.mock.calls.length).toBe(0); - const value = cacher('a'); - expect(xformer.mock.calls.length).toBe(1); - expect(cacher('a')).toBe(value); - expect(xformer.mock.calls.length).toBe(1); -}); - -it('the returned function ignores the cached value when invoked with different arguments', () => { - expect(xformer.mock.calls.length).toBe(0); - const firstValue = cacher('a'); - expect(xformer.mock.calls.length).toBe(1); - expect(cacher('a')).toBe(firstValue); - expect(xformer.mock.calls.length).toBe(1); - const secondValue = cacher('b'); - expect(xformer.mock.calls.length).toBe(2); - expect(cacher('b')).toBe(secondValue); - expect(xformer.mock.calls.length).toBe(2); -}); - -it('the functionality works with multiple arguments', () => { - const multiXformer = jest.fn((a, b) => [a + a, b + b]); - const multiCacher = getLastXformCacher(multiXformer); - - expect(multiXformer.mock.calls.length).toBe(0); - const firstValue = multiCacher('a', 'b'); - - expect(multiXformer.mock.calls.length).toBe(1); - expect(firstValue).toEqual(['aa', 'bb']); - expect(multiCacher('a', 'b')).toBe(firstValue); - expect(multiXformer.mock.calls.length).toBe(1); - - const secondValue = multiCacher('A', 'B'); - expect(multiXformer.mock.calls.length).toBe(2); - expect(secondValue).toEqual(['AA', 'BB']); - expect(multiCacher('A', 'B')).toBe(secondValue); - expect(multiXformer.mock.calls.length).toBe(2); - - const thirdValue = multiCacher('A', 'B', 'extra-arg'); - expect(multiXformer.mock.calls.length).toBe(3); - expect(thirdValue).not.toBe(secondValue); - expect(thirdValue).toEqual(secondValue); -}); diff --git a/yarn.lock b/yarn.lock index 379062d7e8..ca3a2cd4b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7773,6 +7773,7 @@ lodash.uniq@^4.5.0: lodash@4.17.11, lodash@4.x, "lodash@>=3.5 <5", lodash@^3.10.0, lodash@^4.15.0, lodash@^4.16.5, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" + integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg== logfmt@^1.2.0: version "1.2.1" @@ -7924,6 +7925,11 @@ mem@^4.0.0: mimic-fn "^1.0.0" p-is-promise "^2.0.0" +memoize-one@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.0.0.tgz#d55007dffefb8de7546659a1722a5d42e128286e" + integrity sha512-7g0+ejkOaI9w5x6LvQwmj68kUj6rxROywPSCqmclG/HBacmFnZqhVscQ8kovkn9FBCNJmOz6SY42+jnvZzDWdw== + memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"