diff --git a/python/ray/dashboard/client/package-lock.json b/python/ray/dashboard/client/package-lock.json index 68d5dab3c..bd8ea06dd 100644 --- a/python/ray/dashboard/client/package-lock.json +++ b/python/ray/dashboard/client/package-lock.json @@ -1647,22 +1647,22 @@ } }, "@material-ui/core": { - "version": "4.9.7", - "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.9.7.tgz", - "integrity": "sha512-RTRibZgq572GHEskMAG4sP+bt3P3XyIkv3pOTR8grZAW2rSUd6JoGZLRM4S2HkuO7wS7cAU5SpU2s1EsmTgWog==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.0.tgz", + "integrity": "sha512-bYo9uIub8wGhZySHqLQ833zi4ZML+XCBE1XwJ8EuUVSpTWWG57Pm+YugQToJNFsEyiKFhPh8DPD0bgupz8n01g==", "requires": { "@babel/runtime": "^7.4.4", - "@material-ui/styles": "^4.9.6", - "@material-ui/system": "^4.9.6", - "@material-ui/types": "^5.0.0", - "@material-ui/utils": "^4.9.6", + "@material-ui/styles": "^4.10.0", + "@material-ui/system": "^4.9.14", + "@material-ui/types": "^5.1.0", + "@material-ui/utils": "^4.10.2", "@types/react-transition-group": "^4.2.0", - "clsx": "^1.0.2", + "clsx": "^1.0.4", "hoist-non-react-statics": "^3.3.2", - "popper.js": "^1.14.1", + "popper.js": "1.16.1-lts", "prop-types": "^15.7.2", "react-is": "^16.8.0", - "react-transition-group": "^4.3.0" + "react-transition-group": "^4.4.0" } }, "@material-ui/icons": { @@ -1674,15 +1674,15 @@ } }, "@material-ui/styles": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.9.6.tgz", - "integrity": "sha512-ijgwStEkw1OZ6gCz18hkjycpr/3lKs1hYPi88O/AUn4vMuuGEGAIrqKVFq/lADmZUNF3DOFIk8LDkp7zmjPxtA==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.10.0.tgz", + "integrity": "sha512-XPwiVTpd3rlnbfrgtEJ1eJJdFCXZkHxy8TrdieaTvwxNYj42VnnCyFzxYeNW9Lhj4V1oD8YtQ6S5Gie7bZDf7Q==", "requires": { "@babel/runtime": "^7.4.4", "@emotion/hash": "^0.8.0", - "@material-ui/types": "^5.0.0", + "@material-ui/types": "^5.1.0", "@material-ui/utils": "^4.9.6", - "clsx": "^1.0.2", + "clsx": "^1.0.4", "csstype": "^2.5.2", "hoist-non-react-statics": "^3.3.2", "jss": "^10.0.3", @@ -1697,24 +1697,25 @@ } }, "@material-ui/system": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.9.6.tgz", - "integrity": "sha512-QtfoAePyqXoZ2HUVSwGb1Ro0kucMCvVjbI0CdYIR21t0Opgfm1Oer6ni9P5lfeXA39xSt0wCierw37j+YES48Q==", + "version": "4.9.14", + "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.9.14.tgz", + "integrity": "sha512-oQbaqfSnNlEkXEziDcJDDIy8pbvwUmZXWNqlmIwDqr/ZdCK8FuV3f4nxikUh7hvClKV2gnQ9djh5CZFTHkZj3w==", "requires": { "@babel/runtime": "^7.4.4", "@material-ui/utils": "^4.9.6", + "csstype": "^2.5.2", "prop-types": "^15.7.2" } }, "@material-ui/types": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.0.0.tgz", - "integrity": "sha512-UeH2BuKkwDndtMSS0qgx1kCzSMw+ydtj0xx/XbFtxNSTlXydKwzs5gVW5ZKsFlAkwoOOQ9TIsyoCC8hq18tOwg==" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", + "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==" }, "@material-ui/utils": { - "version": "4.9.6", - "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.9.6.tgz", - "integrity": "sha512-gqlBn0JPPTUZeAktn1rgMcy9Iczrr74ecx31tyZLVGdBGGzsxzM6PP6zeS7FuoLS6vG4hoZP7hWnOoHtkR0Kvw==", + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.10.2.tgz", + "integrity": "sha512-eg29v74P7W5r6a4tWWDAAfZldXIzfyO1am2fIsC39hdUUHm/33k6pGOKPbgDjg/U/4ifmgAePy/1OjkKN6rFRw==", "requires": { "@babel/runtime": "^7.4.4", "prop-types": "^15.7.2", @@ -2045,9 +2046,9 @@ } }, "@types/react-transition-group": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.2.4.tgz", - "integrity": "sha512-8DMUaDqh0S70TjkqU0DxOu80tFUiiaS9rxkWip/nb7gtvAsbqOXm02UCmR8zdcjWujgeYPiPNTVpVpKzUDotwA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", + "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", "requires": { "@types/react": "*" } @@ -3747,9 +3748,9 @@ } }, "clsx": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.0.tgz", - "integrity": "sha512-3avwM37fSK5oP6M5rQ9CNe99lwxhXDOeSWVPAOYF6OazUTgZCMb0yWlJpmdD74REy1gkEaFiub2ULv4fq9GUhA==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", + "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" }, "co": { "version": "4.6.0", @@ -4310,11 +4311,11 @@ } }, "css-vendor": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.7.tgz", - "integrity": "sha512-VS9Rjt79+p7M0WkPqcAza4Yq1ZHrsHrwf7hPL/bjQB+c1lwmAI+1FXxYTYt818D/50fFVflw0XKleiBN5RITkg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", "requires": { - "@babel/runtime": "^7.6.2", + "@babel/runtime": "^7.8.3", "is-in-browser": "^1.0.2" } }, @@ -4761,12 +4762,19 @@ } }, "dom-helpers": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.3.tgz", - "integrity": "sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", + "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", "requires": { - "@babel/runtime": "^7.6.3", - "csstype": "^2.6.7" + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.2.tgz", + "integrity": "sha512-ofovWglpqoqbfLNOTBNZLSbMuGrblAf1efvvArGKOZMBrIoJeu5UsAipQolkijtyQx5MtAzT/J9IHj/CEY1mJw==" + } } }, "dom-serializer": { @@ -6956,9 +6964,9 @@ "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=" }, "hyphenate-style-name": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz", - "integrity": "sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" }, "iconv-lite": { "version": "0.4.24", @@ -9857,80 +9865,88 @@ } }, "jss": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/jss/-/jss-10.1.1.tgz", - "integrity": "sha512-Xz3qgRUFlxbWk1czCZibUJqhVPObrZHxY3FPsjCXhDld4NOj1BgM14Ir5hVm+Qr6OLqVljjGvoMcCdXNOAbdkQ==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.4.0.tgz", + "integrity": "sha512-l7EwdwhsDishXzqTc3lbsbyZ83tlUl5L/Hb16pHCvZliA9lRDdNBZmHzeJHP0sxqD0t1mrMmMR8XroR12JBYzw==", "requires": { "@babel/runtime": "^7.3.1", - "csstype": "^2.6.5", + "csstype": "^3.0.2", "is-in-browser": "^1.1.3", "tiny-warning": "^1.0.2" + }, + "dependencies": { + "csstype": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.2.tgz", + "integrity": "sha512-ofovWglpqoqbfLNOTBNZLSbMuGrblAf1efvvArGKOZMBrIoJeu5UsAipQolkijtyQx5MtAzT/J9IHj/CEY1mJw==" + } } }, "jss-plugin-camel-case": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.1.1.tgz", - "integrity": "sha512-MDIaw8FeD5uFz1seQBKz4pnvDLnj5vIKV5hXSVdMaAVq13xR6SVTVWkIV/keyTs5txxTvzGJ9hXoxgd1WTUlBw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.4.0.tgz", + "integrity": "sha512-9oDjsQ/AgdBbMyRjc06Kl3P8lDCSEts2vYZiPZfGAxbGCegqE4RnMob3mDaBby5H9vL9gWmyyImhLRWqIkRUCw==", "requires": { "@babel/runtime": "^7.3.1", "hyphenate-style-name": "^1.0.3", - "jss": "10.1.1" + "jss": "10.4.0" } }, "jss-plugin-default-unit": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.1.1.tgz", - "integrity": "sha512-UkeVCA/b3QEA4k0nIKS4uWXDCNmV73WLHdh2oDGZZc3GsQtlOCuiH3EkB/qI60v2MiCq356/SYWsDXt21yjwdg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.4.0.tgz", + "integrity": "sha512-BYJ+Y3RUYiMEgmlcYMLqwbA49DcSWsGgHpVmEEllTC8MK5iJ7++pT9TnKkKBnNZZxTV75ycyFCR5xeLSOzVm4A==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.1.1" + "jss": "10.4.0" } }, "jss-plugin-global": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.1.1.tgz", - "integrity": "sha512-VBG3wRyi3Z8S4kMhm8rZV6caYBegsk+QnQZSVmrWw6GVOT/Z4FA7eyMu5SdkorDlG/HVpHh91oFN56O4R9m2VA==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.4.0.tgz", + "integrity": "sha512-b8IHMJUmv29cidt3nI4bUI1+Mo5RZE37kqthaFpmxf5K7r2aAegGliAw4hXvA70ca6ckAoXMUl4SN/zxiRcRag==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.1.1" + "jss": "10.4.0" } }, "jss-plugin-nested": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.1.1.tgz", - "integrity": "sha512-ozEu7ZBSVrMYxSDplPX3H82XHNQk2DQEJ9TEyo7OVTPJ1hEieqjDFiOQOxXEj9z3PMqkylnUbvWIZRDKCFYw5Q==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.4.0.tgz", + "integrity": "sha512-cKgpeHIxAP0ygeWh+drpLbrxFiak6zzJ2toVRi/NmHbpkNaLjTLgePmOz5+67ln3qzJiPdXXJB1tbOyYKAP4Pw==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.1.1", + "jss": "10.4.0", "tiny-warning": "^1.0.2" } }, "jss-plugin-props-sort": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.1.1.tgz", - "integrity": "sha512-g/joK3eTDZB4pkqpZB38257yD4LXB0X15jxtZAGbUzcKAVUHPl9Jb47Y7lYmiGsShiV4YmQRqG1p2DHMYoK91g==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.4.0.tgz", + "integrity": "sha512-j/t0R40/2fp+Nzt6GgHeUFnHVY2kPGF5drUVlgkcwYoHCgtBDOhTTsOfdaQFW6sHWfoQYgnGV4CXdjlPiRrzwA==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.1.1" + "jss": "10.4.0" } }, "jss-plugin-rule-value-function": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.1.1.tgz", - "integrity": "sha512-ClV1lvJ3laU9la1CUzaDugEcwnpjPTuJ0yGy2YtcU+gG/w9HMInD5vEv7xKAz53Bk4WiJm5uLOElSEshHyhKNw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.4.0.tgz", + "integrity": "sha512-w8504Cdfu66+0SJoLkr6GUQlEb8keHg8ymtJXdVHWh0YvFxDG2l/nS93SI5Gfx0fV29dO6yUugXnKzDFJxrdFQ==", "requires": { "@babel/runtime": "^7.3.1", - "jss": "10.1.1" + "jss": "10.4.0", + "tiny-warning": "^1.0.2" } }, "jss-plugin-vendor-prefixer": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.1.1.tgz", - "integrity": "sha512-09MZpQ6onQrhaVSF6GHC4iYifQ7+4YC/tAP6D4ZWeZotvCMq1mHLqNKRIaqQ2lkgANjlEot2JnVi1ktu4+L4pw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.4.0.tgz", + "integrity": "sha512-DpF+/a+GU8hMh/948sBGnKSNfKkoHg2p9aRFUmyoyxgKjOeH9n74Ht3Yt8lOgdZsuWNJbPrvaa3U4PXKwxVpTQ==", "requires": { "@babel/runtime": "^7.3.1", - "css-vendor": "^2.0.7", - "jss": "10.1.1" + "css-vendor": "^2.0.8", + "jss": "10.4.0" } }, "jsx-ast-utils": { @@ -11386,9 +11402,9 @@ } }, "popper.js": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", - "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" + "version": "1.16.1-lts", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", + "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==" }, "portfinder": { "version": "1.0.25", @@ -13020,9 +13036,9 @@ } }, "react-transition-group": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz", - "integrity": "sha512-1qRV1ZuVSdxPlPf4O8t7inxUGpdyO5zG9IoNfJxSO0ImU2A1YWkEQvFPuIPZmMLkg5hYs7vv5mMOyfgSkvAwvw==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", + "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", "requires": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", diff --git a/python/ray/dashboard/client/package.json b/python/ray/dashboard/client/package.json index fb2af9f1c..7a9680a9f 100644 --- a/python/ray/dashboard/client/package.json +++ b/python/ray/dashboard/client/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@material-ui/core": "^4.9.7", + "@material-ui/core": "4.11.0", "@material-ui/icons": "^4.9.1", "@reduxjs/toolkit": "^1.3.1", "@types/classnames": "^2.2.10", diff --git a/python/ray/dashboard/client/src/api.ts b/python/ray/dashboard/client/src/api.ts index 835188911..9e8ea97c7 100644 --- a/python/ray/dashboard/client/src/api.ts +++ b/python/ray/dashboard/client/src/api.ts @@ -155,14 +155,13 @@ export enum ActorState { Dead = 4, } -export type RayletActorInfo = FullActorInfo | PartialActorInfo; +export type ActorInfo = FullActorInfo | PartialActorInfo; export type FullActorInfo = { actorId: string; actorTitle: string; averageTaskExecutionSpeed: number; - children: RayletInfoResponse["actors"]; - // currentTaskFuncDesc: string[]; + children?: ActorInfo[]; ipAddress: string; jobId: string; nodeId: string; @@ -187,8 +186,8 @@ export type FullActorInfo = { }; export type PartialActorInfo = { - actorId: string; - actorTitle: string; + actorId?: string; + actorTitle?: string; requiredResources?: { [key: string]: number }; state: ActorState.Invalid; invalidStateType?: InvalidStateType; @@ -196,12 +195,12 @@ export type PartialActorInfo = { // eslint-disable-next-line export function isFullActorInfo( - rayletInfo: RayletActorInfo, -): rayletInfo is FullActorInfo { + actorInfo: ActorInfo, +): actorInfo 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; + return actorInfo.state !== ActorState.Invalid; } export type InvalidStateType = "infeasibleActor" | "pendingActor"; @@ -214,7 +213,7 @@ export type RayletInfoResponse = { }; }; actors: { - [actorId: string]: RayletActorInfo; + [actorId: string]: ActorInfo; }; }; diff --git a/python/ray/dashboard/client/src/pages/dashboard/logical-view/Actor.tsx b/python/ray/dashboard/client/src/pages/dashboard/logical-view/Actor.tsx index f8d9fa539..3fe6bdcff 100644 --- a/python/ray/dashboard/client/src/pages/dashboard/logical-view/Actor.tsx +++ b/python/ray/dashboard/client/src/pages/dashboard/logical-view/Actor.tsx @@ -1,5 +1,4 @@ import { - Collapse, createStyles, Theme, Typography, @@ -8,6 +7,7 @@ import { } from "@material-ui/core"; import React from "react"; import { + ActorInfo, ActorState, checkProfilingStatus, CheckProfilingStatusResponse, @@ -15,11 +15,9 @@ import { isFullActorInfo, launchKillActor, launchProfiling, - RayletActorInfo, } from "../../../api"; import { sum } from "../../../common/util"; import ActorDetailsPane from "./ActorDetailsPane"; -import Actors from "./Actors"; const memoryDebuggingDocLink = "https://docs.ray.io/en/latest/memory-management.html#debugging-using-ray-memory"; @@ -31,6 +29,7 @@ const styles = (theme: Theme) => borderWidth: 1, marginTop: theme.spacing(2), padding: theme.spacing(2), + width: "100%", }, title: { color: theme.palette.text.secondary, @@ -60,11 +59,10 @@ const styles = (theme: Theme) => }); type Props = { - actor: RayletActorInfo; + actor: ActorInfo; }; type State = { - expanded: boolean; profiling: { [profilingId: string]: { startTime: number; @@ -75,14 +73,9 @@ type State = { class Actor extends React.Component, State> { state: State = { - expanded: true, profiling: {}, }; - setExpanded = (expanded: boolean) => () => { - this.setState({ expanded }); - }; - handleProfilingClick = (duration: number) => async () => { const actor = this.props.actor; if (actor.state === ActorState.Alive) { @@ -125,7 +118,7 @@ class Actor extends React.Component, State> { render() { const { classes, actor } = this.props; - const { expanded, profiling } = this.state; + const { profiling } = this.state; const invalidStateType = isFullActorInfo(actor) ? undefined : actor.invalidStateType; @@ -243,20 +236,7 @@ class Actor extends React.Component, State> { {isFullActorInfo(actor) ? ( - Actor {actor.actorId}{" "} - {Object.entries(actor.children).length > 0 && ( - - ( - - {expanded ? "Collapse" : "Expand"} - - ) - - )}{" "} - (Profile for + Actor {actor.actorId} (Profile for {[10, 30, 60].map((duration) => ( {" "} @@ -313,7 +293,7 @@ class Actor extends React.Component, State> { @@ -322,10 +302,6 @@ class Actor extends React.Component, State> { {actorCustomDisplay.length > 0 && ( {actorCustomDisplay} )} - - - - )} diff --git a/python/ray/dashboard/client/src/pages/dashboard/logical-view/ActorClassGroup.tsx b/python/ray/dashboard/client/src/pages/dashboard/logical-view/ActorClassGroup.tsx new file mode 100644 index 000000000..0adf2f8d3 --- /dev/null +++ b/python/ray/dashboard/client/src/pages/dashboard/logical-view/ActorClassGroup.tsx @@ -0,0 +1,57 @@ +import { + Accordion, + AccordionDetails, + AccordionSummary, + Box, + createStyles, + makeStyles, + Paper, + Typography, +} from "@material-ui/core"; +import ExpandMoreIcon from "@material-ui/icons/ExpandMore"; +import React from "react"; +import { ActorInfo } from "../../../api"; +import Actor from "./Actor"; + +const useActorClassGroupStyles = makeStyles((theme) => + createStyles({ + container: { + margin: theme.spacing(1), + }, + actorEntry: { + width: "100%", + }, + }), +); + +type ActorClassGroupProps = { + title: string; + actors: ActorInfo[]; +}; + +const ActorClassGroup: React.FC = ({ actors, title }) => { + const classes = useActorClassGroupStyles(); + const entries = actors.map((actor, i) => ( + + + + )); + return ( + + + } + aria-controls="panel1a-content" + id="panel1a-header" + > + {title} + + + {entries} + + + + ); +}; + +export default ActorClassGroup; diff --git a/python/ray/dashboard/client/src/pages/dashboard/logical-view/ActorClassGroups.tsx b/python/ray/dashboard/client/src/pages/dashboard/logical-view/ActorClassGroups.tsx new file mode 100644 index 000000000..d3ab1f754 --- /dev/null +++ b/python/ray/dashboard/client/src/pages/dashboard/logical-view/ActorClassGroups.tsx @@ -0,0 +1,39 @@ +import React from "react"; +import { ActorInfo } from "../../../api"; +import ActorClassGroup from "./ActorClassGroup"; + +type ActorClassGroupsProps = { + actors: ActorInfo[]; +}; + +const extractClassName = (actor: ActorInfo) => { + // Given a python class name like Foo(arg1, arg2) + // this function returns "Foo" + const re = /(.+)\(/; + const matches = actor.actorTitle?.match(re); + if (matches) { + return matches[1]; + } +}; + +const ActorClassGroups: React.FC = ({ actors }) => { + const groups = new Map(); + actors.forEach((actor) => { + const className = extractClassName(actor) ?? "Unknown Class"; + const existingGroup = groups.get(className); + if (existingGroup) { + existingGroup.push(actor); + } else { + groups.set(className, [actor]); + } + }); + + const children = Array.from(groups) + .sort(([title], [title2]) => (title > title2 ? 1 : -1)) + .map(([title, actorGroup]) => ( + + )); + return {children}; +}; + +export default ActorClassGroups; diff --git a/python/ray/dashboard/client/src/pages/dashboard/logical-view/LogicalView.tsx b/python/ray/dashboard/client/src/pages/dashboard/logical-view/LogicalView.tsx index 56cef7f06..6c3f70e60 100644 --- a/python/ray/dashboard/client/src/pages/dashboard/logical-view/LogicalView.tsx +++ b/python/ray/dashboard/client/src/pages/dashboard/logical-view/LogicalView.tsx @@ -7,19 +7,12 @@ import { } from "@material-ui/core"; import React, { useState } from "react"; import { connect } from "react-redux"; -import { - isFullActorInfo, - RayletActorInfo, - RayletInfoResponse, -} from "../../../api"; +import { ActorInfo, isFullActorInfo, RayletInfoResponse } from "../../../api"; import { filterObj } from "../../../common/util"; import { StoreState } from "../../../store"; -import Actors from "./Actors"; +import ActorClassGroups from "./ActorClassGroups"; -const actorMatchesSearch = ( - actor: RayletActorInfo, - nameFilter: string, -): boolean => { +const actorMatchesSearch = (actor: ActorInfo, nameFilter: string): boolean => { // Performs a case insensitive search for the name filter string within the // actor and all of its nested subactors. const actorTitles = getNestedActorTitles(actor); @@ -30,7 +23,7 @@ const actorMatchesSearch = ( return match !== undefined; }; -const getNestedActorTitles = (actor: RayletActorInfo): string[] => { +const getNestedActorTitles = (actor: ActorInfo): string[] => { const actorTitle = actor.actorTitle; const titles: string[] = actorTitle ? [actorTitle] : []; if (!isFullActorInfo(actor)) { @@ -62,10 +55,8 @@ const LogicalView: React.FC = ({ rayletInfo }) => { } let filteredActors = rayletInfo.actors; if (nameFilter !== "") { - filteredActors = filterObj( - filteredActors, - ([_, actor]: [any, RayletActorInfo]) => - actorMatchesSearch(actor, nameFilter), + filteredActors = filterObj(filteredActors, ([_, actor]: [any, ActorInfo]) => + actorMatchesSearch(actor, nameFilter), ); } @@ -87,7 +78,7 @@ const LogicalView: React.FC = ({ rayletInfo }) => { Search for an actor by name - + )} diff --git a/python/ray/dashboard/dashboard.py b/python/ray/dashboard/dashboard.py index e8d996028..0ca09ed91 100644 --- a/python/ray/dashboard/dashboard.py +++ b/python/ray/dashboard/dashboard.py @@ -91,8 +91,8 @@ class DashboardController(BaseDashboardController): # (e.g., Actor requires 2 GPUs but there is only 1 gpu available). ready_tasks = sum((data.get("readyTasks", []) for data in D.values()), []) - actor_tree = self.node_stats.get_actor_tree( - workers_info_by_node, infeasible_tasks, ready_tasks) + actors = self.node_stats.get_actors(workers_info_by_node, + infeasible_tasks, ready_tasks) for address, data in D.items(): # process view data @@ -131,15 +131,14 @@ class DashboardController(BaseDashboardController): stats_name, stats_value)) data["extraInfo"] += ", ".join(extra_info_strings) # process actor info - actor_tree_str = json.dumps( - actor_tree, indent=2, sort_keys=True) - lines = actor_tree_str.split("\n") + actors_str = json.dumps(actors, indent=2, sort_keys=True) + lines = actors_str.split("\n") max_line_length = max(map(len, lines)) to_print = [] for line in lines: to_print.append(line + (max_line_length - len(line)) * " ") data["extraInfo"] += "\n" + "\n".join(to_print) - return {"nodes": D, "actors": actor_tree} + return {"nodes": D, "actors": actors} def get_ray_config(self): try: diff --git a/python/ray/dashboard/node_stats.py b/python/ray/dashboard/node_stats.py index 76f99c67c..63eb420e8 100644 --- a/python/ray/dashboard/node_stats.py +++ b/python/ray/dashboard/node_stats.py @@ -9,6 +9,7 @@ import copy import logging import datetime import time +from typing import Dict import re from operator import itemgetter @@ -91,6 +92,55 @@ class NodeStats(threading.Thread): key=itemgetter("boot_time")) return {"clients": node_stats} + # Gets actors in a flat way to allow for grouping by actor type. + def get_actors(self, workers_info_by_node, infeasible_tasks, ready_tasks): + now = time.time() + actors: Dict[str, Dict[str, any]] = {} + # construct flattened actor tree + with self._node_stats_lock: + for addr, actor_id in self._addr_to_actor_id.items(): + actors[actor_id] = copy.deepcopy(self._default_info) + actors[actor_id].update(self._addr_to_extra_info_dict[addr]) + + for node_id, workers_info in workers_info_by_node.items(): + for worker_info in workers_info: + if "coreWorkerStats" in worker_info: + core_worker_stats = worker_info["coreWorkerStats"] + addr = (core_worker_stats["ipAddress"], + str(core_worker_stats["port"])) + if addr in self._addr_to_actor_id: + actor_info = actors[self._addr_to_actor_id[addr]] + format_reply_id(core_worker_stats) + actor_info.update(core_worker_stats) + actor_info["averageTaskExecutionSpeed"] = round( + actor_info["numExecutedTasks"] / + (now - actor_info["timestamp"] / 1000), 2) + actor_info["nodeId"] = node_id + actor_info["pid"] = worker_info["pid"] + + def _update_from_actor_tasks(task, task_spec_type, + invalid_state_type): + actor_id = ray.utils.binary_to_hex( + b64decode(task[task_spec_type]["actorId"])) + task["state"] = -1 + task["invalidStateType"] = invalid_state_type + task["actorTitle"] = task["functionDescriptor"][ + "pythonFunctionDescriptor"]["className"] + format_reply_id(task) + actors[actor_id] = task + + for infeasible_task in infeasible_tasks: + _update_from_actor_tasks(infeasible_task, + "actorCreationTaskSpec", + "infeasibleActor") + + for ready_task in ready_tasks: + _update_from_actor_tasks(ready_task, "actorCreationTaskSpec", + "pendingActor") + + return actors + + # Gets actors in a nested structure showing parent child relationships def get_actor_tree(self, workers_info_by_node, infeasible_tasks, ready_tasks): now = time.time() diff --git a/python/ray/tests/test_metrics.py b/python/ray/tests/test_metrics.py index c7ffe745c..79c52b017 100644 --- a/python/ray/tests/test_metrics.py +++ b/python/ray/tests/test_metrics.py @@ -206,14 +206,15 @@ def test_raylet_info_endpoint(shutdown_only): except Exception as ex: print("failed response: {}".format(response.text)) raise ex - actor_info = raylet_info["result"]["actors"] + actors_info = raylet_info["result"]["actors"] try: - assert len(actor_info) == 1 - _, parent_actor_info = actor_info.popitem() - assert parent_actor_info["numObjectRefsInScope"] == 13 - assert parent_actor_info["numLocalObjects"] == 10 - children = parent_actor_info["children"] - assert len(children) == 2 + assert len(actors_info) == 3 + c_actor_info = [ + actor for actor in actors_info.values() + if "ActorC" in actor["actorTitle"] + ][0] + assert c_actor_info["numObjectRefsInScope"] == 13 + assert c_actor_info["numLocalObjects"] == 10 break except AssertionError: if time.time() > start_time + 30: @@ -230,16 +231,8 @@ def test_raylet_info_endpoint(shutdown_only): cpu_resources += slot["allocation"] return cpu_resources - assert cpu_resources(parent_actor_info) == 2 - assert parent_actor_info["numExecutedTasks"] == 4 - for _, child_actor_info in children.items(): - if child_actor_info["state"] == -1: - assert child_actor_info["requiredResources"]["CustomResource"] == 1 - else: - assert child_actor_info[ - "state"] == ray.gcs_utils.ActorTableData.ALIVE - assert len(child_actor_info["children"]) == 0 - assert cpu_resources(child_actor_info) == 1 + assert cpu_resources(c_actor_info) == 2 + assert c_actor_info["numExecutedTasks"] == 4 profiling_id = requests.get( webui_url + "/api/launch_profiling", @@ -329,26 +322,12 @@ def test_raylet_pending_tasks(shutdown_only): actor_info = raylet_info["result"]["actors"] assert len(actor_info) == 1 _, infeasible_actor_info = actor_info.popitem() - - # Verify there are 4 spawned actors. - children = infeasible_actor_info["children"] - assert len(children) == 4 - - pending_actor_detected = 0 - for child_id, child in children.items(): - if ("invalidStateType" in child - and child["invalidStateType"] == "pendingActor"): - pending_actor_detected += 1 - # 4 GPUActors are spawned although there are only 3 GPUs. - # One actor should be in the pending state. - assert pending_actor_detected == 1 - - assert (wait_until_succeeded_without_exception( - test_pending_actor, - (AssertionError, requests.exceptions.ConnectionError), - addresses, - timeout_ms=30000, - retry_interval_ms=1000) is True) + wait_until_succeeded_without_exception( + test_pending_actor, + (AssertionError, requests.exceptions.ConnectionError), + addresses, + timeout_ms=30000, + retry_interval_ms=1000) @pytest.mark.skipif(