Logical View: Restructuring, tooltips, and QoL changes (#8916)

This commit is contained in:
Max Fitton
2020-06-15 14:09:29 -07:00
committed by GitHub
parent ddb9368f2c
commit 4a66b6783a
5 changed files with 236 additions and 58 deletions
+17 -3
View File
@@ -160,7 +160,11 @@ export type RayletActorInfo =
numObjectIdsInScope: number;
pid: number;
port: number;
state: 0 | 1 | 2;
state:
| ActorState.Creating
| ActorState.Alive
| ActorState.Restarting
| ActorState.Dead;
taskQueueLength: number;
timestamp: number;
usedObjectStoreMemory: number;
@@ -173,10 +177,20 @@ export type RayletActorInfo =
actorId: string;
actorTitle: string;
requiredResources: { [key: string]: number };
state: -1;
invalidStateType?: "infeasibleActor" | "pendingActor";
state: ActorState.Invalid;
invalidStateType?: InvalidStateType;
};
export type InvalidStateType = "infeasibleActor" | "pendingActor";
export enum ActorState {
Invalid = -1,
Creating = 0,
Alive = 1,
Restarting = 2,
Dead = 3,
}
export type RayletInfoResponse = {
nodes: {
[ip: string]: {
@@ -8,6 +8,7 @@ import {
} from "@material-ui/core";
import React from "react";
import {
ActorState,
checkProfilingStatus,
CheckProfilingStatusResponse,
getProfilingResultURL,
@@ -15,8 +16,11 @@ import {
launchProfiling,
RayletActorInfo,
} from "../../../api";
import ActorDetailsPane from "./ActorDetailsPane";
import Actors from "./Actors";
const memoryDebuggingDocLink =
"https://docs.ray.io/en/latest/memory-management.html#debugging-using-ray-memory";
const styles = (theme: Theme) =>
createStyles({
root: {
@@ -43,14 +47,7 @@ const styles = (theme: Theme) =>
invalidStateTypePendingActor: {
color: theme.palette.secondary.main,
},
information: {
fontSize: "0.875rem",
},
datum: {
"&:not(:first-child)": {
marginLeft: theme.spacing(2),
},
},
webuiDisplay: {
fontSize: "0.875rem",
},
@@ -86,7 +83,7 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
handleProfilingClick = (duration: number) => async () => {
const actor = this.props.actor;
if (actor.state !== -1) {
if (actor.state !== ActorState.Invalid) {
const profilingId = await launchProfiling(
actor.nodeId,
actor.pid,
@@ -119,7 +116,10 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
killActor = () => {
const actor = this.props.actor;
if (actor.state === 0) {
if (
actor.state === ActorState.Creating ||
actor.state === ActorState.Alive
) {
launchKillActor(actor.actorId, actor.ipAddress, actor.port);
}
};
@@ -129,16 +129,8 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
const { expanded, profiling } = this.state;
const information =
actor.state !== -1
actor.state !== ActorState.Invalid
? [
{
label: "ActorTitle",
value: actor.actorTitle,
},
{
label: "State",
value: actor.state.toLocaleString(),
},
{
label: "Resources",
value:
@@ -149,34 +141,48 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
.join(", "),
},
{
label: "Pending",
label: "Number of pending tasks",
value: actor.taskQueueLength.toLocaleString(),
tooltip:
"The number of tasks that are currently pending to execute on this actor. If this number " +
"remains consistently high, it may indicate that this actor is a bottleneck in your application.",
},
{
label: "Executed",
label: "Number of executed tasks",
value: actor.numExecutedTasks.toLocaleString(),
tooltip:
"The number of tasks this actor has executed throughout its lifetimes.",
},
{
label: "NumObjectIdsInScope",
label: "Number of ObjectIDs in scope",
value: actor.numObjectIdsInScope.toLocaleString(),
tooltip:
"The number of ObjectIDs that this actor is keeping in scope via its internal state. " +
"This does not imply that the objects are in active use or colocated on the node with the actor " +
`currently. This can be useful for debugging memory leaks. See the docs at ${memoryDebuggingDocLink} ` +
"for more information.",
},
{
label: "NumLocalObjects",
label: "Number of local objects",
value: actor.numLocalObjects.toLocaleString(),
tooltip:
"The number of small objects that this actor has stored in its local in-process memory store. This can be useful for " +
`debugging memory leaks. See the docs at ${memoryDebuggingDocLink} for more information`,
},
{
label: "UsedLocalObjectMemory",
label: "Object store memory used (MiB)",
value: actor.usedObjectStoreMemory.toLocaleString(),
tooltip:
"The total amount of memory that this actor is occupying in the Ray object store. " +
"If this number is increasing without bounds, you might have a memory leak. See " +
`the docs at: ${memoryDebuggingDocLink} for more information.`,
},
// {
// label: "Task",
// value: actor.currentTaskFuncDesc.join(".")
// }
]
: [
{
label: "ID",
label: "Actor ID",
value: actor.actorId,
tooltip: "",
},
{
label: "Required resources",
@@ -186,12 +192,13 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
.sort((a, b) => a[0].localeCompare(b[0]))
.map(([key, value]) => `${value.toLocaleString()} ${key}`)
.join(", "),
tooltip: "",
},
];
// Construct the custom message from the actor.
let actorCustomDisplay: JSX.Element[] = [];
if (actor.state !== -1 && actor.webuiDisplay) {
if (actor.state !== ActorState.Invalid && actor.webuiDisplay) {
actorCustomDisplay = Object.keys(actor.webuiDisplay)
.sort()
.map((key, _, __) => {
@@ -228,7 +235,7 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
return (
<div className={classes.root}>
<Typography className={classes.title}>
{actor.state !== -1 ? (
{actor.state !== ActorState.Invalid ? (
<React.Fragment>
Actor {actor.actorId}{" "}
{Object.entries(actor.children).length > 0 && (
@@ -289,8 +296,8 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
</React.Fragment>
) : actor.invalidStateType === "infeasibleActor" ? (
<span className={classes.invalidStateTypeInfeasible}>
{actor.actorTitle} is infeasible. (This actor cannot be created
because the Ray cluster cannot satisfy its resource requirements.)
{actor.actorTitle} cannot be created because the Ray cluster
cannot satisfy its resource requirements.)
</span>
) : (
<span className={classes.invalidStateTypePendingActor}>
@@ -298,20 +305,12 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
</span>
)}
</Typography>
<Typography className={classes.information}>
{information.map(
({ label, value }) =>
value &&
value.length > 0 && (
<React.Fragment key={label}>
<span className={classes.datum}>
{label}: {value}
</span>{" "}
</React.Fragment>
),
)}
</Typography>
{actor.state !== -1 && (
<ActorDetailsPane
actorDetails={information}
actorTitle={actor.actorTitle}
actorState={actor.state}
/>
{actor.state !== ActorState.Invalid && (
<React.Fragment>
{actorCustomDisplay.length > 0 && (
<React.Fragment>{actorCustomDisplay}</React.Fragment>
@@ -0,0 +1,155 @@
import {
createStyles,
Divider,
Grid,
makeStyles,
Theme,
Tooltip,
} from "@material-ui/core";
import React from "react";
import { ActorState, InvalidStateType } from "../../../api";
type LabeledDatumProps = {
label: string;
datum: any;
tooltip?: string;
};
const useLabeledDatumStyles = makeStyles({
label: {
textDecorationLine: "underline",
textDecorationColor: "#a6c3e3",
textDecorationThickness: "1px",
textDecorationStyle: "dotted",
cursor: "help",
},
});
const LabeledDatum: React.FC<LabeledDatumProps> = ({
label,
datum,
tooltip,
}) => {
const classes = useLabeledDatumStyles();
const innerHtml = (
<Grid container item xs={6}>
<Grid item xs={6}>
<span className={classes.label}>{label}</span>
</Grid>
<Grid item xs={6}>
<span>{datum}</span>
</Grid>
</Grid>
);
return tooltip ? <Tooltip title={tooltip}>{innerHtml}</Tooltip> : innerHtml;
};
type ActorStateReprProps = {
state: ActorState;
ist?: InvalidStateType;
};
const actorStateReprStyles = makeStyles((theme: Theme) =>
createStyles({
infeasible: {
color: theme.palette.error.light,
},
pending: {
color: theme.palette.warning.light,
},
unknown: {
color: theme.palette.warning.light,
},
creating: {
color: theme.palette.success.light,
},
alive: {
color: theme.palette.success.dark,
},
restarting: {
color: theme.palette.warning.light,
},
dead: {
color: "#cccccc",
},
}),
);
const ActorStateRepr: React.FC<ActorStateReprProps> = ({ state, ist }) => {
const classes = actorStateReprStyles();
const { Alive, Dead, Creating, Restarting, Invalid } = ActorState;
switch (state) {
case Invalid:
if (ist === "infeasibleActor") {
return <div className={classes.infeasible}>Infeasible</div>;
}
if (ist === "pendingActor") {
return <div className={classes.pending}>Pending Resources</div>;
}
return <div className={classes.unknown}>Unknown</div>;
case Creating:
return <div className={classes.creating}>Creating</div>;
case Alive:
return <div className={classes.alive}>Alive</div>;
case Restarting:
return <div className={classes.restarting}>Restarting</div>;
case Dead:
return <div className={classes.dead}>Dead</div>;
}
};
type ActorDetailsPaneProps = {
actorTitle: string;
invalidStateType?: InvalidStateType;
actorState: ActorState;
actorDetails: {
label: string;
value: any;
tooltip?: string;
}[];
};
const useStyles = makeStyles((theme: Theme) => ({
divider: {
width: "100%",
margin: "0 auto",
},
actorTitleWrapper: {
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1),
fontWeight: "bold",
fontSize: "130%",
},
detailsPane: {
margin: theme.spacing(1),
},
}));
const ActorDetailsPane: React.FC<ActorDetailsPaneProps> = ({
actorTitle,
actorDetails,
actorState,
invalidStateType,
}) => {
const classes = useStyles();
return (
<React.Fragment>
<div className={classes.actorTitleWrapper}>
<div>{actorTitle}</div>
<ActorStateRepr ist={invalidStateType} state={actorState} />
</div>
<Divider className={classes.divider} />
<Grid container className={classes.detailsPane}>
{actorDetails.map(
({ label, value, tooltip }) =>
value &&
value.length > 0 && (
<LabeledDatum label={label} datum={value} tooltip={tooltip} />
),
)}
</Grid>
</React.Fragment>
);
};
export default ActorDetailsPane;
@@ -1,5 +1,5 @@
import React, { Fragment } from "react";
import { RayletInfoResponse } from "../../../api";
import { ActorState, RayletInfoResponse } from "../../../api";
import Actor from "./Actor";
type ActorProps = {
@@ -8,10 +8,20 @@ type ActorProps = {
const Actors = (props: ActorProps) => {
const { actors } = props;
const actorChildren = Object.entries(actors).map(([actorId, actor]) => (
<Actor actor={actor} key={actorId} />
));
const actorChildren = Object.values(actors)
.sort((actor1, actor2) => {
if (
actor1.state === ActorState.Dead &&
actor2.state === ActorState.Dead
) {
return 0;
} else if (actor2.state === ActorState.Dead) {
return -1;
} else {
return 1;
}
})
.map((actor) => <Actor actor={actor} key={actor.actorId} />);
return <Fragment>{actorChildren}</Fragment>;
};
@@ -7,7 +7,7 @@ import {
} from "@material-ui/core";
import React, { useState } from "react";
import { connect } from "react-redux";
import { RayletActorInfo, RayletInfoResponse } from "../../../api";
import { ActorState, RayletActorInfo, RayletInfoResponse } from "../../../api";
import { StoreState } from "../../../store";
import Actors from "./Actors";
@@ -29,7 +29,7 @@ const getNestedActorTitles = (actor: RayletActorInfo): string[] => {
const actorTitle = actor.actorTitle;
const titles: string[] = actorTitle ? [actorTitle] : [];
// state of -1 indicates an actor data record that does not have children.
if (actor.state === -1) {
if (actor.state === ActorState.Invalid) {
return titles;
}
const children = actor["children"];
@@ -53,7 +53,7 @@ type LogicalViewProps = {
rayletInfo: RayletInfoResponse | null;
} & ReturnType<typeof mapStateToProps>;
const LogicalView = ({ rayletInfo }: LogicalViewProps) => {
const LogicalView: React.FC<LogicalViewProps> = ({ rayletInfo }) => {
const [nameFilter, setNameFilter] = useState("");
if (rayletInfo === null) {