Skip to content

Commit

Permalink
vtadmin: Initiate workflow details tab
Browse files Browse the repository at this point in the history
Signed-off-by: Noble Mittal <[email protected]>
  • Loading branch information
beingnoble03 committed Aug 9, 2024
1 parent cf8f5d1 commit 749b607
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 0 deletions.
6 changes: 6 additions & 0 deletions web/vtadmin/src/components/routes/workflow/Workflow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { WorkspaceTitle } from '../../layout/WorkspaceTitle';
import { useDocumentTitle } from '../../../hooks/useDocumentTitle';
import { KeyspaceLink } from '../../links/KeyspaceLink';
import { WorkflowStreams } from './WorkflowStreams';
import { WorkflowDetails } from './WorkflowDetails';
import { ContentContainer } from '../../layout/ContentContainer';
import { TabContainer } from '../../tabs/TabContainer';
import { Tab } from '../../tabs/Tab';
Expand Down Expand Up @@ -74,6 +75,7 @@ export const Workflow = () => {
<ContentContainer>
<TabContainer>
<Tab text="Streams" to={`${url}/streams`} count={streams.length} />
<Tab text="Details" to={`${url}/details`} />
<Tab text="JSON" to={`${url}/json`} />
</TabContainer>

Expand All @@ -82,6 +84,10 @@ export const Workflow = () => {
<WorkflowStreams clusterID={clusterID} keyspace={keyspace} name={name} />
</Route>

<Route path={`${path}/details`}>
<WorkflowDetails clusterID={clusterID} keyspace={keyspace} name={name} />
</Route>

<Route path={`${path}/json`}>
<Code code={JSON.stringify(data, null, 2)} />
</Route>
Expand Down
155 changes: 155 additions & 0 deletions web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/**
* Copyright 2024 The Vitess Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { groupBy, orderBy } from "lodash-es";

Check warning on line 17 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / lint

'groupBy' is defined but never used

Check warning on line 17 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / build

'groupBy' is defined but never used
import React, { useMemo } from "react";
import { Link } from "react-router-dom";

import { useWorkflow, useWorkflows } from "../../../hooks/api";
import { formatAlias } from "../../../util/tablets";

Check warning on line 22 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / lint

'formatAlias' is defined but never used

Check warning on line 22 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / build

'formatAlias' is defined but never used
import { formatDateTime, formatRelativeTime } from "../../../util/time";

Check warning on line 23 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / lint

'formatRelativeTime' is defined but never used

Check warning on line 23 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / build

'formatRelativeTime' is defined but never used
import {
formatStreamKey,
getReverseWorkflow,
getStreams,
getStreamSource,

Check warning on line 28 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / lint

'getStreamSource' is defined but never used

Check warning on line 28 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / build

'getStreamSource' is defined but never used
getStreamTarget,

Check warning on line 29 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / lint

'getStreamTarget' is defined but never used

Check warning on line 29 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / build

'getStreamTarget' is defined but never used
} from "../../../util/workflows";
import { DataTable } from "../../dataTable/DataTable";
import { vtctldata } from "../../../proto/vtadmin";
import { DataCell } from "../../dataTable/DataCell";
import { StreamStatePip } from "../../pips/StreamStatePip";
import { ThrottleThresholdSeconds } from "../Workflows";
import { workerData } from "worker_threads";

Check warning on line 36 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / lint

'workerData' is defined but never used

Check warning on line 36 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / build

'workerData' is defined but never used

interface Props {
clusterID: string;
keyspace: string;
name: string;
}

export const WorkflowDetails = ({ clusterID, keyspace, name }: Props) => {
const { data } = useWorkflow({ clusterID, keyspace, name });

const { data: workflowsData = [] } = useWorkflows();

const streams = useMemo(() => {
const rows = getStreams(data).map((stream) => ({
key: formatStreamKey(stream),
...stream,
}));

return orderBy(rows, "streamKey");
}, [data]);

const renderRows = (rows: vtctldata.Workflow.Stream.ILog[]) => {
return rows.map((row) => {
let message = row.message ? `${row.message}` : "-";
// TODO(@beingnoble03): Investigate how message can be parsed and displayed to JSON in case of "Stream Created"
if (row.type == "Stream Created") {

Check warning on line 62 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / lint

Expected '===' and instead saw '=='

Check warning on line 62 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / build

Expected '===' and instead saw '=='
message = "-";
}
return (
<tr key={`${row.id}`}>
<DataCell>{`${row.type}`}</DataCell>
<DataCell>{`${row.state}`}</DataCell>
<DataCell>{`${formatDateTime(
parseInt(`${row.updated_at?.seconds}`, 10)
)}`}</DataCell>
<DataCell>{message}</DataCell>
<DataCell>{`${row.count}`}</DataCell>
</tr>
);
});
};

const reverseWorkflow = getReverseWorkflow(workflowsData, data);

return (
<div className="mt-12 mb-16">
{reverseWorkflow && (
<div>
<h3 className="my-8">Reverse Workflow</h3>
<div className="font-bold font-mono text-lg">
<Link
to={`/workflow/${reverseWorkflow.cluster?.id}/${reverseWorkflow.keyspace}/${reverseWorkflow.workflow?.name}`}
>
{reverseWorkflow.workflow?.name}
</Link>
</div>
<p className="text-base">
<strong>Keyspace</strong> <br />
<Link
to={`/keyspace/${reverseWorkflow.cluster?.id}/${reverseWorkflow.keyspace}`}
>
{`${reverseWorkflow.keyspace}`}
</Link>
</p>
{reverseWorkflow.workflow?.max_v_replication_lag && (
<p className="text-base">
<strong>Max VReplication Lag</strong> <br />
{`${reverseWorkflow.workflow?.max_v_replication_lag}`}
</p>
)}
</div>
)}
<h3 className="my-8">Streams</h3>
{streams.map((stream) => {
const href =
stream.tablet && stream.id
? `/workflow/${clusterID}/${keyspace}/${name}/stream/${stream.tablet.cell}/${stream.tablet.uid}/${stream.id}`
: null;

var isThrottled =
Number(stream.throttler_status?.time_throttled?.seconds) >
Date.now() / 1000 - ThrottleThresholdSeconds;
const streamState = isThrottled ? "Throttled" : stream.state;
return (
<div className="my-8">
<div className="text-lg font-bold">
<StreamStatePip state={streamState} />{" "}
<Link to={href}>{`${stream.key}`}</Link>
</div>
<p className="text-base">
<strong>State</strong> <br />
{streamState}
</p>
{isThrottled && (
<p className="text-base">
<strong>Component Throttled</strong> <br />
{stream.throttler_status?.component_throttled}
</p>
)}
{streamState == "Running" &&

Check warning on line 136 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / lint

Expected '===' and instead saw '=='

Check warning on line 136 in web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx

View workflow job for this annotation

GitHub Actions / build

Expected '===' and instead saw '=='
data?.workflow?.max_v_replication_lag && (
<p className="text-base">
<strong>VReplication Lag</strong> <br />
{`${data?.workflow?.max_v_replication_lag}`}
</p>
)}
<DataTable
columns={["Type", "State", "Updated At", "Message", "Count"]}
data={stream.logs?.reverse()!}
renderRows={renderRows}
pageSize={1000}
title="Recent Logs"
/>
</div>
);
})}
</div>
);
};
20 changes: 20 additions & 0 deletions web/vtadmin/src/util/workflows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,23 @@ export const getStreamTablets = <W extends pb.IWorkflow>(workflow: W | null | un

return [...aliases];
};

/**
* getReverseWorkflow returns the reverse workflow of `originalWorkflow` by looking for the '_reverse'
* suffix and the source and target keyspace from all `workflows` list.
*/
export const getReverseWorkflow = <W extends pb.Workflow>(
workflows: W[],
originalWorkflow: W | undefined | null
): W | undefined => {
if (!originalWorkflow) return;
const originalWorkflowName = originalWorkflow.workflow?.name!;
let reverseWorkflowName = originalWorkflowName.concat("_reverse");
if (originalWorkflowName.endsWith("_reverse")) {
reverseWorkflowName = originalWorkflowName.split("_reverse")[0];
}
return workflows.find((workflow) =>
workflow.workflow?.name === reverseWorkflowName &&
workflow.workflow?.source?.keyspace === originalWorkflow.workflow?.target?.keyspace &&
workflow.workflow?.target?.keyspace === originalWorkflow.workflow?.source?.keyspace);
};

0 comments on commit 749b607

Please sign in to comment.