Skip to content

Commit

Permalink
VTAdmin(web): Some tweaks in the workflow details (#16706)
Browse files Browse the repository at this point in the history
Signed-off-by: Noble Mittal <noblemittal@outlook.com>
  • Loading branch information
beingnoble03 committed Sep 8, 2024
1 parent 10710d8 commit 53dbede
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 142 deletions.
60 changes: 54 additions & 6 deletions web/vtadmin/src/components/routes/workflow/Workflow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Link, Redirect, Route, Switch, useParams, useRouteMatch } from 'react-router-dom';
import { Link, Redirect, Route, Switch, useLocation, useParams, useRouteMatch } from 'react-router-dom';

import style from './Workflow.module.scss';

Expand All @@ -30,6 +30,7 @@ import { TabContainer } from '../../tabs/TabContainer';
import { Tab } from '../../tabs/Tab';
import { getStreams } from '../../../util/workflows';
import { Code } from '../../Code';
import { ShardLink } from '../../links/ShardLink';

interface RouteParams {
clusterID: string;
Expand All @@ -40,12 +41,21 @@ interface RouteParams {
export const Workflow = () => {
const { clusterID, keyspace, name } = useParams<RouteParams>();
const { path, url } = useRouteMatch();
const location = useLocation();

useDocumentTitle(`${name} (${keyspace})`);

const { data } = useWorkflow({ clusterID, keyspace, name });
const streams = getStreams(data);

const detailsURL = `${url}/details`;
const detailsTab = location.pathname === detailsURL;

let isReshard = false;
if (data && data.workflow) {
isReshard = data.workflow.workflow_type === 'Reshard';
}

return (
<div>
<WorkspaceHeader>
Expand All @@ -54,28 +64,66 @@ export const Workflow = () => {
</NavCrumbs>

<WorkspaceTitle className="font-mono">{name}</WorkspaceTitle>
{isReshard && (
<div className={style.headingMetaContainer}>
<div className={style.headingMeta}>
<span>
{data?.workflow?.source?.shards?.length! > 1 ? 'Source Shards: ' : 'Source Shard: '}
{data?.workflow?.source?.shards?.map((shard) => (
<code key={`${keyspace}/${shard}`}>
<ShardLink
className="mr-1"
clusterID={clusterID}
keyspace={keyspace}
shard={shard}
>
{`${keyspace}/${shard}`}
</ShardLink>
</code>
))}
</span>
<span>
{data?.workflow?.target?.shards?.length! > 1 ? 'Target Shards: ' : 'Target Shard: '}
{data?.workflow?.target?.shards?.map((shard) => (
<code key={`${keyspace}/${shard}`}>
<ShardLink
className="mr-1"
clusterID={clusterID}
keyspace={keyspace}
shard={shard}
>
{`${keyspace}/${shard}`}
</ShardLink>
</code>
))}
</span>
</div>
</div>
)}
<div className={style.headingMetaContainer}>
<div className={style.headingMeta} style={{ float: 'left' }}>
<span>
Cluster: <code>{clusterID}</code>
</span>
<span>
Target keyspace:{' '}
Target Keyspace:{' '}
<KeyspaceLink clusterID={clusterID} name={keyspace}>
<code>{keyspace}</code>
</KeyspaceLink>
</span>
</div>
<div style={{ float: 'right' }}>
<a href={`#workflowStreams`}>Scroll To Streams</a>
</div>
{detailsTab && (
<div style={{ float: 'right' }}>
<a href={`#workflowStreams`}>Scroll To Streams</a>
</div>
)}
</div>
</WorkspaceHeader>

<ContentContainer>
<TabContainer>
<Tab text="Streams" to={`${url}/streams`} count={streams.length} />
<Tab text="Details" to={`${url}/details`} />
<Tab text="Details" to={detailsURL} />
<Tab text="JSON" to={`${url}/json`} />
</TabContainer>

Expand Down
97 changes: 73 additions & 24 deletions web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ import React, { useMemo } from 'react';
import { Link } from 'react-router-dom';

import { useWorkflow, useWorkflowStatus, useWorkflows } from '../../../hooks/api';
import { formatDateTime } from '../../../util/time';
import { formatDateTimeShort } from '../../../util/time';
import {
TableCopyState,
formatStreamKey,
getReverseWorkflow,
getStreamSource,
getStreamTarget,
getStreams,
getTableCopyStates,
} from '../../../util/workflows';
Expand All @@ -32,6 +34,10 @@ import { vtctldata } from '../../../proto/vtadmin';
import { DataCell } from '../../dataTable/DataCell';
import { StreamStatePip } from '../../pips/StreamStatePip';
import { ThrottleThresholdSeconds } from '../Workflows';
import { ShardLink } from '../../links/ShardLink';
import { Tooltip } from '../../tooltip/Tooltip';
import { TabletLink } from '../../links/TabletLink';
import { formatAlias } from '../../../util/tablets';

interface Props {
clusterID: string;
Expand All @@ -45,7 +51,7 @@ const LOG_COLUMNS = ['Type', 'State', 'Updated At', 'Message', 'Count'];

const TABLE_COPY_STATE_COLUMNS = ['Table Name', 'Total Bytes', 'Bytes Copied', 'Total Rows', 'Rows Copied'];

const STREAM_COLUMNS = ['Stream', 'State', 'Message', 'Transaction Timestamp', 'Database Name'];
const STREAM_COLUMNS = ['Stream', 'Source Shard', 'Target Shard', 'Message', 'Transaction Timestamp', 'Database Name'];

export const WorkflowDetails = ({ clusterID, keyspace, name }: Props) => {
const { data: workflowData } = useWorkflow({ clusterID, keyspace, name });
Expand Down Expand Up @@ -112,15 +118,20 @@ export const WorkflowDetails = ({ clusterID, keyspace, name }: Props) => {
const renderSummaryRows = (rows: (typeof workflowSummary)[]) => {
return rows.map((row) => {
const reverseWorkflow = row.reverseWorkflow;
let maxVReplicationLag = '-';
if (row.workflowData && row.workflowData.workflow?.max_v_replication_lag) {
maxVReplicationLag = `${row.workflowData.workflow?.max_v_replication_lag}`;
if (maxVReplicationLag === '1') {
maxVReplicationLag += ' second';
} else {
maxVReplicationLag += ' seconds';
}
}
return (
<tr key={reverseWorkflow?.workflow?.name}>
<DataCell>{row.streamSummary ? row.streamSummary : '-'}</DataCell>
<DataCell>{row.workflowStatus ? row.workflowStatus.traffic_state : '-'}</DataCell>
<DataCell>
{row.workflowData && row.workflowData.workflow?.max_v_replication_lag
? `${row.workflowData.workflow?.max_v_replication_lag}`
: '-'}
</DataCell>
<DataCell>{maxVReplicationLag}</DataCell>
<DataCell>
{reverseWorkflow ? (
<Link
Expand All @@ -140,6 +151,8 @@ export const WorkflowDetails = ({ clusterID, keyspace, name }: Props) => {

const renderStreamRows = (rows: typeof streams) => {
return rows.map((row) => {
const source = getStreamSource(row);
const target = getStreamTarget(row, keyspace);
const href =
row.tablet && row.id
? `/workflow/${clusterID}/${keyspace}/${name}/stream/${row.tablet.cell}/${row.tablet.uid}/${row.id}`
Expand All @@ -151,12 +164,22 @@ export const WorkflowDetails = ({ clusterID, keyspace, name }: Props) => {
return (
<tr key={row.key}>
<DataCell>
<StreamStatePip state={rowState} />{' '}
<Tooltip text={rowState!}>
<span>
<StreamStatePip state={rowState} />{' '}
</span>
</Tooltip>
<Link className="font-bold" to={href}>
{row.key}
</Link>
<div className="text-sm text-secondary">
Updated {formatDateTime(row.time_updated?.seconds)}
Tablet{' '}
<TabletLink alias={formatAlias(row.tablet)} clusterID={clusterID}>
{formatAlias(row.tablet)}
</TabletLink>
</div>
<div className="text-sm text-secondary">
Updated {formatDateTimeShort(row.time_updated?.seconds)}
</div>
{isThrottled ? (
<div className="text-sm text-secondary">
Expand All @@ -165,11 +188,32 @@ export const WorkflowDetails = ({ clusterID, keyspace, name }: Props) => {
</div>
) : null}
</DataCell>
<DataCell>{rowState}</DataCell>
<DataCell>
{source ? (
<ShardLink
clusterID={clusterID}
keyspace={row.binlog_source?.keyspace}
shard={row.binlog_source?.shard}
>
{source}
</ShardLink>
) : (
'-'
)}
</DataCell>
<DataCell>
{target ? (
<ShardLink clusterID={clusterID} keyspace={keyspace} shard={row.shard}>
{target}
</ShardLink>
) : (
'-'
)}
</DataCell>
<DataCell>{row.message ? row.message : '-'}</DataCell>
<DataCell>
{row.transaction_timestamp && row.transaction_timestamp.seconds
? formatDateTime(row.transaction_timestamp.seconds)
? `${formatDateTimeShort(row.transaction_timestamp.seconds)}`
: '-'}
</DataCell>
<DataCell>{row.db_name}</DataCell>
Expand All @@ -189,7 +233,7 @@ export const WorkflowDetails = ({ clusterID, keyspace, name }: Props) => {
<tr key={`${row.id}`}>
<DataCell>{`${row.type}`}</DataCell>
<DataCell>{`${row.state}`}</DataCell>
<DataCell>{`${formatDateTime(parseInt(`${row.updated_at?.seconds}`, 10))}`}</DataCell>
<DataCell>{`${formatDateTimeShort(parseInt(`${row.updated_at?.seconds}`, 10))}`}</DataCell>
<DataCell>{message}</DataCell>
<DataCell>{`${row.count}`}</DataCell>
</tr>
Expand Down Expand Up @@ -219,14 +263,15 @@ export const WorkflowDetails = ({ clusterID, keyspace, name }: Props) => {
};

return (
<div className="mt-12 mb-16">
<div className="mt-8 mb-16">
<DataTable
columns={SUMMARY_COLUMNS}
data={[workflowSummary]}
renderRows={renderSummaryRows}
pageSize={1}
title="Summary"
/>
<span id="workflowStreams"></span>
<DataTable
columns={STREAM_COLUMNS}
data={streams}
Expand All @@ -244,17 +289,21 @@ export const WorkflowDetails = ({ clusterID, keyspace, name }: Props) => {
/>
)}
<h3 className="mt-8 mb-4">Recent Logs</h3>
{streams.map((stream) => (
<div className="mt-2" key={stream.key}>
<DataTable
columns={LOG_COLUMNS}
data={orderBy(stream.logs, 'id', 'desc')}
renderRows={renderLogRows}
pageSize={10}
title={stream.key!}
/>
</div>
))}
{streams.length <= 8 ? (
streams.map((stream) => (
<div className="mt-2" key={stream.key}>
<DataTable
columns={LOG_COLUMNS}
data={orderBy(stream.logs, 'id', 'desc')}
renderRows={renderLogRows}
pageSize={10}
title={stream.key!}
/>
</div>
))
) : (
<span>Recent logs from streams are not displayed due to the large number of shards.</span>
)}
</div>
);
};
Loading

0 comments on commit 53dbede

Please sign in to comment.