diff --git a/dashboard/client/src/api.ts b/dashboard/client/src/api.ts index 94f0222f5..7e0b45b69 100644 --- a/dashboard/client/src/api.ts +++ b/dashboard/client/src/api.ts @@ -3,6 +3,11 @@ const base = ? "http://localhost:8265" : window.location.origin; +type APIResponse = { + result: boolean; + msg: string; + data?: T; +}; // TODO(mitchellstern): Add JSON schema validation for the responses. const get = async (path: string, params: { [key: string]: any }) => { const url = new URL(path, base); @@ -11,67 +16,65 @@ const get = async (path: string, params: { [key: string]: any }) => { } const response = await fetch(url.toString()); - const json = await response.json(); + const json: APIResponse = await response.json(); - const { result, error } = json; + const { result, msg, data } = json; - if (error !== null) { - throw Error(error); + if (!result) { + throw Error(msg); } - return result as T; -}; - -const post = async (path: string, params: { [key: string]: any }) => { - const requestOptions = { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(params), - }; - - const url = new URL(path, base); - - const response = await fetch(url.toString(), requestOptions); - const json = await response.json(); - - const { result, error } = json; - - if (error !== null) { - throw Error(error); - } - - return result as T; + return data as T; }; export type RayConfigResponse = { - min_workers: number; - max_workers: number; - initial_workers: number; - autoscaling_mode: string; - idle_timeout_minutes: number; - head_type: string; - worker_type: string; + minWorkers: number; + maxWorkers: number; + initialWorkers: number; + autoscalingMode: string; + idleTimeoutMinutes: number; + headType: string; + workerType: string; }; export const getRayConfig = () => get("/api/ray_config", {}); -export type NodeInfoResponseWorker = { +export type Worker = { pid: number; - create_time: number; - cmdline: string[]; - cpu_percent: number; - cpu_times: { - system: number; - children_system: number; - user: number; - children_user: number; - }; - memory_info: { - pageins: number; - pfaults: number; - vms: number; + workerId: string; + createTime: number; + memoryInfo: { rss: number; + vms: number; + shared: number; + text: number; + lib: number; + data: number; + dirty: Number; }; + cmdline: string[]; + cpuTimes: { + user: number; + system: number; + childrenUser: number; + childrenSystem: number; + iowait: number; + }; + cpuPercent: number; + logCount: number; + errorCount: number; + language: string; + jobId: string; + coreWorkerStats: CoreWorkerStats[]; +}; + +export type CoreWorkerStats = { + ipAddress: string; + port: number; + usedResources?: { [key: string]: ResourceAllocations }; + numExecutedTasks: number; + workerId: string; + // We need the below but Ant's API does not yet support it. }; export type GPUProcessStats = { @@ -79,7 +82,7 @@ export type GPUProcessStats = { // utilization of a single process of a single GPU. username: string; command: string; - gpu_memory_usage: number; + gpuMemoryUsage: number; pid: number; }; @@ -87,43 +90,89 @@ export type GPUStats = { // This represents stats fetched from a node about a single GPU uuid: string; name: string; - temperature_gpu: number; - fan_speed: number; - utilization_gpu: number; - power_draw: number; - enforced_power_limit: number; - memory_used: number; - memory_total: number; - processes: Array; + temperatureGpu: number; + fanSpeed: number; + utilizationGpu: number; + powerDraw: number; + enforcedPowerLimit: number; + memoryUsed: number; + memoryTotal: number; + processes: GPUProcessStats[]; +}; + +export type NodeSummary = BaseNodeInfo; + +export type NodeDetails = { + workers: Worker[]; + raylet: RayletData; +} & BaseNodeInfo; + +export type RayletData = { + // Merger of GCSNodeStats and GetNodeStatsReply + // GetNodeStatsReply fields. + // Note workers are in an array in NodeDetails + objectStoreUsedMemory: number; + objectStoreAvailableMemory: number; + numWorkers: number; + + // GCSNodeStats fields + nodeId: number; + nodeManagerAddress: string; + rayletSocketName: string; + objectStoreSocketName: string; + nodeManagerPort: number; + objectManagerPort: number; + state: "ALIVE" | "DEAD"; + nodeManagerHostname: string; + metricsExportPort: number; +}; + +export type ViewData = { + viewName: string; + measures: Measure[]; +}; + +export type Measure = { + tags: string; // e.g. "Tag1:Value1,Tag2:Value2,Tag3:Value3" + intValue?: number; + doubleValue?: number; + distributionMin?: number; + distributionMean?: number; + distributionMax?: number; + distributionCount?: number; + distributionBucketBoundaries?: number[]; + distributionBucketCounts?: number[]; +}; + +type BaseNodeInfo = { + now: number; + hostname: string; + ip: string; + bootTime: number; // System boot time expressed in seconds since epoch + cpu: number; // System-wide CPU utilization expressed as a percentage + cpus: [number, number]; // Number of logical CPUs and physical CPUs + gpus: Array; // GPU stats fetched from node, 1 entry per GPU + mem: [number, number, number]; // Total, available, and used percentage of memory + disk: { + [dir: string]: { + total: number; + free: number; + used: number; + percent: number; + }; + }; + loadAvg: [[number, number, number], [number, number, number]]; + net: [number, number]; // Sent and received network traffic in bytes / second + logCount: number; + errorCount: number; }; export type NodeInfoResponse = { - clients: Array<{ - now: number; - hostname: string; - ip: string; - boot_time: number; // System boot time expressed in seconds since epoch - cpu: number; // System-wide CPU utilization expressed as a percentage - cpus: [number, number]; // Number of logical CPUs and physical CPUs - gpus: Array; // GPU stats fetched from node, 1 entry per GPU - mem: [number, number, number]; // Total, available, and used percentage of memory - disk: { - [path: string]: { - total: number; - free: number; - used: number; - percent: number; - }; - }; - load_avg: [[number, number, number], [number, number, number]]; - net: [number, number]; // Sent and received network traffic in bytes / second - log_count?: { [pid: string]: number }; - error_count?: { [pid: string]: number }; - workers: Array; - }>; + clients: NodeDetails[]; }; -export const getNodeInfo = () => get("/api/node_info", {}); +export const getNodeInfo = () => + get("/nodes", { view: "details" }); export type ResourceSlot = { slot: number; @@ -134,44 +183,34 @@ export type ResourceAllocations = { resourceSlots: ResourceSlot[]; }; -export type RayletCoreWorkerStats = { - usedResources: { - [key: string]: ResourceAllocations; - }; -}; - -export type RayletWorkerStats = { - pid: number; - isDriver?: boolean; - coreWorkerStats: RayletCoreWorkerStats; -}; +export const getActorGroups = () => + get("logical/actor_groups", {}); export enum ActorState { // These two are virtual states that we air because there is // an existing task to create an actor - Infeasible = -2, // Actor task is waiting on resources (e.g. RAM, CPUs or GPUs) that the cluster does not have - PendingResources = -1, // Actor task is waiting on resources the cluster has but are in-use + Infeasible = "INFEASIBLE", // Actor task is waiting on resources (e.g. RAM, CPUs or GPUs) that the cluster does not have + PendingResources = "PENDING_RESOURCES", // Actor task is waiting on resources the cluster has but are in-use // The rest below are "official" GCS actor states - DependenciesUnready = 0, // Actor is pending on an argument to be ready - PendingCreation = 1, // Actor creation is running - Alive = 2, // Actor is alive and handling tasks - Restarting = 3, // Actor died and is being restarted - Dead = 4, // Actor died and is not being restarted + DependenciesUnready = "PENDING", // Actor is pending on an argument to be ready + PendingCreation = "CREATING", // Actor creation is running + Alive = "ALIVE", // Actor is alive and handling tasks + Restarting = "RESTARTING", // Actor died and is being restarted + Dead = "DEAD", // Actor died and is not being restarted } export type ActorInfo = FullActorInfo | ActorTaskInfo; export type FullActorInfo = { actorId: string; - actorTitle: string; - averageTaskExecutionSpeed: number; - children?: ActorInfo[]; + actorConstructor: string; + actorClass: string; ipAddress: string; jobId: string; nodeId: string; - numExecutedTasks: number; - numLocalObjects: number; - numObjectRefsInScope: number; + numExecutedTasks?: number; + numLocalObjects?: number; + numObjectRefsInScope?: number; pid: number; port: number; state: @@ -180,9 +219,9 @@ export type FullActorInfo = { | ActorState.Dead | ActorState.DependenciesUnready | ActorState.PendingCreation; - taskQueueLength: number; + taskQueueLength?: number; timestamp: number; - usedObjectStoreMemory: number; + usedObjectStoreMemory?: number; usedResources: { [key: string]: ResourceAllocations }; currentTaskDesc?: string; numPendingTasks?: number; @@ -190,8 +229,8 @@ export type FullActorInfo = { }; export type ActorTaskInfo = { - actorId?: string; - actorTitle?: string; + actorId: string; + actorClass: string; requiredResources?: { [key: string]: number }; state: ActorState.Infeasible | ActorState.PendingResources; }; @@ -221,52 +260,41 @@ export type ActorGroup = { summary: ActorGroupSummary; }; -export type RayletInfoResponse = { - nodes: { - [ip: string]: { - extraInfo?: string; - workersStats: Array; - }; - }; +export type ActorsResponse = { actorGroups: { - [groupKey: string]: ActorGroup; - }; - plasmaStats: { - [ip: string]: PlasmaStats; + [key: string]: ActorGroup; }; }; -export type PlasmaStats = { - object_store_num_local_objects: number; - object_store_available_memory: number; - object_store_used_memory: number; -}; - -export const getRayletInfo = () => - get("/api/raylet_info", {}); - export type ErrorsResponse = { - [pid: string]: Array<{ + errors: ErrorsByPid; +}; + +export type ErrorsByPid = { + [pid: string]: { message: string; timestamp: number; type: string; - }>; + }[]; }; - -export const getErrors = (hostname: string, pid: number | null) => - get("/api/errors", { - hostname, - pid: pid === null ? "" : pid, +export const getErrors = (nodeIp: string, pid: number | null) => + get("/node_errors", { + nodeIp, + pid: pid ?? "", }); export type LogsResponse = { + logs: LogsByPid; +}; + +export type LogsByPid = { [pid: string]: string[]; }; -export const getLogs = (hostname: string, pid: number | null) => - get("/api/logs", { - hostname, - pid: pid === null ? "" : pid, +export const getLogs = (nodeIp: string, pid: number | null) => + get("/node_logs", { + ip: nodeIp, + pid: pid ?? "", }); export type LaunchProfilingResponse = string; @@ -302,34 +330,34 @@ export const launchKillActor = ( actorIpAddress: string, actorPort: number, ) => - get("/api/kill_actor", { + get<{}>("/logical/kill_actor", { // make sure object is okay - actor_id: actorId, - ip_address: actorIpAddress, + actorId: actorId, + ipAddress: actorIpAddress, port: actorPort, }); export type TuneTrial = { date: string; - episodes_total: string; - experiment_id: string; - experiment_tag: string; + episodesTotal: string; + experimentId: string; + experimentTag: string; hostname: string; - iterations_since_restore: number; + iterationsSinceRestore: number; logdir: string; - node_ip: string; + nodeIp: string; pid: number; - time_since_restore: number; - time_this_iter_s: number; - time_total_s: number; + timeSinceRestore: number; + timeThisIterS: number; + timeTotalS: number; timestamp: number; - timesteps_since_restore: number; - timesteps_total: number; - training_iteration: number; - start_time: string; + timestepsSinceRestore: number; + timestepsTotal: number; + trainingIteration: number; + startTime: string; status: string; - trial_id: string | number; - job_id: string; + trialId: string | number; + jobId: string; params: { [key: string]: string | number }; metrics: { [key: string]: string | number }; error: string; @@ -337,59 +365,72 @@ export type TuneTrial = { export type TuneError = { text: string; - job_id: string; - trial_id: string; + jobId: string; + trialId: string; }; export type TuneJobResponse = { - trial_records: { [key: string]: TuneTrial }; + result: TuneJob; +}; + +export type TuneJob = { + trialRecords: { [key: string]: TuneTrial }; errors: { [key: string]: TuneError }; tensorboard: { - tensorboard_current: boolean; - tensorboard_enabled: boolean; + tensorboardCurrent: boolean; + tensorboardEnabled: boolean; }; }; -export const getTuneInfo = () => get("/api/tune_info", {}); +export const getTuneInfo = () => get("/tune/info", {}); + +export type TuneAvailability = { + available: boolean; + trialsAvailable: boolean; +}; export type TuneAvailabilityResponse = { - available: boolean; - trials_available: boolean; + result: TuneAvailability; }; export const getTuneAvailability = () => - get("/api/tune_availability", {}); + get("/tune/availability", {}); -export type TuneSetExperimentReponse = { +export type TuneSetExperimentResponse = { experiment: string; }; export const setTuneExperiment = (experiment: string) => - post("/api/set_tune_experiment", { + get("/tune/set_experiment", { experiment: experiment, }); export const enableTuneTensorBoard = () => - post<{}>("/api/enable_tune_tensorboard", {}); + get<{}>("/tune/enable_tensorboard", {}); export type MemoryTableSummary = { - total_actor_handles: number; - total_captured_in_objects: number; - total_local_ref_count: number; + totalActorHandles: number; + totalCapturedInObjects: number; + totalLocalRefCount: number; // The measurement is B. - total_object_size: number; - total_pinned_in_memory: number; - total_used_by_pending_task: number; + totalObjectSize: number; + totalPinnedInMemory: number; + totalUsedByPendingTask: number; }; export type MemoryTableEntry = { - node_ip_address: string; + nodeIpAddress: string; pid: number; type: string; - object_ref: string; - object_size: number; - reference_type: string; - call_site: string; + objectRef: string; + objectSize: number; + referenceType: string; + callSite: string; +}; + +export type MemoryTable = { + group: MemoryTableGroups; + summary: MemoryTableSummary; }; export type MemoryTableGroups = { @@ -402,8 +443,7 @@ export type MemoryTableGroup = { }; export type MemoryTableResponse = { - group: MemoryTableGroups; - summary: MemoryTableSummary; + memoryTable: MemoryTable; }; // This doesn't return anything. @@ -412,10 +452,10 @@ export type StopMemoryTableResponse = {}; export type MemoryGroupByKey = "node" | "stack_trace" | ""; export const getMemoryTable = async (groupByKey: MemoryGroupByKey) => { - return get("/api/memory_table", { - group_by: groupByKey, + return get("/memory/memory_table", { + groupBy: groupByKey, }); }; -export const stopMemoryTableCollection = () => - get("/api/stop_memory_table", {}); +export const setMemoryTableCollection = (value: boolean) => + get<{}>("/memory/set_fetch", { shouldFetch: value }); diff --git a/dashboard/client/src/pages/dashboard/Dashboard.tsx b/dashboard/client/src/pages/dashboard/Dashboard.tsx index 44eda33e2..0ffbce7f5 100644 --- a/dashboard/client/src/pages/dashboard/Dashboard.tsx +++ b/dashboard/client/src/pages/dashboard/Dashboard.tsx @@ -1,15 +1,14 @@ import { createStyles, + makeStyles, Tab, Tabs, Theme, Typography, - WithStyles, - withStyles, } from "@material-ui/core"; -import React from "react"; -import { connect } from "react-redux"; -import { getNodeInfo, getRayletInfo, getTuneAvailability } from "../../api"; +import React, { useCallback, useEffect, useRef } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { getActorGroups, getNodeInfo, getTuneAvailability } from "../../api"; import { StoreState } from "../../store"; import LastUpdated from "./LastUpdated"; import LogicalView from "./logical-view/LogicalView"; @@ -19,7 +18,14 @@ import RayConfig from "./ray-config/RayConfig"; import { dashboardActions } from "./state"; import Tune from "./tune/Tune"; -const styles = (theme: Theme) => +const { + setNodeInfo, + setTuneAvailability, + setActorGroups, + setError, + setTab, +} = dashboardActions; +const useDashboardStyles = makeStyles((theme: Theme) => createStyles({ root: { backgroundColor: theme.palette.background.paper, @@ -33,89 +39,85 @@ const styles = (theme: Theme) => borderBottomStyle: "solid", borderBottomWidth: 1, }, - }); + }), +); -const mapStateToProps = (state: StoreState) => ({ - tab: state.dashboard.tab, - tuneAvailability: state.dashboard.tuneAvailability, -}); +const tabSelector = (state: StoreState) => state.dashboard.tab; +const tuneAvailabilitySelector = (state: StoreState) => + state.dashboard.tuneAvailability; -const mapDispatchToProps = dashboardActions; +const allTabs = [ + { label: "Machine view", component: NodeInfo }, + { label: "Logical view", component: LogicalView }, + { label: "Memory", component: MemoryInfo }, + { label: "Ray config", component: RayConfig }, + { label: "Tune", component: Tune }, +]; -class Dashboard extends React.Component< - WithStyles & - ReturnType & - typeof mapDispatchToProps -> { - timeoutId = 0; - tabs = [ - { label: "Machine view", component: NodeInfo }, - { label: "Logical view", component: LogicalView }, - { label: "Memory", component: MemoryInfo }, - { label: "Ray config", component: RayConfig }, - { label: "Tune", component: Tune }, - ]; +const Dashboard: React.FC = () => { + const dispatch = useDispatch(); + const tuneAvailability = useSelector(tuneAvailabilitySelector); + const tab = useSelector(tabSelector); + const classes = useDashboardStyles(); - refreshInfo = async () => { + // Polling Function + const refreshInfo = useCallback(async () => { try { - const [nodeInfo, rayletInfo, tuneAvailability] = await Promise.all([ + const [nodeInfo, tuneAvailability, actorGroups] = await Promise.all([ getNodeInfo(), - getRayletInfo(), getTuneAvailability(), + getActorGroups(), ]); - this.props.setNodeAndRayletInfo({ nodeInfo, rayletInfo }); - this.props.setTuneAvailability(tuneAvailability); - this.props.setError(null); + dispatch(setNodeInfo({ nodeInfo })); + dispatch(setTuneAvailability(tuneAvailability)); + dispatch(setActorGroups(actorGroups)); + dispatch(setError(null)); } catch (error) { - this.props.setError(error.toString()); - } finally { - this.timeoutId = window.setTimeout(this.refreshInfo, 1000); + dispatch(setError(error.toString())); } - }; + }, [dispatch]); - async componentDidMount() { - await this.refreshInfo(); - } - - componentWillUnmount() { - clearTimeout(this.timeoutId); - } - - handleTabChange = async (event: React.ChangeEvent<{}>, value: number) => - this.props.setTab(value); - - render() { - const { classes, tab, tuneAvailability } = this.props; - const tabs = this.tabs.slice(); - - // if Tune information is not available, remove Tune tab from the dashboard - if (tuneAvailability === null || !tuneAvailability.available) { - tabs.splice(4); + // Run the poller + const intervalId = useRef(null); + useEffect(() => { + if (intervalId.current === null) { + refreshInfo(); + intervalId.current = setInterval(refreshInfo, 1000); } + const cleanup = () => { + clearInterval(intervalId.current); + }; + return cleanup; + }, [refreshInfo]); - const SelectedComponent = tabs[tab].component; - return ( -
- Ray Dashboard - - {tabs.map(({ label }) => ( - - ))} - - - -
- ); + const handleTabChange = (_: any, value: number) => dispatch(setTab(value)); + + const tabs = allTabs.slice(); + + // if Tune information is not available, remove Tune tab from the dashboard + if (tuneAvailability === null || !tuneAvailability.available) { + tabs.splice(4); } -} -export default connect( - mapStateToProps, - mapDispatchToProps, -)(withStyles(styles)(Dashboard)); + const SelectedComponent = tabs[tab].component; + return ( +
+ Ray Dashboard + + {tabs.map(({ label }) => ( + + ))} + + + +
+ ); +}; + +export default Dashboard; diff --git a/dashboard/client/src/pages/dashboard/logical-view/Actor.tsx b/dashboard/client/src/pages/dashboard/logical-view/Actor.tsx index c715db1a8..d59426418 100644 --- a/dashboard/client/src/pages/dashboard/logical-view/Actor.tsx +++ b/dashboard/client/src/pages/dashboard/logical-view/Actor.tsx @@ -108,6 +108,7 @@ const Actor: React.FC = ({ actor }) => { { label: "Resources", value: + actor.usedResources && Object.entries(actor.usedResources).length > 0 && Object.entries(actor.usedResources) .sort((a, b) => a[0].localeCompare(b[0])) @@ -121,20 +122,20 @@ const Actor: React.FC = ({ actor }) => { }, { label: "Number of pending tasks", - value: actor.taskQueueLength.toLocaleString(), + value: actor.taskQueueLength?.toLocaleString() ?? "0", 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(), + value: actor.numExecutedTasks?.toLocaleString() ?? "0", tooltip: "The number of tasks this actor has executed throughout its lifetimes.", }, { label: "Number of ObjectRefs in scope", - value: actor.numObjectRefsInScope.toLocaleString(), + value: actor.numObjectRefsInScope?.toLocaleString() ?? "0", 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 " + @@ -143,14 +144,14 @@ const Actor: React.FC = ({ actor }) => { }, { label: "Number of local objects", - value: actor.numLocalObjects.toLocaleString(), + value: actor.numLocalObjects?.toLocaleString() ?? "0", 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(), + value: actor.usedObjectStoreMemory?.toLocaleString() ?? "0", 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 " + @@ -263,18 +264,18 @@ const Actor: React.FC = ({ actor }) => { ) : actor.state === ActorState.Infeasible ? ( - {actor.actorTitle} cannot be created because the Ray cluster cannot + {actor.actorClass} cannot be created because the Ray cluster cannot satisfy its resource requirements. ) : ( - {actor.actorTitle} is pending until resources are available. + {actor.actorClass} is pending until resources are available. )} {isFullActorInfo(actor) && ( diff --git a/dashboard/client/src/pages/dashboard/logical-view/ActorDetailsPane.tsx b/dashboard/client/src/pages/dashboard/logical-view/ActorDetailsPane.tsx index 0cccd7b4b..8593023f8 100644 --- a/dashboard/client/src/pages/dashboard/logical-view/ActorDetailsPane.tsx +++ b/dashboard/client/src/pages/dashboard/logical-view/ActorDetailsPane.tsx @@ -5,7 +5,7 @@ import LabeledDatum from "../../../common/LabeledDatum"; import ActorStateRepr from "./ActorStateRepr"; type ActorDetailsPaneProps = { - actorTitle: string; + actorClass: string; actorState: ActorState; actorDetails: { label: string; @@ -31,15 +31,15 @@ const useStyles = makeStyles((theme: Theme) => ({ })); const ActorDetailsPane: React.FC = ({ - actorTitle, actorDetails, + actorClass, actorState, }) => { const classes = useStyles(); return (
-
{actorTitle}
+
{actorClass}
diff --git a/dashboard/client/src/pages/dashboard/logical-view/LogicalView.tsx b/dashboard/client/src/pages/dashboard/logical-view/LogicalView.tsx index abab2ba47..0726e5e51 100644 --- a/dashboard/client/src/pages/dashboard/logical-view/LogicalView.tsx +++ b/dashboard/client/src/pages/dashboard/logical-view/LogicalView.tsx @@ -31,25 +31,32 @@ const actorClassMatchesSearch = ( return actorClass.toLowerCase().search(loweredNameFilter) !== -1; }; -const rayletInfoSelector = (state: StoreState) => state.dashboard.rayletInfo; +const actorGroupsSelector = (state: StoreState) => state.dashboard.actorGroups; const LogicalView: React.FC = () => { const [nameFilter, setNameFilter] = useState(""); const [debouncedNameFilter] = useDebounce(nameFilter, 500); const classes = useLogicalViewStyles(); - const rayletInfo = useSelector(rayletInfoSelector); - if (rayletInfo === null || !rayletInfo.actorGroups) { + const actorGroups = useSelector(actorGroupsSelector); + if (!actorGroups) { return Loading...; } - const actorGroups = + if (Object.keys(actorGroups).length === 0) { + return ( + + Finished loading, but have found no actors yet. + + ); + } + const filteredGroups = debouncedNameFilter === "" - ? Object.entries(rayletInfo.actorGroups) - : Object.entries(rayletInfo.actorGroups).filter(([key, _]) => + ? Object.entries(actorGroups) + : Object.entries(actorGroups).filter(([key, _]) => actorClassMatchesSearch(key, debouncedNameFilter), ); return ( - {actorGroups.length === 0 ? ( + {filteredGroups.length === 0 ? ( No actors found. ) : ( @@ -65,7 +72,7 @@ const LogicalView: React.FC = () => { Search for an actor by name - + )} diff --git a/dashboard/client/src/pages/dashboard/memory/Memory.tsx b/dashboard/client/src/pages/dashboard/memory/Memory.tsx index 7b97d4034..794251ea7 100644 --- a/dashboard/client/src/pages/dashboard/memory/Memory.tsx +++ b/dashboard/client/src/pages/dashboard/memory/Memory.tsx @@ -19,7 +19,7 @@ import { getMemoryTable, MemoryGroupByKey, MemoryTableResponse, - stopMemoryTableCollection, + setMemoryTableCollection, } from "../../../api"; import { StoreState } from "../../../store"; import { dashboardActions } from "../state"; @@ -68,11 +68,7 @@ const useMemoryInfoStyles = makeStyles((theme: Theme) => }), ); -const memoryInfoSelector = (state: StoreState) => ({ - tab: state.dashboard.tab, - memoryTable: state.dashboard.memoryTable, - shouldObtainMemoryTable: state.dashboard.shouldObtainMemoryTable, -}); +const memoryTableSelector = (state: StoreState) => state.dashboard.memoryTable; const fetchMemoryTable = ( groupByKey: MemoryGroupByKey, @@ -85,7 +81,7 @@ const fetchMemoryTable = ( }; const MemoryInfo: React.FC<{}> = () => { - const { memoryTable } = useSelector(memoryInfoSelector); + const memoryTable = useSelector(memoryTableSelector); const dispatch = useDispatch(); const [paused, setPaused] = useState(false); @@ -94,6 +90,13 @@ const MemoryInfo: React.FC<{}> = () => { const classes = useMemoryInfoStyles(); const [groupBy, setGroupBy] = useState("node"); + // Turn memory collection on render + useEffect(() => { + setMemoryTableCollection(true); + return () => { + setMemoryTableCollection(false); + }; + }, []); // Set up polling memory data const fetchData = useCallback( fetchMemoryTable(groupBy, (resp) => @@ -118,8 +121,13 @@ const MemoryInfo: React.FC<{}> = () => { if (!memoryTable) { return ( - - Loading memory information + Loading memory information + ); + } + if (Object.keys(memoryTable.group).length === 0) { + return ( + + Finished loading, but have found no memory data yet. ); } @@ -162,9 +170,7 @@ const MemoryInfo: React.FC<{}> = () => { color="primary" className={classes.pauseButton} onClick={() => { - if (!paused) { - stopMemoryTableCollection(); - } + setMemoryTableCollection(!paused); setPaused(!paused); }} > diff --git a/dashboard/client/src/pages/dashboard/memory/MemorySummary.tsx b/dashboard/client/src/pages/dashboard/memory/MemorySummary.tsx index 1e1a3224e..e15965a48 100644 --- a/dashboard/client/src/pages/dashboard/memory/MemorySummary.tsx +++ b/dashboard/client/src/pages/dashboard/memory/MemorySummary.tsx @@ -28,24 +28,21 @@ const MemorySummary: React.FC = ({ }) => { const classes = useMemorySummaryStyles(); const memoryData = [ - [ - "Total Local Reference Count", - `${memoryTableSummary.total_local_ref_count}`, - ], - ["Pinned in Memory Count", `${memoryTableSummary.total_pinned_in_memory}`], + ["Total Local Reference Count", `${memoryTableSummary.totalLocalRefCount}`], + ["Pinned in Memory Count", `${memoryTableSummary.totalPinnedInMemory}`], [ "Total Used by Pending Tasks Count", - `${memoryTableSummary.total_used_by_pending_task}`, + `${memoryTableSummary.totalUsedByPendingTask}`, ], [ "Total Captured in Objects Count", - `${memoryTableSummary.total_captured_in_objects}`, + `${memoryTableSummary.totalCapturedInObjects}`, ], [ "Total Memory Used by Objects", - `${formatByteAmount(memoryTableSummary.total_object_size, "mebibyte")}`, + `${formatByteAmount(memoryTableSummary.totalObjectSize, "mebibyte")}`, ], - ["Total Actor Handle Count", `${memoryTableSummary.total_actor_handles}`], + ["Total Actor Handle Count", `${memoryTableSummary.totalActorHandles}`], ]; return ( diff --git a/dashboard/client/src/pages/dashboard/memory/MemoryTable.tsx b/dashboard/client/src/pages/dashboard/memory/MemoryTable.tsx index 0b27bb80f..979061e23 100644 --- a/dashboard/client/src/pages/dashboard/memory/MemoryTable.tsx +++ b/dashboard/client/src/pages/dashboard/memory/MemoryTable.tsx @@ -29,37 +29,37 @@ const useMemoryTableStyles = makeStyles((theme: Theme) => ); type memoryColumnId = - | "node_ip_address" + | "nodeIpAddress" | "pid" | "type" - | "object_ref" - | "object_size" - | "reference_type" - | "call_site"; + | "objectRef" + | "objectSize" + | "referenceType" + | "callSite"; const memoryHeaderInfo: HeaderInfo[] = [ { - id: "node_ip_address", + id: "nodeIpAddress", label: "IP Address", numeric: false, sortable: true, }, { id: "pid", label: "PID", numeric: false, sortable: true }, { id: "type", label: "Type", numeric: false, sortable: true }, - { id: "object_ref", label: "Object Ref", numeric: false, sortable: true }, + { id: "objectRef", label: "Object Ref", numeric: false, sortable: true }, { - id: "object_size", + id: "objectSize", label: "Object Size", numeric: false, sortable: true, }, { - id: "reference_type", + id: "referenceType", label: "Reference Type", numeric: false, sortable: true, }, - { id: "call_site", label: "Call Site", numeric: false, sortable: true }, + { id: "callSite", label: "Call Site", numeric: false, sortable: true }, ]; type MemoryTableProps = { @@ -76,7 +76,7 @@ const MemoryTable: React.FC = ({ tableEntries }) => { ? stableSort(tableEntries, comparator) : tableEntries; const tableRows = sortedTableEntries.map((tableEntry) => ( - + )); // Todo(max) add in sorting code return ( diff --git a/dashboard/client/src/pages/dashboard/memory/MemoryTableRow.tsx b/dashboard/client/src/pages/dashboard/memory/MemoryTableRow.tsx index 2c0f5d60b..856f062dd 100644 --- a/dashboard/client/src/pages/dashboard/memory/MemoryTableRow.tsx +++ b/dashboard/client/src/pages/dashboard/memory/MemoryTableRow.tsx @@ -11,17 +11,17 @@ type Props = { export const MemoryTableRow = (props: Props) => { const { memoryTableEntry } = props; const object_size = - memoryTableEntry.object_size === -1 + memoryTableEntry.objectSize === -1 ? "?" - : formatByteAmount(memoryTableEntry.object_size, "mebibyte"); + : formatByteAmount(memoryTableEntry.objectSize, "mebibyte"); const memoryTableEntryValues = [ - memoryTableEntry.node_ip_address, + memoryTableEntry.nodeIpAddress, memoryTableEntry.pid, memoryTableEntry.type, - memoryTableEntry.object_ref, + memoryTableEntry.objectRef, object_size, - memoryTableEntry.reference_type, - memoryTableEntry.call_site, + memoryTableEntry.referenceType, + memoryTableEntry.callSite, ]; return ( diff --git a/dashboard/client/src/pages/dashboard/node-info/NodeInfo.tsx b/dashboard/client/src/pages/dashboard/node-info/NodeInfo.tsx index 06000ca48..0c2246718 100644 --- a/dashboard/client/src/pages/dashboard/node-info/NodeInfo.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/NodeInfo.tsx @@ -10,7 +10,6 @@ import { } from "@material-ui/core"; import React, { useState } from "react"; import { useSelector } from "react-redux"; -import { RayletInfoResponse } from "../../../api"; import SortableTableHead, { HeaderInfo, } from "../../../common/SortableTableHead"; @@ -68,24 +67,17 @@ const makeGroupedTableContents = ( nodes: Node[], sortWorkerComparator: any, sortGroupComparator: any, - rayletInfo: RayletInfoResponse | null, nodeInfoFeatures: NodeInfoFeature[], ) => { const sortedGroups = sortGroupComparator ? stableSort(nodes, sortGroupComparator) : nodes; return sortedGroups.map((node) => { - const plasmaStats = rayletInfo?.plasmaStats?.[node.ip]; const workerFeatureData: WorkerFeatureData[] = node.workers.map( (worker) => { - const rayletWorker = - rayletInfo?.nodes?.[node.ip]?.workersStats?.find( - (workerStats) => workerStats.pid === worker.pid, - ) || null; return { node, worker, - rayletWorker, }; }, ); @@ -100,7 +92,6 @@ const makeGroupedTableContents = ( node={node} workerFeatureData={sortedClusterWorkers} features={nodeInfoFeatures} - plasmaStats={plasmaStats} initialExpanded={nodes.length <= 1} /> ); @@ -110,7 +101,6 @@ const makeGroupedTableContents = ( const makeUngroupedTableContents = ( nodes: Node[], sortWorkerComparator: any, - rayletInfo: RayletInfoResponse | null, nodeInfoFeatures: NodeInfoFeature[], ) => { const workerInfoFeatures = nodeInfoFeatures.map( @@ -118,14 +108,9 @@ const makeUngroupedTableContents = ( ); const allWorkerFeatures: WorkerFeatureData[] = nodes.flatMap((node) => { return node.workers.map((worker) => { - const rayletWorker = - rayletInfo?.nodes?.[node.ip]?.workersStats?.find( - (workerStats) => workerStats.pid === worker.pid, - ) || null; return { - node: node, + node, worker, - rayletWorker, }; }); }); @@ -154,13 +139,10 @@ const useNodeInfoStyles = makeStyles((theme: Theme) => }), ); -const nodeInfoSelector = (state: StoreState) => ({ - nodeInfo: state.dashboard.nodeInfo, - rayletInfo: state.dashboard.rayletInfo, -}); +const nodesSelector = (state: StoreState) => state.dashboard?.nodeInfo?.clients; type DialogState = { - hostname: string; + nodeIp: string; pid: number | null; } | null; @@ -188,13 +170,11 @@ const NodeInfo: React.FC<{}> = () => { const toggleOrder = () => setOrder(order === "asc" ? "desc" : "asc"); const [orderBy, setOrderBy] = React.useState(null); const classes = useNodeInfoStyles(); - const { nodeInfo, rayletInfo } = useSelector(nodeInfoSelector); - if (nodeInfo === null || rayletInfo === null) { + const nodes = useSelector(nodesSelector); + if (!nodes) { return Loading...; } - const clusterTotalWorkers = sum( - nodeInfo.clients.map((c) => c.workers.length), - ); + const clusterTotalWorkers = sum(nodes.map((n) => n.workers.length)); const nodeInfoFeatures: NodeInfoFeature[] = [ hostFeature, workersFeature, @@ -207,8 +187,8 @@ const NodeInfo: React.FC<{}> = () => { diskFeature, sentFeature, receivedFeature, - makeLogsFeature((hostname, pid) => setLogDialog({ hostname, pid })), - makeErrorsFeature((hostname, pid) => setErrorDialog({ hostname, pid })), + makeLogsFeature((nodeIp, pid) => setLogDialog({ nodeIp, pid })), + makeErrorsFeature((nodeIp, pid) => setErrorDialog({ nodeIp, pid })), ]; const sortNodeAccessor = nodeInfoFeatures.find( (feature) => feature.id === orderBy, @@ -222,18 +202,12 @@ const NodeInfo: React.FC<{}> = () => { sortWorkerAccessor && getFnComparator(order, sortWorkerAccessor); const tableContents = isGrouped ? makeGroupedTableContents( - nodeInfo.clients, + nodes, sortWorkerComparator, sortNodeComparator, - rayletInfo, nodeInfoFeatures, ) - : makeUngroupedTableContents( - nodeInfo.clients, - sortWorkerComparator, - rayletInfo, - nodeInfoFeatures, - ); + : makeUngroupedTableContents(nodes, sortWorkerComparator, nodeInfoFeatures); return ( = () => { {tableContents} feature.ClusterFeatureRenderFn, )} @@ -276,14 +249,14 @@ const NodeInfo: React.FC<{}> = () => { {logDialog !== null && ( setLogDialog(null)} - hostname={logDialog.hostname} + nodeIp={logDialog.nodeIp} pid={logDialog.pid} /> )} {errorDialog !== null && ( setErrorDialog(null)} - hostname={errorDialog.hostname} + nodeIp={errorDialog.nodeIp} pid={errorDialog.pid} /> )} diff --git a/dashboard/client/src/pages/dashboard/node-info/NodeRowGroup.tsx b/dashboard/client/src/pages/dashboard/node-info/NodeRowGroup.tsx index b324c4b1e..57b556cad 100644 --- a/dashboard/client/src/pages/dashboard/node-info/NodeRowGroup.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/NodeRowGroup.tsx @@ -9,7 +9,7 @@ import AddIcon from "@material-ui/icons/Add"; import RemoveIcon from "@material-ui/icons/Remove"; import classNames from "classnames"; import React, { useState } from "react"; -import { NodeInfoResponse, PlasmaStats } from "../../../api"; +import { NodeInfoResponse } from "../../../api"; import { StyledTableCell } from "../../../common/TableCell"; import { NodeInfoFeature, WorkerFeatureData } from "./features/types"; import { NodeWorkerRow } from "./NodeWorkerRow"; @@ -45,7 +45,6 @@ type NodeRowGroupProps = { features: NodeInfoFeature[]; node: Node; rayletInfo?: string; - plasmaStats?: PlasmaStats; workerFeatureData: WorkerFeatureData[]; initialExpanded: boolean; }; @@ -56,7 +55,6 @@ const NodeRowGroup: React.FC = ({ initialExpanded, rayletInfo, workerFeatureData, - plasmaStats, }) => { const [expanded, setExpanded] = useState(initialExpanded); const toggleExpand = () => setExpanded(!expanded); @@ -65,7 +63,7 @@ const NodeRowGroup: React.FC = ({ const FeatureComponent = nodeInfoFeature.NodeFeatureRenderFn; return ( - + ); }); diff --git a/dashboard/client/src/pages/dashboard/node-info/NodeWorkerRow.tsx b/dashboard/client/src/pages/dashboard/node-info/NodeWorkerRow.tsx index c87b943a0..f9847721f 100644 --- a/dashboard/client/src/pages/dashboard/node-info/NodeWorkerRow.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/NodeWorkerRow.tsx @@ -12,17 +12,13 @@ export const NodeWorkerRow: React.FC = ({ features, data, }) => { - const { node, worker, rayletWorker } = data; + const { node, worker } = data; return ( {features.map((WorkerFeature, index) => ( - + ))} diff --git a/dashboard/client/src/pages/dashboard/node-info/TotalRow.tsx b/dashboard/client/src/pages/dashboard/node-info/TotalRow.tsx index 367f9d031..882a65b65 100644 --- a/dashboard/client/src/pages/dashboard/node-info/TotalRow.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/TotalRow.tsx @@ -7,7 +7,7 @@ import { } from "@material-ui/core"; import LayersIcon from "@material-ui/icons/Layers"; import React from "react"; -import { NodeInfoResponse, PlasmaStats } from "../../../api"; +import { NodeInfoResponse } from "../../../api"; import { StyledTableCell } from "../../../common/TableCell"; import { ClusterFeatureRenderFn } from "./features/types"; @@ -33,16 +33,11 @@ const useTotalRowStyles = makeStyles((theme: Theme) => type TotalRowProps = { nodes: NodeInfoResponse["clients"]; - plasmaStats: PlasmaStats[]; clusterTotalWorkers: number; features: (ClusterFeatureRenderFn | undefined)[]; }; -const TotalRow: React.FC = ({ - nodes, - features, - plasmaStats, -}) => { +const TotalRow: React.FC = ({ nodes, features }) => { const classes = useTotalRowStyles(); return ( @@ -52,7 +47,7 @@ const TotalRow: React.FC = ({ {features.map((ClusterFeature, index) => ClusterFeature ? ( - + ) : ( diff --git a/dashboard/client/src/pages/dashboard/node-info/dialogs/errors/Errors.tsx b/dashboard/client/src/pages/dashboard/node-info/dialogs/errors/Errors.tsx index 1dd7b3705..307d80598 100644 --- a/dashboard/client/src/pages/dashboard/node-info/dialogs/errors/Errors.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/dialogs/errors/Errors.tsx @@ -7,7 +7,7 @@ import { WithStyles, } from "@material-ui/core"; import React from "react"; -import { ErrorsResponse, getErrors } from "../../../../../api"; +import { ErrorsByPid, getErrors } from "../../../../../api"; import DialogWithTitle from "../../../../../common/DialogWithTitle"; import NumberedLines from "../../../../../common/NumberedLines"; @@ -34,12 +34,12 @@ const styles = (theme: Theme) => type Props = { clearErrorDialog: () => void; - hostname: string; + nodeIp: string; pid: number | null; }; type State = { - result: ErrorsResponse | null; + result: ErrorsByPid | null; error: string | null; }; @@ -51,16 +51,16 @@ class Errors extends React.Component, State> { async componentDidMount() { try { - const { hostname, pid } = this.props; - const result = await getErrors(hostname, pid); - this.setState({ result, error: null }); + const { nodeIp, pid } = this.props; + const result = await getErrors(nodeIp, pid); + this.setState({ result: result.errors, error: null }); } catch (error) { this.setState({ result: null, error: error.toString() }); } } render() { - const { classes, clearErrorDialog, hostname } = this.props; + const { classes, clearErrorDialog, nodeIp } = this.props; const { result, error } = this.state; return ( @@ -73,7 +73,7 @@ class Errors extends React.Component, State> { Object.entries(result).map(([pid, errors]) => ( - {hostname} (PID: {pid}) + {nodeIp} (PID: {pid}) {errors.length > 0 ? ( errors.map(({ message, timestamp }, index) => ( diff --git a/dashboard/client/src/pages/dashboard/node-info/dialogs/logs/Logs.tsx b/dashboard/client/src/pages/dashboard/node-info/dialogs/logs/Logs.tsx index 7d04749c8..4651be36d 100644 --- a/dashboard/client/src/pages/dashboard/node-info/dialogs/logs/Logs.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/dialogs/logs/Logs.tsx @@ -7,7 +7,7 @@ import { withStyles, } from "@material-ui/core"; import React from "react"; -import { getLogs, LogsResponse } from "../../../../../api"; +import { getLogs, LogsByPid } from "../../../../../api"; import DialogWithTitle from "../../../../../common/DialogWithTitle"; import NumberedLines from "../../../../../common/NumberedLines"; @@ -29,12 +29,12 @@ const styles = (theme: Theme) => type Props = { clearLogDialog: () => void; - hostname: string; + nodeIp: string; pid: number | null; }; type State = { - result: LogsResponse | null; + result: LogsByPid | null; error: string | null; }; @@ -46,16 +46,16 @@ class Logs extends React.Component, State> { async componentDidMount() { try { - const { hostname, pid } = this.props; - const result = await getLogs(hostname, pid); - this.setState({ result, error: null }); + const { nodeIp, pid } = this.props; + const result = await getLogs(nodeIp, pid); + this.setState({ result: result.logs, error: null }); } catch (error) { this.setState({ result: null, error: error.toString() }); } } render() { - const { classes, clearLogDialog, hostname } = this.props; + const { classes, clearLogDialog, nodeIp } = this.props; const { result, error } = this.state; return ( @@ -68,7 +68,7 @@ class Logs extends React.Component, State> { Object.entries(result).map(([pid, lines]) => ( - {hostname} (PID: {pid}) + {nodeIp} (PID: {pid}) {lines.length > 0 ? (
diff --git a/dashboard/client/src/pages/dashboard/node-info/features/CPU.tsx b/dashboard/client/src/pages/dashboard/node-info/features/CPU.tsx index b76a1ca7c..d6a7143a4 100644 --- a/dashboard/client/src/pages/dashboard/node-info/features/CPU.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/features/CPU.tsx @@ -37,14 +37,14 @@ export const nodeCPUAccessor: Accessor = ({ node }) => { export const WorkerCPU: WorkerFeatureRenderFn = ({ worker }) => (
); export const workerCPUAccessor: Accessor = ({ worker }) => { - return worker.cpu_percent; + return worker.cpuPercent; }; const cpuFeature: NodeInfoFeature = { diff --git a/dashboard/client/src/pages/dashboard/node-info/features/Errors.tsx b/dashboard/client/src/pages/dashboard/node-info/features/Errors.tsx index e711e2563..cdeaa5ca4 100644 --- a/dashboard/client/src/pages/dashboard/node-info/features/Errors.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/features/Errors.tsx @@ -5,7 +5,6 @@ import { Accessor } from "../../../../common/tableUtils"; import { sum } from "../../../../common/util"; import { ClusterFeatureRenderFn, - Node, NodeFeatureData, NodeFeatureRenderFn, NodeInfoFeature, @@ -13,11 +12,8 @@ import { WorkerFeatureRenderFn, } from "./types"; -const nodeErrCount = (node: Node) => - node.error_count ? sum(Object.values(node.error_count)) : 0; - const ClusterErrors: ClusterFeatureRenderFn = ({ nodes }) => { - const totalErrCount = sum(nodes.map(nodeErrCount)); + const totalErrCount = sum(nodes.map((node) => node.errorCount)); return totalErrCount === 0 ? ( No errors @@ -31,30 +27,28 @@ const ClusterErrors: ClusterFeatureRenderFn = ({ nodes }) => { }; const makeNodeErrors = ( - setErrorDialog: (hostname: string, pid: number | null) => void, + setErrorDialog: (nodeIp: string, pid: number | null) => void, ): NodeFeatureRenderFn => ({ node }) => { - const nodeErrorCount = nodeErrCount(node); - return nodeErrorCount === 0 ? ( + return node.errorCount === 0 ? ( No errors ) : ( - setErrorDialog(node.hostname, null)}> - View all errors ({nodeErrorCount.toLocaleString()}) + setErrorDialog(node.ip, null)}> + View all errors ({node.errorCount.toLocaleString()}) ); }; const nodeErrorsAccessor: Accessor = ({ node }) => - nodeErrCount(node); + node.errorCount; const makeWorkerErrors = ( - setErrorDialog: (hostname: string, pid: number | null) => void, + setErrorDialog: (nodeIp: string, pid: number | null) => void, ): WorkerFeatureRenderFn => ({ node, worker }) => { - const workerErrorCount = node.error_count?.[worker.pid] || 0; - return workerErrorCount !== 0 ? ( - setErrorDialog(node.hostname, worker.pid)}> - View errors ({workerErrorCount.toLocaleString()}) + return worker.errorCount !== 0 ? ( + setErrorDialog(node.ip, worker.pid)}> + View errors ({worker.errorCount.toLocaleString()}) ) : ( @@ -63,11 +57,11 @@ const makeWorkerErrors = ( ); }; -const workerErrorsAccessor: Accessor = ({ node, worker }) => - node.error_count?.[worker.pid] || 0; +const workerErrorsAccessor: Accessor = ({ worker }) => + worker.errorCount; const makeErrorsFeature = ( - setErrorDialog: (hostname: string, pid: number | null) => void, + setErrorDialog: (nodeIp: string, pid: number | null) => void, ): NodeInfoFeature => ({ id: "errors", ClusterFeatureRenderFn: ClusterErrors, diff --git a/dashboard/client/src/pages/dashboard/node-info/features/GPU.tsx b/dashboard/client/src/pages/dashboard/node-info/features/GPU.tsx index 6a6615c45..f00b43149 100644 --- a/dashboard/client/src/pages/dashboard/node-info/features/GPU.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/features/GPU.tsx @@ -1,6 +1,6 @@ import { Box, Tooltip, Typography } from "@material-ui/core"; import React from "react"; -import { GPUStats, RayletWorkerStats, ResourceSlot } from "../../../../api"; +import { GPUStats, ResourceSlot, Worker } from "../../../../api"; import { RightPaddedTypography } from "../../../../common/CustomTypography"; import { Accessor } from "../../../../common/tableUtils"; @@ -35,7 +35,7 @@ const nodeGPUUtilization = (node: Node): number => { if (!node.gpus || node.gpus.length === 0) { return NaN; } - const utilizationSum = sum(node.gpus.map((gpu) => gpu.utilization_gpu)); + const utilizationSum = sum(node.gpus.map((gpu) => gpu.utilizationGpu)); const avgUtilization = utilizationSum / node.gpus.length; return avgUtilization; }; @@ -88,8 +88,8 @@ const NodeGPUEntry: React.FC = ({ gpu, slot }) => { [{slot}]: ); @@ -119,8 +119,8 @@ const WorkerGPUEntry: React.FC = ({ resourceSlot }) => { ); }; -const WorkerGPU: WorkerFeatureRenderFn = ({ rayletWorker }) => { - const workerRes = rayletWorker?.coreWorkerStats.usedResources; +const WorkerGPU: WorkerFeatureRenderFn = ({ worker }) => { + const workerRes = worker.coreWorkerStats[0].usedResources; const workerUsedGPUResources = workerRes?.["GPU"]; let message; if (workerUsedGPUResources === undefined) { @@ -147,8 +147,8 @@ const WorkerGPU: WorkerFeatureRenderFn = ({ rayletWorker }) => { return
{message}
; }; -const workerGPUUtilization = (rayletWorker: RayletWorkerStats | null) => { - const workerRes = rayletWorker?.coreWorkerStats.usedResources; +const workerGPUUtilization = (worker: Worker | null) => { + const workerRes = worker?.coreWorkerStats[0].usedResources; const workerUsedGPUResources = workerRes?.["GPU"]; return ( workerUsedGPUResources && @@ -160,8 +160,8 @@ const workerGPUUtilization = (rayletWorker: RayletWorkerStats | null) => { ); }; -const workerGPUAccessor: Accessor = ({ rayletWorker }) => { - return workerGPUUtilization(rayletWorker) ?? 0; +const workerGPUAccessor: Accessor = ({ worker }) => { + return workerGPUUtilization(worker) ?? 0; }; const gpuFeature: NodeInfoFeature = { diff --git a/dashboard/client/src/pages/dashboard/node-info/features/GRAM.tsx b/dashboard/client/src/pages/dashboard/node-info/features/GRAM.tsx index a900e4596..503e623a8 100644 --- a/dashboard/client/src/pages/dashboard/node-info/features/GRAM.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/features/GRAM.tsx @@ -19,7 +19,7 @@ import { const GRAM_COL_WIDTH = 120; const nodeGRAMUtilization = (node: Node) => { - const utilization = (gpu: GPUStats) => gpu.memory_used / gpu.memory_total; + const utilization = (gpu: GPUStats) => gpu.memoryUsed / gpu.memoryTotal; if (node.gpus.length === 0) { return NaN; } @@ -69,8 +69,8 @@ export const NodeGRAM: NodeFeatureRenderFn = ({ node }) => { const nodeGRAMEntries = node.gpus.map((gpu, i) => { const props = { gpuName: gpu.name, - utilization: gpu.memory_used, - total: gpu.memory_total, + utilization: gpu.memoryUsed, + total: gpu.memoryTotal, slot: i, }; return ; @@ -124,8 +124,8 @@ export const WorkerGRAM: WorkerFeatureRenderFn = ({ worker, node }) => { } const props = { gpuName: gpu.name, - total: gpu.memory_total, - utilization: process.gpu_memory_usage, + total: gpu.memoryTotal, + utilization: process.gpuMemoryUsage, slot: i, }; return ; @@ -148,7 +148,7 @@ const workerGRAMUtilization = (worker: any, node: Node) => { processes.find((process) => process.pid === worker.pid), ); const workerUtilPerGPU = workerProcessPerGPU.map( - (proc) => proc?.gpu_memory_usage || 0, + (proc) => proc?.gpuMemoryUsage || 0, ); return sum(workerUtilPerGPU); }; diff --git a/dashboard/client/src/pages/dashboard/node-info/features/Logs.tsx b/dashboard/client/src/pages/dashboard/node-info/features/Logs.tsx index aae7b7c9c..78701ba7a 100644 --- a/dashboard/client/src/pages/dashboard/node-info/features/Logs.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/features/Logs.tsx @@ -5,7 +5,6 @@ import { Accessor } from "../../../../common/tableUtils"; import { sum } from "../../../../common/util"; import { ClusterFeatureRenderFn, - Node, NodeFeatureData, NodeFeatureRenderFn, NodeInfoFeature, @@ -13,11 +12,8 @@ import { WorkerFeatureRenderFn, } from "./types"; -const nodeLogCount = (node: Node) => - node.log_count ? sum(Object.values(node.log_count)) : 0; - const ClusterLogs: ClusterFeatureRenderFn = ({ nodes }) => { - const totalLogCount = sum(nodes.map(nodeLogCount)); + const totalLogCount = sum(nodes.map((n) => n.logCount)); return totalLogCount === 0 ? ( No logs @@ -30,47 +26,41 @@ const ClusterLogs: ClusterFeatureRenderFn = ({ nodes }) => { }; const makeNodeLogs = ( - setLogDialog: (hostname: string, pid: number | null) => void, -): NodeFeatureRenderFn => ({ node }) => { - const logCount = nodeLogCount(node); - return logCount === 0 ? ( + setLogDialog: (nodeIp: string, pid: number | null) => void, +): NodeFeatureRenderFn => ({ node }) => + node.logCount === 0 ? ( No logs ) : ( - setLogDialog(node.hostname, null)}> - View all logs ({logCount.toLocaleString()}{" "} - {logCount === 1 ? "line" : "lines"}) + setLogDialog(node.ip, null)}> + View all logs ({node.logCount.toLocaleString()}{" "} + {node.logCount === 1 ? "line" : "lines"}) ); -}; const nodeLogsAccessor: Accessor = ({ node }) => - node.log_count ? sum(Object.values(node.log_count)) : 0; + node.logCount ? sum(Object.values(node.logCount)) : 0; const makeWorkerLogs = ( - setLogDialog: (hostname: string, pid: number | null) => void, -): WorkerFeatureRenderFn => ({ node, worker }) => { - const workerLogCount = node.log_count?.[worker.pid] || 0; - return workerLogCount !== 0 ? ( - setLogDialog(node.hostname, worker.pid)}> - View log ({workerLogCount.toLocaleString()}{" "} - {workerLogCount === 1 ? "line" : "lines"}) + setLogDialog: (nodeIp: string, pid: number | null) => void, +): WorkerFeatureRenderFn => ({ worker, node }) => + worker.logCount !== 0 ? ( + setLogDialog(node.ip, worker.pid)}> + View log ({worker.logCount.toLocaleString()}{" "} + {worker.logCount === 1 ? "line" : "lines"}) ) : ( No logs ); -}; -const workerLogsAccessor: Accessor = ({ worker, node }) => { - const workerLogCount = node.log_count?.[worker.pid] || 0; - return workerLogCount; -}; +const workerLogsAccessor: Accessor = ({ worker }) => + worker.logCount; const makeLogsFeature = ( - setLogDialog: (hostname: string, pid: number | null) => void, + setLogDialog: (nodeIp: string, pid: number | null) => void, ): NodeInfoFeature => ({ id: "logs", ClusterFeatureRenderFn: ClusterLogs, diff --git a/dashboard/client/src/pages/dashboard/node-info/features/ObjectStoreMemory.tsx b/dashboard/client/src/pages/dashboard/node-info/features/ObjectStoreMemory.tsx index ac3a351e2..d6314fe9a 100644 --- a/dashboard/client/src/pages/dashboard/node-info/features/ObjectStoreMemory.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/features/ObjectStoreMemory.tsx @@ -13,13 +13,11 @@ import { WorkerFeatureRenderFn, } from "./types"; -export const ClusterObjectStoreMemory: ClusterFeatureRenderFn = ({ - plasmaStats, -}) => { +export const ClusterObjectStoreMemory: ClusterFeatureRenderFn = ({ nodes }) => { const totalAvailable = sum( - plasmaStats.map((s) => s.object_store_available_memory), + nodes.map((n) => n.raylet.objectStoreAvailableMemory), ); - const totalUsed = sum(plasmaStats.map((s) => s.object_store_used_memory)); + const totalUsed = sum(nodes.map((n) => n.raylet.objectStoreUsedMemory)); return (
{ - if (!plasmaStats) { +export const NodeObjectStoreMemory: NodeFeatureRenderFn = ({ node }) => { + const total = node.raylet.objectStoreAvailableMemory; + const used = node.raylet.objectStoreUsedMemory; + if (!used || !total) { return ( N/A ); } - const { - object_store_used_memory, - object_store_available_memory, - } = plasmaStats; - const usageRatio = object_store_used_memory / object_store_available_memory; + const usageRatio = used / total; return (
); }; export const nodeObjectStoreMemoryAccessor: Accessor = ({ - plasmaStats, -}) => { - return plasmaStats?.object_store_used_memory ?? 0; -}; + node, +}) => node.raylet.objectStoreUsedMemory; export const WorkerObjectStoreMemory: WorkerFeatureRenderFn = () => ( diff --git a/dashboard/client/src/pages/dashboard/node-info/features/RAM.tsx b/dashboard/client/src/pages/dashboard/node-info/features/RAM.tsx index ca57db36d..9ecaffa5c 100644 --- a/dashboard/client/src/pages/dashboard/node-info/features/RAM.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/features/RAM.tsx @@ -38,13 +38,13 @@ export const nodeRAMAccessor: Accessor = ({ node }) => export const WorkerRAM: WorkerFeatureRenderFn = ({ node, worker }) => ( ); export const workerRAMAccessor: Accessor = ({ worker }) => - worker.memory_info.rss; + worker.memoryInfo.rss; const ramFeature: NodeInfoFeature = { id: "ram", diff --git a/dashboard/client/src/pages/dashboard/node-info/features/Uptime.tsx b/dashboard/client/src/pages/dashboard/node-info/features/Uptime.tsx index 5e6a11a6c..34e2472fb 100644 --- a/dashboard/client/src/pages/dashboard/node-info/features/Uptime.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/features/Uptime.tsx @@ -20,20 +20,20 @@ export const ClusterUptime: ClusterFeatureRenderFn = ({ nodes }) => ( ); export const NodeUptime: NodeFeatureRenderFn = ({ node }) => ( - {formatDuration(getUptime(node.boot_time))} + {formatDuration(getUptime(node.bootTime))} ); export const nodeUptimeAccessor: Accessor = ({ node }) => - getUptime(node.boot_time); + getUptime(node.bootTime); export const WorkerUptime: WorkerFeatureRenderFn = ({ worker }) => ( - {formatDuration(getUptime(worker.create_time))} + {formatDuration(getUptime(worker.createTime))} ); const workerUptimeAccessor: Accessor = ({ worker }) => - getUptime(worker.create_time); + getUptime(worker.createTime); const uptimeFeature: NodeInfoFeature = { id: "uptime", diff --git a/dashboard/client/src/pages/dashboard/node-info/features/types.tsx b/dashboard/client/src/pages/dashboard/node-info/features/types.tsx index a6c3b596a..1e63d73ce 100644 --- a/dashboard/client/src/pages/dashboard/node-info/features/types.tsx +++ b/dashboard/client/src/pages/dashboard/node-info/features/types.tsx @@ -1,21 +1,16 @@ import React from "react"; -import { - NodeInfoResponse, - PlasmaStats, - RayletWorkerStats, -} from "../../../../api"; +import { NodeInfoResponse } from "../../../../api"; import { Accessor } from "../../../../common/tableUtils"; type ArrayType = T extends Array ? U : never; export type Node = ArrayType; export type Worker = ArrayType; -type ClusterFeatureData = { nodes: Node[]; plasmaStats: PlasmaStats[] }; -export type NodeFeatureData = { node: Node; plasmaStats?: PlasmaStats }; +type ClusterFeatureData = { nodes: Node[] }; +export type NodeFeatureData = { node: Node }; export type WorkerFeatureData = { node: Node; worker: Worker; - rayletWorker: RayletWorkerStats | null; }; export type ClusterFeatureRenderFn = ( diff --git a/dashboard/client/src/pages/dashboard/ray-config/RayConfig.tsx b/dashboard/client/src/pages/dashboard/ray-config/RayConfig.tsx index ed2e62df1..9c23e2411 100644 --- a/dashboard/client/src/pages/dashboard/ray-config/RayConfig.tsx +++ b/dashboard/client/src/pages/dashboard/ray-config/RayConfig.tsx @@ -1,5 +1,6 @@ import { createStyles, + makeStyles, Table, TableBody, TableCell, @@ -7,17 +8,15 @@ import { TableRow, Theme, Typography, - withStyles, - WithStyles, } from "@material-ui/core"; import classNames from "classnames"; -import React from "react"; -import { connect } from "react-redux"; +import React, { useCallback, useEffect, useRef } from "react"; +import { useDispatch, useSelector } from "react-redux"; import { getRayConfig } from "../../../api"; import { StoreState } from "../../../store"; import { dashboardActions } from "../state"; -const styles = (theme: Theme) => +const useRayConfigStyles = makeStyles((theme: Theme) => createStyles({ table: { marginTop: theme.spacing(1), @@ -36,104 +35,98 @@ const styles = (theme: Theme) => key: { color: theme.palette.text.secondary, }, - }); + }), +); +const { setRayConfig, setError } = dashboardActions; +const configSelector = (state: StoreState) => state.dashboard.rayConfig; -const mapStateToProps = (state: StoreState) => ({ - rayConfig: state.dashboard.rayConfig, -}); - -const mapDispatchToProps = dashboardActions; - -class RayConfig extends React.Component< - WithStyles & - ReturnType & - typeof mapDispatchToProps -> { - refreshRayConfig = async () => { +const RayConfig: React.FC = () => { + const classes = useRayConfigStyles(); + const rayConfig = useSelector(configSelector); + const dispatch = useDispatch(); + const refreshData = useCallback(async () => { try { const rayConfig = await getRayConfig(); - this.props.setRayConfig(rayConfig); + dispatch(setRayConfig(rayConfig)); } catch (error) { - } finally { - setTimeout(this.refreshRayConfig, 10 * 1000); + dispatch(setError(error.toString())); } - }; - - async componentDidMount() { - await this.refreshRayConfig(); - } - - render() { - const { classes, rayConfig } = this.props; - - if (rayConfig === null) { - return ( - - No Ray configuration detected. - - ); + }, [dispatch]); + const intervalId = useRef(null); + useEffect(() => { + if (intervalId.current === null) { + refreshData(); + intervalId.current = setInterval(refreshData, 10000); } - - const formattedRayConfig = [ - { - key: "Autoscaling mode", - value: rayConfig.autoscaling_mode, - }, - { - key: "Head node type", - value: rayConfig.head_type, - }, - { - key: "Worker node type", - value: rayConfig.worker_type, - }, - { - key: "Min worker nodes", - value: rayConfig.min_workers, - }, - { - key: "Initial worker nodes", - value: rayConfig.initial_workers, - }, - { - key: "Max worker nodes", - value: rayConfig.max_workers, - }, - { - key: "Idle timeout", - value: `${rayConfig.idle_timeout_minutes} ${ - rayConfig.idle_timeout_minutes === 1 ? "minute" : "minutes" - }`, - }, - ]; - + const cleanup = () => { + clearInterval(intervalId.current); + }; + return cleanup; + }, [refreshData]); + if (rayConfig === null) { return ( -
- Ray cluster configuration: - - - - Setting - Value - - - - {formattedRayConfig.map(({ key, value }, index) => ( - - - {key} - - {value} - - ))} - -
-
+ + No Ray configuration detected. + ); } -} -export default connect( - mapStateToProps, - mapDispatchToProps, -)(withStyles(styles)(RayConfig)); + const formattedRayConfig = [ + { + key: "Autoscaling mode", + value: rayConfig.autoscalingMode, + }, + { + key: "Head node type", + value: rayConfig.headType, + }, + { + key: "Worker node type", + value: rayConfig.workerType, + }, + { + key: "Min worker nodes", + value: rayConfig.minWorkers, + }, + { + key: "Initial worker nodes", + value: rayConfig.initialWorkers, + }, + { + key: "Max worker nodes", + value: rayConfig.maxWorkers, + }, + { + key: "Idle timeout", + value: `${rayConfig.idleTimeoutMinutes} ${ + rayConfig.idleTimeoutMinutes === 1 ? "minute" : "minutes" + }`, + }, + ]; + + return ( +
+ Ray cluster configuration: + + + + Setting + Value + + + + {formattedRayConfig.map(({ key, value }, index) => ( + + + {key} + + {value} + + ))} + +
+
+ ); +}; + +export default RayConfig; diff --git a/dashboard/client/src/pages/dashboard/state.ts b/dashboard/client/src/pages/dashboard/state.ts index 92410ea38..6139a0384 100644 --- a/dashboard/client/src/pages/dashboard/state.ts +++ b/dashboard/client/src/pages/dashboard/state.ts @@ -1,13 +1,16 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { + ActorGroup, + ActorsResponse, + MemoryTable, MemoryTableResponse, NodeInfoResponse, RayConfigResponse, - RayletInfoResponse, + TuneAvailability, TuneAvailabilityResponse, + TuneJob, TuneJobResponse, } from "../../api"; -import { filterObj } from "../../common/util"; const name = "dashboard"; @@ -15,20 +18,20 @@ type State = { tab: number; rayConfig: RayConfigResponse | null; nodeInfo: NodeInfoResponse | null; - rayletInfo: RayletInfoResponse | null; - tuneInfo: TuneJobResponse | null; - tuneAvailability: TuneAvailabilityResponse | null; + actorGroups: { [key: string]: ActorGroup } | null; + tuneInfo: TuneJob | null; + tuneAvailability: TuneAvailability | null; lastUpdatedAt: number | null; error: string | null; - memoryTable: MemoryTableResponse | null; + memoryTable: MemoryTable | null; shouldObtainMemoryTable: boolean; }; const initialState: State = { + actorGroups: null, tab: 0, rayConfig: null, nodeInfo: null, - rayletInfo: null, tuneInfo: null, tuneAvailability: null, lastUpdatedAt: null, @@ -47,39 +50,34 @@ const slice = createSlice({ setRayConfig: (state, action: PayloadAction) => { state.rayConfig = action.payload; }, - setNodeAndRayletInfo: ( + setNodeInfo: ( state, action: PayloadAction<{ nodeInfo: NodeInfoResponse; - rayletInfo: RayletInfoResponse; }>, ) => { - state.rayletInfo = action.payload.rayletInfo; - state.nodeInfo = filterNonClusterWorkerInfo( - action.payload.rayletInfo, - action.payload.nodeInfo, - ); + state.nodeInfo = action.payload.nodeInfo; state.lastUpdatedAt = Date.now(); }, + setActorGroups: (state, action: PayloadAction) => { + state.actorGroups = action.payload.actorGroups; + }, setTuneInfo: (state, action: PayloadAction) => { - state.tuneInfo = action.payload; + state.tuneInfo = action.payload.result; state.lastUpdatedAt = Date.now(); }, setTuneAvailability: ( state, action: PayloadAction, ) => { - state.tuneAvailability = action.payload; + state.tuneAvailability = action.payload.result; state.lastUpdatedAt = Date.now(); }, setError: (state, action: PayloadAction) => { state.error = action.payload; }, - setMemoryTable: ( - state, - action: PayloadAction, - ) => { - state.memoryTable = action.payload; + setMemoryTable: (state, action: PayloadAction) => { + state.memoryTable = action.payload.memoryTable; }, setShouldObtainMemoryTable: (state, action: PayloadAction) => { state.shouldObtainMemoryTable = action.payload; @@ -87,55 +85,5 @@ const slice = createSlice({ }, }); -const clusterWorkerPids = ( - rayletInfo: RayletInfoResponse, -): Map> => { - // Groups PIDs registered with the raylet by node IP address - // This is used to filter out processes belonging to other ray clusters. - const nodeMap = new Map(); - const workerPids = new Set(); - for (const [nodeIp, { workersStats }] of Object.entries(rayletInfo.nodes)) { - for (const worker of workersStats) { - if (!worker.isDriver) { - workerPids.add(worker.pid); - } - } - nodeMap.set(nodeIp, workerPids); - } - return nodeMap; -}; - -const filterNonClusterWorkerInfo = ( - rayletInfo: RayletInfoResponse, - nodeInfo: NodeInfoResponse, -) => { - // The back-end that generates the NodeInfoResponse does not remove worker - // information of workers that belong to other clusters, so we do it here. - const workerPidsByIP = clusterWorkerPids(rayletInfo); - const filteredClients = nodeInfo.clients.map((client) => { - const workerPids = workerPidsByIP.get(client.ip); - const workers = client.workers.filter((worker) => - workerPids?.has(worker.pid), - ); - const logs = client.log_count - ? filterObj(client.log_count, ([pid, _]: [string, any]) => - workerPids?.has(parseInt(pid)), - ) - : {}; - const errors = client.error_count - ? filterObj(client.error_count, ([pid, _]: [string, any]) => - workerPids?.has(parseInt(pid)), - ) - : {}; - client.workers = workers; - client.log_count = logs; - client.error_count = errors; - return client; - }); - return { - clients: filteredClients, - }; -}; - export const dashboardActions = slice.actions; export const dashboardReducer = slice.reducer; diff --git a/dashboard/client/src/pages/dashboard/tune/Tune.tsx b/dashboard/client/src/pages/dashboard/tune/Tune.tsx index b5f0bb3be..08dd287cc 100644 --- a/dashboard/client/src/pages/dashboard/tune/Tune.tsx +++ b/dashboard/client/src/pages/dashboard/tune/Tune.tsx @@ -177,7 +177,7 @@ class Tune extends React.Component< render() { const { classes, tuneInfo, tuneAvailability } = this.props; - if (tuneAvailability && !tuneAvailability.trials_available) { + if (tuneAvailability && !tuneAvailability.trialsAvailable) { return this.experimentChoice(true); } diff --git a/dashboard/client/src/pages/dashboard/tune/TuneErrors.tsx b/dashboard/client/src/pages/dashboard/tune/TuneErrors.tsx index 39b319069..17c46a2b1 100644 --- a/dashboard/client/src/pages/dashboard/tune/TuneErrors.tsx +++ b/dashboard/client/src/pages/dashboard/tune/TuneErrors.tsx @@ -90,10 +90,10 @@ class TuneErrors extends React.Component< Object.keys(tuneInfo.errors).map((key, index) => ( - {tuneInfo.errors[key].job_id} + {tuneInfo.errors[key].jobId} - {tuneInfo.errors[key].trial_id} + {tuneInfo.errors[key].trialId} {key} diff --git a/dashboard/client/src/pages/dashboard/tune/TuneTable.tsx b/dashboard/client/src/pages/dashboard/tune/TuneTable.tsx index ec2874342..e318409b6 100644 --- a/dashboard/client/src/pages/dashboard/tune/TuneTable.tsx +++ b/dashboard/client/src/pages/dashboard/tune/TuneTable.tsx @@ -184,11 +184,11 @@ class TuneTable extends React.Component< const { tuneInfo } = this.props; const { sortedColumn, ascending, metricParamColumn } = this.state; - if (tuneInfo === null || Object.keys(tuneInfo.trial_records).length === 0) { + if (tuneInfo === null || Object.keys(tuneInfo.trialRecords).length === 0) { return null; } - const trialDetails = Object.values(tuneInfo.trial_records); + const trialDetails = Object.values(tuneInfo.trialRecords); if (!sortedColumn) { return trialDetails; @@ -304,15 +304,12 @@ class TuneTable extends React.Component< const { metricColumns, paramColumns, open, errorTrial } = this.state; - if ( - tuneInfo === null || - Object.keys(tuneInfo["trial_records"]).length === 0 - ) { + if (tuneInfo === null || Object.keys(tuneInfo.trialRecords).length === 0) { return null; } - const firstTrial = Object.keys(tuneInfo.trial_records)[0]; - const paramsDict = tuneInfo.trial_records[firstTrial].params; + const firstTrial = Object.keys(tuneInfo.trialRecords)[0]; + const paramsDict = tuneInfo.trialRecords[firstTrial].params; const paramNames = Object.keys(paramsDict).filter((k) => k !== "args"); let viewableParams = paramNames; @@ -326,7 +323,7 @@ class TuneTable extends React.Component< viewableParams = paramColumns; } - const metricNames = Object.keys(tuneInfo.trial_records[firstTrial].metrics); + const metricNames = Object.keys(tuneInfo.trialRecords[firstTrial].metrics); let viewableMetrics = metricNames; const metricOptions = metricNames.length > 3; @@ -358,9 +355,9 @@ class TuneTable extends React.Component< - {this.sortedCell("trial_id")} - {this.sortedCell("job_id")} - {this.sortedCell("start_time")} + {this.sortedCell("trialId")} + {this.sortedCell("jobId")} + {this.sortedCell("startTime")} {viewableParams.map((value, index) => this.sortedCell("params", value, index), )} @@ -378,30 +375,30 @@ class TuneTable extends React.Component< trialDetails.map((trial, index) => ( - {trial["trial_id"]} + {trial.trialId} - {trial["job_id"]} + {trial.jobId} - {trial["start_time"]} + {trial.startTime} {viewableParams.map((value, index) => ( - {typeof trial["params"][value] === "number" - ? formatValue(Number(trial["params"][value])) - : trial["params"][value]} + {typeof trial.params[value] === "number" + ? formatValue(Number(trial.params[value])) + : trial.params[value]} ))} {trial["status"]} - {trial["metrics"] && + {trial.metrics && viewableMetrics.map((value, index) => ( - {typeof trial["metrics"][value] === "number" - ? formatValue(Number(trial["metrics"][value])) - : trial["metrics"][value]} + {typeof trial.metrics[value] === "number" + ? formatValue(Number(trial.metrics[value])) + : trial.metrics[value]} ))} @@ -412,7 +409,7 @@ class TuneTable extends React.Component< component="button" variant="body2" onClick={() => { - this.handleOpen(trial["trial_id"]); + this.handleOpen(trial.trialId); }} > Show Error @@ -429,7 +426,7 @@ class TuneTable extends React.Component< {open && ( diff --git a/dashboard/client/src/pages/dashboard/tune/TuneTensorBoard.tsx b/dashboard/client/src/pages/dashboard/tune/TuneTensorBoard.tsx index 8135afdd1..e0c799c65 100644 --- a/dashboard/client/src/pages/dashboard/tune/TuneTensorBoard.tsx +++ b/dashboard/client/src/pages/dashboard/tune/TuneTensorBoard.tsx @@ -84,7 +84,7 @@ class TuneTensorBoard extends React.Component< "tensorboard --logdir" if not displaying below. )} - {tuneInfo && !tuneInfo.tensorboard.tensorboard_current && ( + {tuneInfo && !tuneInfo.tensorboard.tensorboardCurrent && ( The below Tensorboard reflects a previously entered log directory. Restart the Ray Dashboard to change the Tensorboard logdir. @@ -107,7 +107,7 @@ class TuneTensorBoard extends React.Component< if (tuneInfo === null) { return; } - const enabled = tuneInfo.tensorboard.tensorboard_enabled; + const enabled = tuneInfo.tensorboard.tensorboardEnabled; return (
{!enabled && (