Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Heap size UI #718

Merged
merged 13 commits into from
Aug 26, 2022
2 changes: 2 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions packages/chart/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export { default as ChartUtils } from './ChartUtils';
export { default as FigureChartModel } from './FigureChartModel';
export { default as MockChartModel } from './MockChartModel';
export { default as Plot } from './plotly/Plot';
export { default as ChartTheme } from './ChartTheme';
export { default as isFigureChartModel } from './isFigureChartModel';
1 change: 1 addition & 0 deletions packages/console/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"@deephaven/log": "file:../log",
"@deephaven/storage": "file:../storage",
"@deephaven/utils": "file:../utils",
"@deephaven/chart": "file:../chart",
"@fortawesome/react-fontawesome": "^0.1.18",
"classnames": "^2.3.1",
"lodash.debounce": "^4.0.8",
Expand Down
60 changes: 60 additions & 0 deletions packages/console/src/HeapUsage.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
@import '../../components/scss/custom.scss';

.max-memory {
position: relative;
background-color: $gray-900;
width: 4rem;
vertical-align: middle;
text-align: center;
border: $black;
z-index: 1;
user-select: none;
color: $gray-300;
}

.total-memory,
.used-memory {
position: absolute;
inset: 0;
margin: 1px;
z-index: -1;
}

.total-memory {
background-color: $gray-700;
}

.used-memory {
background-color: $gray-500;
}

.heap-overflow {
background-color: $red;
}

.heap-tooltip {
padding: $spacer-1;
width: 200px;
}

.heap-usage-info-row {
display: flex;
color: white;
padding: $spacer-1;
justify-content: space-between;
}

.heap-plot {
display: flex;
align-items: center;
}

.heading-bottom-border {
border-bottom: 1px solid $gray-900;
margin-bottom: 0.5rem;
}

.heap-utilisation-text {
margin-top: $spacer-1;
color: $gray-400;
}
217 changes: 217 additions & 0 deletions packages/console/src/HeapUsage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
import { Tooltip } from '@deephaven/components';
import { QueryConnectable } from '@deephaven/jsapi-shim';
import React, { useEffect, useState, ReactElement, useRef } from 'react';
import { Plot, ChartTheme } from '@deephaven/chart';
import './HeapUsage.scss';
import classNames from 'classnames';

interface HeapUsageProps {
connection: QueryConnectable;
defaultUpdateInterval: number;
hoverUpdateInterval: number;
bgMonitoring?: boolean;

// in millis
monitorDuration: number;
}

const HeapUsage = ({
connection,
defaultUpdateInterval,
hoverUpdateInterval,
bgMonitoring = true,
monitorDuration,
}: HeapUsageProps): ReactElement => {
const [memoryUsage, setMemoryUsage] = useState({
freeMemory: 0,
maximumHeapSize: 999,
totalHeapSize: 0,
});

const [hover, setHover] = useState(false);

const historyUsage = useRef<{ timestamps: number[]; usages: number[] }>({
timestamps: [],
usages: [],
});

useEffect(
function setUsageUpdateInterval() {
const fetchAndUpdate = async () => {
const newUsage = await connection.getWorkerHeapInfo();
setMemoryUsage(newUsage);

if (bgMonitoring || hover) {
const currentUsage =
(newUsage.totalHeapSize - newUsage.freeMemory) /
newUsage.maximumHeapSize;
const currentTime = Date.now();

const { timestamps, usages } = historyUsage.current;
while (
timestamps.length !== 0 &&
currentTime - timestamps[0] > monitorDuration * 1.5
) {
timestamps.shift();
usages.shift();
}

timestamps.push(currentTime);
usages.push(currentUsage);
} else {
historyUsage.current = { timestamps: [], usages: [] };
}
};
fetchAndUpdate();

const updateUsage = setInterval(
fetchAndUpdate,
hover ? hoverUpdateInterval : defaultUpdateInterval
);
return () => {
clearInterval(updateUsage);
};
},
[
hover,
hoverUpdateInterval,
connection,
defaultUpdateInterval,
monitorDuration,
bgMonitoring,
]
);

const toDecimalPlace = (num: number, dec: number) =>
Math.round(num * 10 ** dec) / 10 ** dec;

const decimalPlace = 2;
const GbToByte = 1024 ** 3;

const { freeMemory, totalHeapSize, maximumHeapSize } = memoryUsage;

const freeMemoryGB = toDecimalPlace(freeMemory / GbToByte, decimalPlace);
const totalHeapGB = toDecimalPlace(totalHeapSize / GbToByte, decimalPlace);
const maxHeapGB = toDecimalPlace(maximumHeapSize / GbToByte, decimalPlace);
const inUseGB = totalHeapGB - freeMemoryGB;

const getRow = (text: string, size: string, bottomBorder = false) => (
<div
className={classNames(`heap-usage-info-row`, {
'heading-bottom-border': bottomBorder,
})}
>
<div>
<h6
style={{
fontWeight: 'bold',
}}
>
{text}
</h6>
</div>
<div>
{/* <h6> */}
{size}
{/* </h6> */}
dsmmcken marked this conversation as resolved.
Show resolved Hide resolved
</div>
</div>
);

const { timestamps, usages } = historyUsage.current;

const lastTimestamp = timestamps[timestamps.length - 1] ?? 0;

const totalPercentage = totalHeapSize / maximumHeapSize;
const usedPercentage = (totalHeapSize - freeMemory) / maximumHeapSize;

return (
<>
<div
className="max-memory"
onMouseEnter={() => setHover(true)}
onMouseLeave={() => setHover(false)}
>
<div
className="total-memory"
style={{
width: `calc(${totalPercentage * 100}% - ${totalPercentage * 2}px`,
}}
/>
<div
className={classNames('used-memory', {
'heap-overflow':
(totalHeapSize - freeMemory) / maximumHeapSize > 0.95,
})}
style={{
width: `calc(${usedPercentage * 100}% - ${usedPercentage * 2}px`,
}}
/>

<div className="memory-text">{maxHeapGB.toFixed(1)} GB</div>
</div>

<Tooltip>
<div className="heap-tooltip">
{getRow(
'In use:',
`${inUseGB.toFixed(decimalPlace)} of ${maxHeapGB.toFixed(
decimalPlace
)} GB`,
true
)}
{getRow('Free:', `${freeMemoryGB.toFixed(decimalPlace)} GB`)}
{getRow('Total:', `${totalHeapGB.toFixed(decimalPlace)} GB`)}
{getRow('Max:', `${maxHeapGB.toFixed(decimalPlace)} GB`)}
<div className="heap-plot">
<Plot
data={[
{
x: [...timestamps],
y: [...usages],
type: 'scatter',
mode: 'lines',
},
]}
config={{ staticPlot: true, responsive: true }}
style={{
width: '196px',
height: '100px',
}}
layout={{
legend: false,
margin: { l: 2, t: 2, r: 2, b: 2 },
plot_bgcolor: 'transparent',
paper_bgcolor: 'transparent',
colorway: ['#4878ea'],
xaxis: {
dtick: Math.round(monitorDuration / 6),
gridcolor: ChartTheme.linecolor,
range: [lastTimestamp - monitorDuration, lastTimestamp],
linecolor: ChartTheme.linecolor,
linewidth: 2,
mirror: true,
},
yaxis: {
dtick: 0.2,
gridcolor: ChartTheme.linecolor,
range: [0, 1],
linecolor: ChartTheme.linecolor,
linewidth: 2,
mirror: true,
},
}}
/>
</div>
<div className="heap-utilisation-text">
<h6>
% utilization over {Math.round(monitorDuration / 1000 / 60)} min.
</h6>
dsmmcken marked this conversation as resolved.
Show resolved Hide resolved
</div>
</div>
</Tooltip>
</>
);
};

