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';
4 changes: 4 additions & 0 deletions packages/components/src/popper/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type TooltipProps = typeof Tooltip.defaultProps & {
reshowTimeout?: number;
timeout?: number;
referenceObject?: ReferenceObject | null;
onEntered?: () => void;
onExited?: () => void;
'data-testid'?: string;
};
Expand Down Expand Up @@ -49,6 +50,7 @@ class Tooltip extends Component<TooltipProps, TooltipState> {
popperClassName: '',
reshowTimeout: Tooltip.defaultReshowTimeout,
timeout: Tooltip.defaultTimeout,
onEntered: (): void => undefined,
onExited: (): void => undefined,
'data-testid': undefined,
};
Expand Down Expand Up @@ -293,6 +295,7 @@ class Tooltip extends Component<TooltipProps, TooltipState> {
referenceObject,
popperClassName,
'data-testid': dataTestId,
onEntered,
} = this.props;
const { isShown } = this.state;
return (
Expand All @@ -305,6 +308,7 @@ class Tooltip extends Component<TooltipProps, TooltipState> {
className={classNames(popperClassName)}
options={options}
ref={this.popper}
onEntered={onEntered}
onExited={this.handleExited}
interactive={interactive}
referenceObject={referenceObject}
Expand Down
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;
color: $gray-300;
background-color: $gray-900;
border: $black;
width: 4.25rem;
vertical-align: middle;
text-align: center;
user-select: none;
z-index: 1;
}

.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: $foreground;
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: $spacer-1;
}

.heap-utilisation-text {
margin-top: $spacer-1;
color: $gray-400;
}
202 changes: 202 additions & 0 deletions packages/console/src/HeapUsage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
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 [isOpen, setIsOpen] = 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 || isOpen) {
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,
isOpen ? hoverUpdateInterval : defaultUpdateInterval
);
return () => {
clearInterval(updateUsage);
};
},
[
isOpen,
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 className="font-weight-bold">{text}</div>
<div>{size}</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">
<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
onEntered={() => setIsOpen(true)}
onExited={() => setIsOpen(false)}
interactive
>
<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">
% utilization over {Math.round(monitorDuration / 1000 / 60)} min.
</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
Loading