Skip to content

Commit

Permalink
feature: add screen to view mirror activity
Browse files Browse the repository at this point in the history
  • Loading branch information
Pankaj Bhageria authored and Pankaj Bhageria committed Nov 8, 2023
1 parent fb90f2a commit 6dda1f1
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 33 deletions.
93 changes: 68 additions & 25 deletions ui/app/mirrors/edit/[mirrorId]/cdcDetails.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,78 @@
'use client'
import { Label } from '@/lib/Label';
import {formatDistance } from 'date-fns'
import { Badge } from '@/lib/Badge';
import { Icon } from '@/lib/Icon';
import {Action} from '@/lib/Action';
import { FlowConnectionConfigs } from '@/grpc_generated/flow';
import CdcGraph from './cdcGraph'

type CDCDetailsProps = {
config: FlowConnectionConfigs | undefined;


type SyncStatusRow = {
batchId: number;
startTime: Date;
endTime: Date | null;
numRows: number;
};

export default function CDCDetails({ config }: CDCDetailsProps) {
if (!config) {
return <div className='text-red-500'>No configuration provided</div>;
}
type props = {
syncs: SyncStatusRow[];
mirrorConfig: FlowConnectionConfigs | undefined;
};
function CdcDetails({ syncs,mirrorConfig }:props) {

let lastSyncedAt = formatDistance(syncs[0]?.startTime, new Date(), { addSuffix: true })

let rowsSynced = syncs.reduce((acc, sync) => acc + sync.numRows, 0)

return (
<div className='p-4 rounded-md'>
<h2 className='text-xl font-semibold mb-4'>CDC Details</h2>
<div className='overflow-x-auto'>
<table className='min-w-full divide-y divide-gray-300'>
<tbody>
<tr>
<td className='px-4 py-2 font-medium'>Source</td>
<td className='px-4 py-2'>{config.source?.name || '-'}</td>
</tr>
<tr>
<td className='px-4 py-2 font-medium'>Destination</td>
<td className='px-4 py-2'>{config.destination?.name || '-'}</td>
</tr>
<tr>
<td className='px-4 py-2 font-medium'>Flow Job Name</td>
<td className='px-4 py-2'>{config.flowJobName}</td>
</tr>
</tbody>
</table>
<>
<div className='mt-10'>
<div className="flex flex-row">
<div className="basis-1/4 md:basis-1/3">
<div><Label variant="subheadline" colorName='lowContrast'>Status</Label></div>
<div><Label variant="body">
<Badge variant='positive' key={1}>
<Icon name='play_circle' />
Active
</Badge>
</Label>
</div>
</div>
<div className="basis-1/4 md:basis-1/3">
<div><Label variant="subheadline" colorName='lowContrast'>Mirror Type</Label></div>
<div><Label variant="body">CDC</Label></div>
</div>
<div className="basis-1/4 md:basis-1/3">
<div><Label variant="subheadline" colorName='lowContrast'>Source</Label></div>
<div><Action href={"/peers/"+mirrorConfig?.source?.name}>{mirrorConfig?.source?.name}</Action></div>
</div>
<div className="basis-1/4 md:basis-1/3">
<div><Label variant="subheadline" colorName='lowContrast'>Destination</Label></div>
<div><Action href={"/peers/"+ mirrorConfig?.destination?.name}>{mirrorConfig?.destination?.name}</Action></div>
</div>
</div>
<div className="flex flex-row mt-10">
<div className="basis-1/4">
<div><Label variant="subheadline" colorName='lowContrast'>Last Sync</Label></div>
<div><Label variant="body">{lastSyncedAt}</Label></div>
</div>
<div className="basis-1/4">
<div><Label variant="subheadline" colorName='lowContrast'>Rows synced</Label></div>
<div><Label variant="body">{numberWithCommas(rowsSynced)}</Label></div>
</div>
</div>
</div>
<div className='mt-10'>
<CdcGraph syncs={syncs}/>
</div>
</>
);
}

function numberWithCommas(x:Number) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

export default CdcDetails
82 changes: 82 additions & 0 deletions ui/app/mirrors/edit/[mirrorId]/cdcGraph.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Label } from '@/lib/Label';
import { useState, useEffect } from 'react';
import { format } from 'date-fns'

type SyncStatusRow = {
batchId: number;
startTime: Date;
endTime: Date | null;
numRows: number;
};

import aggregateCountsByInterval from './aggregatedCountsByInterval'

function CdcGraph({syncs}:{syncs:SyncStatusRow[]}) {

let [aggregateType,setAggregateType] = useState('hour');
let [counts,setCounts] = useState([]);


let rows = syncs.map((sync) => ({
timestamp: sync.startTime,
count: sync.numRows,
}))

useEffect(()=>{
let counts = aggregateCountsByInterval(rows,aggregateType, undefined, new Date());
counts = counts.slice(0,29)
counts = counts.reverse();
setCounts(counts)

},[aggregateType, rows])

return <div>
<div className='float-right'>
<button className={aggregateType === "15min" ? "bg-gray-200 px-1 mx-1 rounded-md":"px-1 mx-1"} onClick={()=>setAggregateType('15min')}>15 mins</button>
<button className={aggregateType === "hour" ? "bg-gray-200 px-1 mx-1 rounded-md":"px-1 mx-1"} onClick={()=>setAggregateType('hour')}>Hour</button>
<button className={aggregateType === "day" ? "bg-gray-200 px-1 mx-1 rounded-md":"px-1 mx-1"} onClick={()=>setAggregateType('day')}>Day</button>
<button className={aggregateType === "month" ? "bg-gray-200 px-1 mx-1 rounded-md":"px-1 mx-1"} onClick={()=>setAggregateType('month')}>Month</button>
</div>
<div><Label variant="body">Sync history</Label></div>

<div className='flex space-x-2 justify-left ml-2'>
{counts.map((count,i)=><GraphBar key={i} label={formatGraphLabel(new Date(count[0]),aggregateType)} count={count[1]}/>)}
</div>
</div>
}

type GraphBarProps = {
count: number | undefined;
label: string
};


function formatGraphLabel(date:Date, aggregateType:String) {
switch (aggregateType) {
case "15min":
return format(date, 'MMM dd HH:mm');
case "hour":
return format(date, 'MMM dd HH:mm');
case "day":
return format(date, 'MMM dd');
case "month":
return format(date, 'MMM yy');
}
}

function GraphBar({label,count}:GraphBarProps){
let color = count && count >0 ? 'bg-green-500' : 'bg-gray-500';
let classNames = `relative w-10 h-24 rounded ${color}`;
return <div className={"group"}>
<div className={classNames}>
<div className="group-hover:opacity-100 transition-opacity bg-gray-800 px-1 text-sm text-gray-100 rounded-md absolute left-1/2
-translate-x-1/2 translate-y-full opacity-0 m-4 mx-auto w-28 z-10 text-center">
<div>{label}</div>
<div>{count}</div>
</div>
</div>
</div>
}


export default CdcGraph;
35 changes: 29 additions & 6 deletions ui/app/mirrors/edit/[mirrorId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import { LayoutMain } from '@/lib/Layout';
import { GetFlowHttpAddressFromEnv } from '@/rpc/http';
import { redirect } from 'next/navigation';
import { Suspense } from 'react';
import { CDCMirror } from './cdc';
import CdcDetails from './cdcDetails';
import SyncStatus from './syncStatus';
import prisma from '@/app/utils/prisma';


export const dynamic = 'force-dynamic';

Expand Down Expand Up @@ -44,17 +46,38 @@ export default async function EditMirror({
redirect(`/mirrors/status/qrep/${mirrorId}`);
}

let syncs = await prisma.cdc_batches.findMany({
where: {
flow_name: mirrorId,
start_time: {
not: undefined,
},
},
orderBy: {
start_time: 'desc',
},
})

const rows = syncs.map((sync) => ({
batchId: sync.id,
startTime: sync.start_time,
endTime: sync.end_time,
numRows: sync.rows_in_batch,
}));

console.log("*********_________-------",mirrorStatus.cdcStatus.config);

return (
<LayoutMain alignSelf='flex-start' justifySelf='flex-start' width='full'>
<Header variant='title2'>{mirrorId}</Header>
<Suspense fallback={<Loading />}>
{mirrorStatus.cdcStatus && (
<CDCMirror
cdc={mirrorStatus.cdcStatus}
syncStatusChild={syncStatusChild}
/>
<CdcDetails syncs={rows} mirrorConfig={mirrorStatus.cdcStatus.config} />
)}
<div className='mt-10'>
{syncStatusChild}
</div>
</Suspense>
</LayoutMain>
);
}
}
5 changes: 4 additions & 1 deletion ui/app/mirrors/edit/[mirrorId]/syncStatus.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import prisma from '@/app/utils/prisma';
import { SyncStatusTable } from './syncStatusTable';
import NewCdcDetails from './newCdcDetails';

type SyncStatusProps = {
flowJobName: string | undefined;
Expand Down Expand Up @@ -29,5 +30,7 @@ export default async function SyncStatus({ flowJobName }: SyncStatusProps) {
numRows: sync.rows_in_batch,
}));

return <SyncStatusTable rows={rows} />;
return <div>
<SyncStatusTable rows={rows} />
</div>;
}
2 changes: 1 addition & 1 deletion ui/app/mirrors/edit/[mirrorId]/syncStatusTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export const SyncStatusTable = ({ rows }: SyncStatusTableProps) => {

return (
<Table
title={<Label variant='headline'>CDC Syncs</Label>}
title={<Label variant='headline'>Batches</Label>}
toolbar={{
left: (
<>
Expand Down
3 changes: 3 additions & 0 deletions ui/app/mirrors/status/timeline.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

//React component to show a timeline of sync events. Each bar on the time line will
//represent the number of

0 comments on commit 6dda1f1

Please sign in to comment.