mirror of
https://github.com/wassname/ray.git
synced 2026-06-27 21:53:18 +08:00
Logical View: Restructuring, tooltips, and QoL changes (#8916)
This commit is contained in:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user