Skip to content

Commit

Permalink
Update profiling (#111)
Browse files Browse the repository at this point in the history
* update PromiseButton

* Update ProfilingControlStack.tsx

* ProfilingModal

* Update table.ts

* Update ProfilingModal.tsx

* Revert "Update table.ts"

This reverts commit 77f42b3.
  • Loading branch information
renzholy authored Aug 18, 2020
1 parent 4970889 commit 4b2b065
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 67 deletions.
38 changes: 22 additions & 16 deletions src/components/ProfilingControlStack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function ProfilingControlStack() {
const [level, setLevel] = useState<ProfilingLevel>()
const [slowms, setSlowms] = useState(0)
const [sampleRate, setSampleRate] = useState(0)
const { data: profile, revalidate, isValidating } = useCommandProfile()
const { data: profile, error, revalidate, isValidating } = useCommandProfile()
const handleSetProfile = useCallback(
async () =>
database && level !== undefined
Expand Down Expand Up @@ -141,7 +141,7 @@ export function ProfilingControlStack() {
styles={{
root: { height: 52, alignItems: 'center' },
}}>
{hosts.length > 1 ? (
{hosts.length > 1 && !error ? (
<>
<Label>Host:</Label>
<DefaultButton
Expand All @@ -167,19 +167,23 @@ export function ProfilingControlStack() {
</DefaultButton>
</>
) : null}
<Label>Level:</Label>
<Dropdown
selectedKey={level}
onChange={(_ev, option) => {
setLevel(option?.key as ProfilingLevel)
}}
styles={{ root: { width: 80 } }}
options={[
{ key: ProfilingLevel.OFF, text: 'Off' },
{ key: ProfilingLevel.SLOW, text: 'Slow' },
{ key: ProfilingLevel.ALL, text: 'All' },
]}
/>
{error ? null : (
<>
<Label>Level:</Label>
<Dropdown
selectedKey={level}
onChange={(_ev, option) => {
setLevel(option?.key as ProfilingLevel)
}}
styles={{ root: { width: 80 } }}
options={[
{ key: ProfilingLevel.OFF, text: 'Off' },
{ key: ProfilingLevel.SLOW, text: 'Slow' },
{ key: ProfilingLevel.ALL, text: 'All' },
]}
/>
</>
)}
{level === ProfilingLevel.SLOW ? (
<>
<SpinButton
Expand Down Expand Up @@ -220,7 +224,9 @@ export function ProfilingControlStack() {
profile?.slowms === slowms &&
profile?.sampleRate === sampleRate) ||
isValidating ? null : (
<PromiseButton icon="CheckMark" promise={promiseSetProfile} />
<div>
<PromiseButton icon="CheckMark" promise={promiseSetProfile} />
</div>
)}
<Stack.Item grow={true}>
<div />
Expand Down
96 changes: 65 additions & 31 deletions src/components/ProfilingList.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,64 @@
import { Stack, ContextualMenu, DirectionalHint } from '@fluentui/react'
import React, { useState } from 'react'
import { ContextualMenu, DirectionalHint, IColumn } from '@fluentui/react'
import React, { useState, useCallback } from 'react'
import { useSelector, useDispatch } from 'react-redux'

import { useCommandSystemProfileFind } from '@/hooks/use-command'
import { actions } from '@/stores'
import { ProfilingCard } from './ProfilingCard'
import { DisplayMode, MongoData } from '@/types'
import { LargeMessage } from './LargeMessage'
import { EditorModal } from './EditorModal'
import { Table } from './Table'
import { TableCell } from './TableCell'
import { ProfilingModal } from './ProfilingModal'

const order = [
'op',
'millis',
'ts',
'keysExamined',
'docsExamined',
'numYield',
'planSummary',
]

export function ProfilingList() {
const { data, error } = useCommandSystemProfileFind()
const collection = useSelector((state) => state.root.collection)
const invokedProfiling = useSelector(
(state) => state.profiling.invokedProfiling,
)
const isEditorOpen = useSelector((state) => state.profiling.isEditorOpen)
const isMenuHidden = useSelector((state) => state.profiling.isMenuHidden)
const dispatch = useDispatch()
const [target, setTarget] = useState<MouseEvent>()
const handleItemContextMenu = useCallback(
(ev: MouseEvent, item?: { [key: string]: MongoData }) => {
setTarget(ev)
if (item) {
dispatch(actions.profiling.setInvokedProfiling(item))
}
dispatch(actions.profiling.setIsMenuHidden(false))
},
[dispatch],
)
const handleItemInvoked = useCallback(
(item) => {
if (item) {
dispatch(actions.profiling.setInvokedProfiling(item))
}
dispatch(actions.profiling.setIsEditorOpen(true))
},
[dispatch],
)
const handleRenderItemColumn = useCallback(
(
item?: { [key: string]: MongoData },
_index?: number,
column?: IColumn,
) => {
return <TableCell value={item?.[column?.key!]} />
},
[],
)

if (error) {
return (
Expand All @@ -31,15 +73,16 @@ export function ProfilingList() {
}
return (
<>
<EditorModal
title="View Profile"
readOnly={true}
value={invokedProfiling}
isOpen={isEditorOpen}
onDismiss={() => {
dispatch(actions.profiling.setIsEditorOpen(false))
}}
/>
{invokedProfiling ? (
<ProfilingModal
title="View Profile"
value={invokedProfiling}
isOpen={isEditorOpen}
onDismiss={() => {
dispatch(actions.profiling.setIsEditorOpen(false))
}}
/>
) : null}
<ContextualMenu
target={target}
hidden={isMenuHidden}
Expand All @@ -59,24 +102,15 @@ export function ProfilingList() {
},
]}
/>
<Stack
tokens={{ childrenGap: 20 }}
styles={{
root: {
overflowY: 'scroll',
padding: 20,
flex: 1,
alignItems: 'center',
},
}}>
{data.cursor.firstBatch.map((item, index) => (
<ProfilingCard
key={index.toString()}
value={item}
onContectMenu={setTarget}
/>
))}
</Stack>
<Table
displayMode={DisplayMode.TABLE}
items={data.cursor.firstBatch}
order={collection === 'system.profile' ? ['ns', ...order] : order}
onlyOrder={true}
onItemContextMenu={handleItemContextMenu}
onItemInvoked={handleItemInvoked}
onRenderItemColumn={handleRenderItemColumn}
/>
</>
)
}
124 changes: 124 additions & 0 deletions src/components/ProfilingModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/* eslint-disable react/self-closing-comp */

import {
Modal,
IconButton,
getTheme,
Text,
Stack,
DefaultButton,
} from '@fluentui/react'
import React, { useState } from 'react'
import { omit } from 'lodash'

import { MongoData } from '@/types'
import { ColorizedData } from './ColorizedData'
import { ExecStage } from './ExecStage'

const tabs = ['execStats', 'command', 'locks']

export function ProfilingModal(props: {
title: string
value: { [key: string]: MongoData }
isOpen: boolean
onDismiss(): void
}) {
const theme = getTheme()
const [tab, setTab] = useState<string | undefined>(tabs[0])

return (
<>
<Modal
styles={{
scrollableContent: {
minWidth: 800,
minHeight: 600,
width: '80vw',
height: '80vh',
borderTop: `4px solid ${theme.palette.themePrimary}`,
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
backgroundColor: theme.palette.neutralLighterAlt,
},
}}
isOpen={props.isOpen}
onDismiss={props.onDismiss}
onDismissed={() => {
setTab(tabs[0])
}}>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: 10,
paddingLeft: 20,
}}>
<Text
variant="xLarge"
block={true}
styles={{
root: {
alignItems: 'center',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
overflow: 'hidden',
},
}}>
{props.title}
</Text>
<IconButton
styles={{ root: { marginLeft: 10 } }}
iconProps={{ iconName: 'Cancel' }}
onClick={props.onDismiss}
/>
</div>
<Stack
horizontal={true}
tokens={{ childrenGap: 10 }}
styles={{ root: { marginLeft: 20, marginRight: 20 } }}>
{tabs.map((t) => (
<DefaultButton
key={t}
text={t}
primary={tab === t}
onClick={() => {
setTab(t)
}}
/>
))}
<DefaultButton
text="other"
primary={tab === undefined}
onClick={() => {
setTab(undefined)
}}
/>
</Stack>
<div style={{ flex: 1, margin: 20, overflow: 'scroll' }}>
{tab === 'execStats' ? (
<div
style={{
display: 'flex',
flexDirection: 'row-reverse',
justifyContent: 'flex-end',
alignItems: 'center',
}}>
<ExecStage
value={props.value.execStats as { [key: string]: MongoData }}
/>
</div>
) : (
<ColorizedData
value={
tab === undefined ? omit(props.value, tabs) : props.value[tab]
}
style={{ marginBottom: 20 }}
/>
)}
</div>
</Modal>
</>
)
}
8 changes: 4 additions & 4 deletions src/components/PromiseButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function PromiseButton(props: {
}) {
const theme = getTheme()
const [hidden, setHidden] = useState(true)
const { rejected, pending, call } = props.promise
const { rejected, pending, call, reset } = props.promise
useEffect(() => {
if (rejected) {
setHidden(false)
Expand All @@ -39,9 +39,6 @@ export function PromiseButton(props: {
title: 'Error',
subText: rejected?.message,
showCloseButton: true,
onDismiss() {
setHidden(true)
},
}}
modalProps={{
styles: {
Expand All @@ -54,6 +51,9 @@ export function PromiseButton(props: {
onDismiss() {
setHidden(true)
},
onDismissed() {
reset()
},
}}>
<DialogFooter>
<DefaultButton
Expand Down
19 changes: 11 additions & 8 deletions src/components/Table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export function Table<T extends { [key: string]: MongoData }>(props: {
displayMode: DisplayMode
items: T[]
order?: string[]
onlyOrder?: boolean
onItemInvoked?(item: T): void
onItemContextMenu?(ev?: MouseEvent, item?: T): void
onRenderItemColumn(
Expand All @@ -40,13 +41,15 @@ export function Table<T extends { [key: string]: MongoData }>(props: {
return []
}
return props.displayMode === DisplayMode.TABLE
? calcHeaders(props.items, props.order).map(({ key, minWidth }) => ({
key,
name: key,
minWidth,
columnActionsMode: ColumnActionsMode.disabled,
isResizable: true,
}))
? calcHeaders(props.items, props.order, props.onlyOrder).map(
({ key, minWidth }) => ({
key,
name: key,
minWidth,
columnActionsMode: ColumnActionsMode.disabled,
isResizable: true,
}),
)
: [
{
key: '',
Expand All @@ -55,7 +58,7 @@ export function Table<T extends { [key: string]: MongoData }>(props: {
isMultiline: true,
},
]
}, [props.displayMode, props.items, props.order])
}, [props.displayMode, props.items, props.order, props.onlyOrder])
const handleRenderDetailsHeader = useCallback(
(detailsHeaderProps?: IDetailsHeaderProps) => (
<Sticky>
Expand Down
Loading

0 comments on commit 4b2b065

Please sign in to comment.