export default HeapUsage;
1 change: 1 addition & 0 deletions packages/console/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export * from './common';
export * from './command-history';
export * from './console-history';
export { default as LogView } from './log/LogView';
export { default as HeapUsage } from './HeapUsage';

export { default } from './Console';
3 changes: 3 additions & 0 deletions packages/console/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
},
{
"path": "../utils"
},
{
"path": "../chart"
}
]
}
14 changes: 13 additions & 1 deletion packages/dashboard-core-plugins/src/panels/ConsolePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
CommandHistoryStorage,
Console,
ConsoleConstants,
HeapUsage,
} from '@deephaven/console';
import { PanelEvent } from '@deephaven/dashboard';
import { IdeSession, VariableDefinition } from '@deephaven/jsapi-shim';
Expand Down Expand Up @@ -321,7 +322,8 @@ export class ConsolePanel extends PureComponent<
unzip,
} = this.props;
const { consoleSettings, error, objectMap } = this.state;
const { config, session } = sessionWrapper;
// eslint-disable-next-line react/prop-types
const { config, session, connection } = sessionWrapper;
const { id: sessionId, type: language } = config;

return (
Expand Down Expand Up @@ -350,6 +352,16 @@ export class ConsolePanel extends PureComponent<
<>
<div>&nbsp;</div>
<div>{ConsoleConstants.LANGUAGE_MAP.get(language)}</div>
<div>&nbsp;</div>
<div>
<HeapUsage
connection={connection}
defaultUpdateInterval={10 * 1000}
hoverUpdateInterval={3 * 1000}
monitorDuration={10 * 60 * 1000}
/>
</div>
<div>&nbsp;</div>
</>
}
scope={sessionId}
Expand Down
7 changes: 7 additions & 0 deletions packages/jsapi-shim/src/dh.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -875,7 +875,14 @@ export interface TableMap extends Evented {
getTable(key: object): Promise<Table>;
}

export interface WorkerHeapInfo {
readonly maximumHeapSize: number;
readonly freeMemory: number;
readonly totalHeapSize: number;
}

export interface QueryConnectable extends Evented {
getWorkerHeapInfo(): Promise<WorkerHeapInfo>;
getConsoleTypes(): Promise<string[]>;
startSession(type: string): Promise<IdeSession>;
}
Expand Down
5 changes: 3 additions & 2 deletions packages/redux/src/actions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Action } from 'redux';
import type { ThunkAction } from 'redux-thunk';
import type { CommandHistoryStorage } from '@deephaven/console';
import type { FileStorage } from '@deephaven/file-explorer';
import {
SET_PLUGINS,
Expand Down Expand Up @@ -42,7 +41,9 @@ export const setWorkspaceStorage: PayloadActionCreator<WorkspaceStorage> = works
payload: workspaceStorage,
});

export const setCommandHistoryStorage: PayloadActionCreator<CommandHistoryStorage> = commandHistoryStorage => ({
export const setCommandHistoryStorage: PayloadActionCreator<
Record<string, unknown>
> = commandHistoryStorage => ({
dsmmcken marked this conversation as resolved.
Show resolved Hide resolved
type: SET_COMMAND_HISTORY_STORAGE,
payload: commandHistoryStorage,
});
Expand Down
Loading