mirror of https://github.com/grafana/grafana
Explore: Memory leak fix due to dedup selector (#20107)
Change custom hashing and lodash.memoize based selector for standard reselect.pull/20052/head
parent
fe584efc70
commit
dca872f75f
@ -1,5 +0,0 @@ |
|||||||
import { memoize } from 'lodash'; |
|
||||||
import { createSelectorCreator } from 'reselect'; |
|
||||||
|
|
||||||
const hashFn = (...args: any[]) => args.reduce((acc, val) => acc + '-' + JSON.stringify(val), ''); |
|
||||||
export const createLodashMemoizedSelector = createSelectorCreator(memoize as any, hashFn); |
|
@ -0,0 +1,77 @@ |
|||||||
|
import React from 'react'; |
||||||
|
import { LogLevel, LogRowModel } from '@grafana/data'; |
||||||
|
import { mount } from 'enzyme'; |
||||||
|
import { LiveLogsWithTheme } from './LiveLogs'; |
||||||
|
|
||||||
|
describe('LiveLogs', () => { |
||||||
|
it('renders logs', () => { |
||||||
|
const rows: LogRowModel[] = [makeLog({ uid: '1' }), makeLog({ uid: '2' }), makeLog({ uid: '3' })]; |
||||||
|
const wrapper = mount( |
||||||
|
<LiveLogsWithTheme |
||||||
|
logRows={rows} |
||||||
|
timeZone={'utc'} |
||||||
|
stopLive={() => {}} |
||||||
|
onPause={() => {}} |
||||||
|
onResume={() => {}} |
||||||
|
isPaused={true} |
||||||
|
/> |
||||||
|
); |
||||||
|
|
||||||
|
expect(wrapper.contains('log message 1')).toBeTruthy(); |
||||||
|
expect(wrapper.contains('log message 2')).toBeTruthy(); |
||||||
|
expect(wrapper.contains('log message 3')).toBeTruthy(); |
||||||
|
}); |
||||||
|
|
||||||
|
it('renders new logs only when not paused', () => { |
||||||
|
const rows: LogRowModel[] = [makeLog({ uid: '1' }), makeLog({ uid: '2' }), makeLog({ uid: '3' })]; |
||||||
|
const wrapper = mount( |
||||||
|
<LiveLogsWithTheme |
||||||
|
logRows={rows} |
||||||
|
timeZone={'utc'} |
||||||
|
stopLive={() => {}} |
||||||
|
onPause={() => {}} |
||||||
|
onResume={() => {}} |
||||||
|
isPaused={true} |
||||||
|
/> |
||||||
|
); |
||||||
|
|
||||||
|
wrapper.setProps({ |
||||||
|
...wrapper.props(), |
||||||
|
logRows: [makeLog({ uid: '4' }), makeLog({ uid: '5' }), makeLog({ uid: '6' })], |
||||||
|
}); |
||||||
|
|
||||||
|
expect(wrapper.contains('log message 1')).toBeTruthy(); |
||||||
|
expect(wrapper.contains('log message 2')).toBeTruthy(); |
||||||
|
expect(wrapper.contains('log message 3')).toBeTruthy(); |
||||||
|
|
||||||
|
(wrapper.find('LiveLogs').instance() as any).liveEndDiv.scrollIntoView = () => {}; |
||||||
|
|
||||||
|
wrapper.setProps({ |
||||||
|
...wrapper.props(), |
||||||
|
isPaused: false, |
||||||
|
}); |
||||||
|
|
||||||
|
expect(wrapper.contains('log message 4')).toBeTruthy(); |
||||||
|
expect(wrapper.contains('log message 5')).toBeTruthy(); |
||||||
|
expect(wrapper.contains('log message 6')).toBeTruthy(); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
const makeLog = (overides: Partial<LogRowModel>): LogRowModel => { |
||||||
|
const uid = overides.uid || '1'; |
||||||
|
const entry = `log message ${uid}`; |
||||||
|
return { |
||||||
|
uid, |
||||||
|
logLevel: LogLevel.debug, |
||||||
|
entry, |
||||||
|
hasAnsi: false, |
||||||
|
labels: {}, |
||||||
|
raw: entry, |
||||||
|
timestamp: '', |
||||||
|
timeFromNow: '', |
||||||
|
timeEpochMs: 1, |
||||||
|
timeLocal: '', |
||||||
|
timeUtc: '', |
||||||
|
...overides, |
||||||
|
}; |
||||||
|
}; |
@ -1,29 +1,19 @@ |
|||||||
import { createLodashMemoizedSelector } from 'app/core/utils/reselect'; |
import { createSelector } from 'reselect'; |
||||||
import { ExploreItemState } from 'app/types'; |
import { ExploreItemState } from 'app/types'; |
||||||
import { filterLogLevels, dedupLogRows } from 'app/core/logs_model'; |
import { filterLogLevels, dedupLogRows } from 'app/core/logs_model'; |
||||||
|
|
||||||
export const exploreItemUIStateSelector = (itemState: ExploreItemState) => { |
const logsRowsSelector = (state: ExploreItemState) => state.logsResult && state.logsResult.rows; |
||||||
const { showingGraph, showingTable, showingStartPage, dedupStrategy } = itemState; |
|
||||||
return { |
|
||||||
showingGraph, |
|
||||||
showingTable, |
|
||||||
showingStartPage, |
|
||||||
dedupStrategy, |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
|
||||||
const logsSelector = (state: ExploreItemState) => state.logsResult; |
|
||||||
const hiddenLogLevelsSelector = (state: ExploreItemState) => state.hiddenLogLevels; |
const hiddenLogLevelsSelector = (state: ExploreItemState) => state.hiddenLogLevels; |
||||||
const dedupStrategySelector = (state: ExploreItemState) => state.dedupStrategy; |
const dedupStrategySelector = (state: ExploreItemState) => state.dedupStrategy; |
||||||
export const deduplicatedLogsSelector = createLodashMemoizedSelector( |
export const deduplicatedRowsSelector = createSelector( |
||||||
logsSelector, |
logsRowsSelector, |
||||||
hiddenLogLevelsSelector, |
hiddenLogLevelsSelector, |
||||||
dedupStrategySelector, |
dedupStrategySelector, |
||||||
(logs, hiddenLogLevels, dedupStrategy) => { |
function dedupRows(rows, hiddenLogLevels, dedupStrategy) { |
||||||
if (!logs) { |
if (!(rows && rows.length)) { |
||||||
return null; |
return rows; |
||||||
} |
} |
||||||
const filteredData = filterLogLevels(logs, new Set(hiddenLogLevels)); |
const filteredRows = filterLogLevels(rows, new Set(hiddenLogLevels)); |
||||||
return dedupLogRows(filteredData, dedupStrategy); |
return dedupLogRows(filteredRows, dedupStrategy); |
||||||
} |
} |
||||||
); |
); |
||||||
|
Loading…
Reference in new issue