[Dashboard] Update ActorState in dashboard to support new actor states (#9855)

* Update ActorState in dashboard to support new actor states

* Update dashboard documentation for new states

* Add missing state to doc

Co-authored-by: Max Fitton <max@semprehealth.com>
This commit is contained in:
Max Fitton
2020-08-05 10:35:18 -07:00
committed by SangBin Cho
parent aa6f0b7a6c
commit c1c65619d1
5 changed files with 176 additions and 146 deletions
+56 -42
View File
@@ -146,52 +146,66 @@ export type RayletWorkerStats = {
coreWorkerStats: RayletCoreWorkerStats;
};
export type RayletActorInfo =
| {
actorId: string;
actorTitle: string;
averageTaskExecutionSpeed: number;
children: RayletInfoResponse["actors"];
// currentTaskFuncDesc: string[];
ipAddress: string;
jobId: string;
nodeId: string;
numExecutedTasks: number;
numLocalObjects: number;
numObjectRefsInScope: number;
pid: number;
port: number;
state:
| ActorState.Creating
| ActorState.Alive
| ActorState.Restarting
| ActorState.Dead;
taskQueueLength: number;
timestamp: number;
usedObjectStoreMemory: number;
usedResources: { [key: string]: ResourceAllocations };
currentTaskDesc?: string;
numPendingTasks?: number;
webuiDisplay?: Record<string, string>;
}
| {
actorId: string;
actorTitle: string;
requiredResources: { [key: string]: number };
state: ActorState.Invalid;
invalidStateType?: InvalidStateType;
};
export type InvalidStateType = "infeasibleActor" | "pendingActor";
export enum ActorState {
Invalid = -1,
Creating = 0,
Alive = 1,
Restarting = 2,
Dead = 3,
DependenciesUnready = 0,
PendingCreation = 1,
Alive = 2,
Restarting = 3,
Dead = 4,
}
export type RayletActorInfo = FullActorInfo | PartialActorInfo;
export type FullActorInfo = {
actorId: string;
actorTitle: string;
averageTaskExecutionSpeed: number;
children: RayletInfoResponse["actors"];
// currentTaskFuncDesc: string[];
ipAddress: string;
jobId: string;
nodeId: string;
numExecutedTasks: number;
numLocalObjects: number;
numObjectRefsInScope: number;
pid: number;
port: number;
state:
| ActorState.Alive
| ActorState.Restarting
| ActorState.Dead
| ActorState.DependenciesUnready
| ActorState.PendingCreation;
taskQueueLength: number;
timestamp: number;
usedObjectStoreMemory: number;
usedResources: { [key: string]: ResourceAllocations };
currentTaskDesc?: string;
numPendingTasks?: number;
webuiDisplay?: Record<string, string>;
};
export type PartialActorInfo = {
actorId: string;
actorTitle: string;
requiredResources: { [key: string]: number };
state: ActorState.Invalid;
invalidStateType?: InvalidStateType;
};
// eslint-disable-next-line
export function isFullActorInfo(
rayletInfo: RayletActorInfo,
): rayletInfo is FullActorInfo {
// Lint disabled because arrow functions don't play well with type guards.
// This function is used to determine what kind of information we have about
// a given actor in a response based on its state.
return rayletInfo.state !== ActorState.Invalid;
}
export type InvalidStateType = "infeasibleActor" | "pendingActor";
export type RayletInfoResponse = {
nodes: {
[ip: string]: {
@@ -12,6 +12,7 @@ import {
checkProfilingStatus,
CheckProfilingStatusResponse,
getProfilingResultURL,
isFullActorInfo,
launchKillActor,
launchProfiling,
RayletActorInfo,
@@ -84,7 +85,7 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
handleProfilingClick = (duration: number) => async () => {
const actor = this.props.actor;
if (actor.state !== ActorState.Invalid) {
if (actor.state === ActorState.Alive) {
const profilingId = await launchProfiling(
actor.nodeId,
actor.pid,
@@ -117,10 +118,7 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
killActor = () => {
const actor = this.props.actor;
if (
actor.state === ActorState.Creating ||
actor.state === ActorState.Alive
) {
if (actor.state === ActorState.Alive) {
launchKillActor(actor.actorId, actor.ipAddress, actor.port);
}
};
@@ -128,83 +126,84 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
render() {
const { classes, actor } = this.props;
const { expanded, profiling } = this.state;
const information =
actor.state !== ActorState.Invalid
? [
{
label: "Resources",
value:
Object.entries(actor.usedResources).length > 0 &&
Object.entries(actor.usedResources)
.sort((a, b) => a[0].localeCompare(b[0]))
.map(
([key, value]) =>
`${sum(
value.resourceSlots.map((slot) => slot.allocation),
)} ${key}`,
)
.join(", "),
},
{
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: "Number of executed tasks",
value: actor.numExecutedTasks.toLocaleString(),
tooltip:
"The number of tasks this actor has executed throughout its lifetimes.",
},
{
label: "Number of ObjectRefs in scope",
value: actor.numObjectRefsInScope.toLocaleString(),
tooltip:
"The number of ObjectRefs 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: "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: "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: "Actor ID",
value: actor.actorId,
tooltip: "",
},
{
label: "Required resources",
value:
Object.entries(actor.requiredResources).length > 0 &&
Object.entries(actor.requiredResources)
.sort((a, b) => a[0].localeCompare(b[0]))
.map(([key, value]) => `${value.toLocaleString()} ${key}`)
.join(", "),
tooltip: "",
},
];
const invalidStateType = isFullActorInfo(actor)
? undefined
: actor.invalidStateType;
const information = isFullActorInfo(actor)
? [
{
label: "Resources",
value:
Object.entries(actor.usedResources).length > 0 &&
Object.entries(actor.usedResources)
.sort((a, b) => a[0].localeCompare(b[0]))
.map(
([key, value]) =>
`${sum(
value.resourceSlots.map((slot) => slot.allocation),
)} ${key}`,
)
.join(", "),
},
{
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: "Number of executed tasks",
value: actor.numExecutedTasks.toLocaleString(),
tooltip:
"The number of tasks this actor has executed throughout its lifetimes.",
},
{
label: "Number of ObjectRefs in scope",
value: actor.numObjectRefsInScope.toLocaleString(),
tooltip:
"The number of ObjectRefs 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: "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: "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: "Actor ID",
value: actor.actorId,
tooltip: "",
},
{
label: "Required resources",
value:
Object.entries(actor.requiredResources).length > 0 &&
Object.entries(actor.requiredResources)
.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 !== ActorState.Invalid && actor.webuiDisplay) {
if (isFullActorInfo(actor) && actor.webuiDisplay) {
actorCustomDisplay = Object.keys(actor.webuiDisplay)
.sort()
.map((key, _, __) => {
@@ -241,7 +240,7 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
return (
<div className={classes.root}>
<Typography className={classes.title}>
{actor.state !== ActorState.Invalid ? (
{isFullActorInfo(actor) ? (
<React.Fragment>
Actor {actor.actorId}{" "}
{Object.entries(actor.children).length > 0 && (
@@ -269,7 +268,7 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
</React.Fragment>
))}
){" "}
{actor.state === 0 && (
{actor.state === ActorState.Alive && (
<span className={classes.action} onClick={this.killActor}>
Kill Actor
</span>
@@ -303,7 +302,7 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
) : actor.invalidStateType === "infeasibleActor" ? (
<span className={classes.invalidStateTypeInfeasible}>
{actor.actorTitle} cannot be created because the Ray cluster
cannot satisfy its resource requirements.)
cannot satisfy its resource requirements.
</span>
) : (
<span className={classes.invalidStateTypePendingActor}>
@@ -315,8 +314,9 @@ class Actor extends React.Component<Props & WithStyles<typeof styles>, State> {
actorDetails={information}
actorTitle={actor.actorTitle}
actorState={actor.state}
invalidStateType={invalidStateType}
/>
{actor.state !== ActorState.Invalid && (
{isFullActorInfo(actor) && (
<React.Fragment>
{actorCustomDisplay.length > 0 && (
<React.Fragment>{actorCustomDisplay}</React.Fragment>
@@ -77,18 +77,28 @@ const actorStateReprStyles = makeStyles((theme: Theme) =>
const ActorStateRepr: React.FC<ActorStateReprProps> = ({ state, ist }) => {
const classes = actorStateReprStyles();
const { Alive, Dead, Creating, Restarting, Invalid } = ActorState;
const {
Alive,
Dead,
PendingCreation,
Restarting,
DependenciesUnready,
Invalid,
} = ActorState;
switch (state) {
case Invalid:
console.log(ist);
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.pending}>Pending</div>;
}
return <div className={classes.unknown}>Unknown</div>;
case Creating:
case PendingCreation:
return <div className={classes.creating}>Creating</div>;
case DependenciesUnready:
return <div className={classes.creating}>Dependencies Unready</div>;
case Alive:
return <div className={classes.alive}>Alive</div>;
case Restarting:
@@ -7,7 +7,11 @@ import {
} from "@material-ui/core";
import React, { useState } from "react";
import { connect } from "react-redux";
import { ActorState, RayletActorInfo, RayletInfoResponse } from "../../../api";
import {
isFullActorInfo,
RayletActorInfo,
RayletInfoResponse,
} from "../../../api";
import { filterObj } from "../../../common/util";
import { StoreState } from "../../../store";
import Actors from "./Actors";
@@ -29,8 +33,7 @@ const actorMatchesSearch = (
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 === ActorState.Invalid) {
if (!isFullActorInfo(actor)) {
return titles;
}
const children = actor["children"];