Skip to content

Commit

Permalink
Adds sort buttons, graph for qrep (#763)
Browse files Browse the repository at this point in the history
<img width="886" alt="Screenshot 2023-12-06 at 4 38 37 PM"
src="https://github.com/PeerDB-io/peerdb/assets/65964360/9770d68c-e261-4dbd-b092-0fa277f6d3dc">

Changes default to Batch ID by descending in CDC page:
<img width="1521" alt="Screenshot 2023-12-06 at 4 41 38 PM"
src="https://github.com/PeerDB-io/peerdb/assets/65964360/14029590-e09a-465a-be19-157063a9e105">
  • Loading branch information
Amogh-Bharadwaj authored Dec 6, 2023
1 parent 1c92ac3 commit d137300
Show file tree
Hide file tree
Showing 9 changed files with 166 additions and 47 deletions.
10 changes: 9 additions & 1 deletion ui/app/mirrors/edit/[mirrorId]/aggregatedCountsByInterval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,15 @@ function aggregateCountsByInterval(

// Iterate through the timestamps and populate the aggregatedCounts object
for (let { timestamp, count } of timestamps) {
const date = roundUpToNearestNMinutes(timestamp, 1);
let N = 1;
if (interval === '1min') {
N = 1;
} else if (interval === '5min') {
N = 5;
} else if (interval === '15min') {
N = 15;
}
const date = roundUpToNearestNMinutes(timestamp, N);
const formattedTimestamp = moment(date).format(timeUnit);

if (!aggregatedCounts[formattedTimestamp]) {
Expand Down
23 changes: 20 additions & 3 deletions ui/app/mirrors/edit/[mirrorId]/cdc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export const SnapshotStatusTable = ({ status }: SnapshotStatusProps) => {
const [currentPage, setCurrentPage] = useState(1);
const totalPages = Math.ceil(allRows.length / ROWS_PER_PAGE);
const [searchQuery, setSearchQuery] = useState<string>('');
const [sortDir, setSortDir] = useState<'asc' | 'dsc'>('asc');
const displayedRows = useMemo(() => {
const shownRows = allRows.filter((row: any) =>
row.tableName.toLowerCase().includes(searchQuery.toLowerCase())
Expand All @@ -117,9 +118,9 @@ export const SnapshotStatusTable = ({ status }: SnapshotStatusProps) => {
}

if (aValue < bValue) {
return -1;
return sortDir === 'dsc' ? 1 : -1;
} else if (aValue > bValue) {
return 1;
return sortDir === 'dsc' ? -1 : 1;
} else {
return 0;
}
Expand All @@ -130,7 +131,7 @@ export const SnapshotStatusTable = ({ status }: SnapshotStatusProps) => {
return shownRows.length > ROWS_PER_PAGE
? shownRows.slice(startRow, endRow)
: shownRows;
}, [allRows, currentPage, searchQuery, sortField]);
}, [allRows, currentPage, searchQuery, sortField, sortDir]);

const handlePrevPage = () => {
if (currentPage > 1) {
Expand Down Expand Up @@ -183,6 +184,22 @@ export const SnapshotStatusTable = ({ status }: SnapshotStatusProps) => {
}}
defaultValue={{ value: 'cloneStartTime', label: 'Start Time' }}
/>
<button
className='IconButton'
onClick={() => setSortDir('asc')}
aria-label='sort up'
style={{ color: sortDir == 'asc' ? 'green' : 'gray' }}
>
<Icon name='arrow_upward' />
</button>
<button
className='IconButton'
onClick={() => setSortDir('dsc')}
aria-label='sort down'
style={{ color: sortDir == 'dsc' ? 'green' : 'gray' }}
>
<Icon name='arrow_downward' />
</button>
</>
),
right: (
Expand Down
30 changes: 3 additions & 27 deletions ui/app/mirrors/edit/[mirrorId]/cdcGraph.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
'use client';
import { formatGraphLabel, timeOptions } from '@/app/utils/graph';
import { Label } from '@/lib/Label';
import { BarChart } from '@tremor/react';
import moment from 'moment';
import { useEffect, useState } from 'react';
import ReactSelect from 'react-select';
import aggregateCountsByInterval from './aggregatedCountsByInterval';

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');
const initialCount: [string, number][] = [];
Expand All @@ -30,14 +30,6 @@ function CdcGraph({ syncs }: { syncs: SyncStatusRow[] }) {
setCounts(counts);
}, [aggregateType, syncs]);

const timeOptions = [
{ label: '1min', value: '1min' },
{ label: '5min', value: '5min' },
{ label: '15min', value: '15min' },
{ label: 'hour', value: 'hour' },
{ label: 'day', value: 'day' },
{ label: 'month', value: 'month' },
];
return (
<div>
<div className='float-right'>
Expand Down Expand Up @@ -65,20 +57,4 @@ function CdcGraph({ syncs }: { syncs: SyncStatusRow[] }) {
);
}

function formatGraphLabel(date: Date, aggregateType: String): string {
switch (aggregateType) {
case '1min':
case '5min':
case '15min':
case 'hour':
return moment(date).format('MMM Do HH:mm');
case 'day':
return moment(date).format('MMM Do');
case 'month':
return moment(date).format('MMM yy');
default:
return 'Unknown aggregate type: ' + aggregateType;
}
}

export default CdcGraph;
37 changes: 29 additions & 8 deletions ui/app/mirrors/edit/[mirrorId]/syncStatusTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,16 +50,18 @@ function TimeWithDurationOrRunning({

const ROWS_PER_PAGE = 5;
const sortOptions = [
{ value: 'batchId', label: 'Batch ID' },
{ value: 'startTime', label: 'Start Time' },
{ value: 'endTime', label: 'End Time' },
{ value: 'numRows', label: 'Rows Synced' },
];
export const SyncStatusTable = ({ rows }: SyncStatusTableProps) => {
const [currentPage, setCurrentPage] = useState(1);
const [sortField, setSortField] = useState<
'startTime' | 'endTime' | 'numRows'
>('startTime');
'startTime' | 'endTime' | 'numRows' | 'batchId'
>('batchId');

const [sortDir, setSortDir] = useState<'asc' | 'dsc'>('dsc');
const totalPages = Math.ceil(rows.length / ROWS_PER_PAGE);
const [searchQuery, setSearchQuery] = useState<string>('');
const displayedRows = useMemo(() => {
Expand All @@ -75,9 +77,9 @@ export const SyncStatusTable = ({ rows }: SyncStatusTableProps) => {
}

if (aValue < bValue) {
return -1;
return sortDir === 'dsc' ? 1 : -1;
} else if (aValue > bValue) {
return 1;
return sortDir === 'dsc' ? -1 : 1;
} else {
return 0;
}
Expand All @@ -88,7 +90,7 @@ export const SyncStatusTable = ({ rows }: SyncStatusTableProps) => {
return shownRows.length > ROWS_PER_PAGE
? shownRows.slice(startRow, endRow)
: shownRows;
}, [searchQuery, currentPage, rows, sortField]);
}, [searchQuery, currentPage, rows, sortField, sortDir]);

const handlePrevPage = () => {
if (currentPage > 1) {
Expand Down Expand Up @@ -130,12 +132,31 @@ export const SyncStatusTable = ({ rows }: SyncStatusTableProps) => {
}}
onChange={(val, _) => {
const sortVal =
(val?.value as 'startTime' | 'endTime' | 'numRows') ??
'startTime';
(val?.value as
| 'startTime'
| 'endTime'
| 'numRows'
| 'batchId') ?? 'batchId';
setSortField(sortVal);
}}
defaultValue={{ value: 'startTime', label: 'Start Time' }}
defaultValue={{ value: 'batchId', label: 'Batch ID' }}
/>
<button
className='IconButton'
onClick={() => setSortDir('asc')}
aria-label='sort up'
style={{ color: sortDir == 'asc' ? 'green' : 'gray' }}
>
<Icon name='arrow_upward' />
</button>
<button
className='IconButton'
onClick={() => setSortDir('dsc')}
aria-label='sort down'
style={{ color: sortDir == 'dsc' ? 'green' : 'gray' }}
>
<Icon name='arrow_downward' />
</button>
</>
),
right: (
Expand Down
10 changes: 10 additions & 0 deletions ui/app/mirrors/status/qrep/[mirrorId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import prisma from '@/app/utils/prisma';
import { Header } from '@/lib/Header';
import { LayoutMain } from '@/lib/Layout';
import QRepConfigViewer from './qrepConfigViewer';
import QrepGraph from './qrepGraph';
import QRepStatusTable, { QRepPartitionStatus } from './qrepStatusTable';

export const dynamic = 'force-dynamic';
Expand Down Expand Up @@ -44,6 +45,15 @@ export default async function QRepMirrorStatus({
<LayoutMain alignSelf='flex-start' justifySelf='flex-start' width='full'>
<Header variant='title2'>{mirrorId}</Header>
<QRepConfigViewer mirrorId={mirrorId} />
<QrepGraph
syncs={partitions.map((partition) => ({
partitionID: partition.partitionId,
startTime: partition.startTime,
endTime: partition.endTime,
numRows: partition.numRows,
}))}
/>
<br></br>
<QRepStatusTable flowJobName={mirrorId} partitions={partitions} />
</LayoutMain>
);
Expand Down
2 changes: 1 addition & 1 deletion ui/app/mirrors/status/qrep/[mirrorId]/qrepConfigViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default async function QRepConfigViewer({

return (
<div className='my-4'>
<Badge variant='warning' type='longText'>
<Badge type='longText'>
<Icon name={qrepConfig.initialCopyOnly ? 'double_arrow' : 'sync'} />
<div className='font-bold'>
{qrepConfig.initialCopyOnly ? 'Initial Load' : 'Continuous Sync'}
Expand Down
60 changes: 60 additions & 0 deletions ui/app/mirrors/status/qrep/[mirrorId]/qrepGraph.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
'use client';
import { formatGraphLabel, timeOptions } from '@/app/utils/graph';
import { Label } from '@/lib/Label';
import { BarChart } from '@tremor/react';
import { useEffect, useState } from 'react';
import ReactSelect from 'react-select';
import aggregateCountsByInterval from '../../../edit/[mirrorId]/aggregatedCountsByInterval';

type QrepStatusRow = {
partitionID: string;
startTime: Date | null;
endTime: Date | null;
numRows: number | null;
};

function QrepGraph({ syncs }: { syncs: QrepStatusRow[] }) {
let [aggregateType, setAggregateType] = useState('hour');
const initialCount: [string, number][] = [];
let [counts, setCounts] = useState(initialCount);

useEffect(() => {
let rows = syncs.map((sync) => ({
timestamp: sync.startTime!,
count: sync.numRows ?? 0,
}));

let counts = aggregateCountsByInterval(rows, aggregateType);
counts = counts.slice(0, 29);
counts = counts.reverse();
setCounts(counts);
}, [aggregateType, syncs]);

return (
<div>
<div className='float-right'>
<ReactSelect
id={aggregateType}
placeholder='Select a timeframe'
options={timeOptions}
defaultValue={{ label: 'hour', value: 'hour' }}
onChange={(val, _) => val && setAggregateType(val.value)}
/>
</div>
<div style={{ height: '3rem' }}>
<Label variant='headline'>Partition sync history</Label>
</div>
<BarChart
className='mt-3'
data={counts.map((count) => ({
name: formatGraphLabel(new Date(count[0]), aggregateType),
'Rows synced at a point in time': count[1],
}))}
index='name'
categories={['Rows synced at a point in time']}
/>
</div>
);
}

export default QrepGraph;
15 changes: 8 additions & 7 deletions ui/app/mirrors/status/qrep/[mirrorId]/qrepStatusTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,14 @@ function RowPerPartition({
return (
<TableRow key={partitionId}>
<TableCell>
<Label>{partitionId}</Label>
<Label as='label' style={{ fontSize: 15 }}>
{partitionId}
</Label>
</TableCell>
<TableCell>
<Label>{runUuid}</Label>
<Label as='label' style={{ fontSize: 15 }}>
{runUuid}
</Label>
</TableCell>
<TableCell>
<Label>{duration}</Label>
Expand Down Expand Up @@ -107,7 +111,7 @@ export default function QRepStatusTable({

return (
<Table
title={<Label variant='headline'>Progress</Label>}
title={<Label>Progress</Label>}
toolbar={{
left: (
<>
Expand All @@ -131,9 +135,6 @@ export default function QRepStatusTable({
>
<Icon name='refresh' />
</Button>
<Button variant='normalBorderless'>
<Icon name='help' />
</Button>
<Button variant='normalBorderless' disabled>
<Icon name='download' />
</Button>
Expand All @@ -146,7 +147,7 @@ export default function QRepStatusTable({
),
right: (
<SearchField
placeholder='Search by partition UUID'
placeholder='Search by partition'
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
setSearchQuery(e.target.value)
}
Expand Down
26 changes: 26 additions & 0 deletions ui/app/utils/graph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import moment from 'moment';

export const timeOptions = [
{ label: '1min', value: '1min' },
{ label: '5min', value: '5min' },
{ label: '15min', value: '15min' },
{ label: 'hour', value: 'hour' },
{ label: 'day', value: 'day' },
{ label: 'month', value: 'month' },
];

export function formatGraphLabel(date: Date, aggregateType: String): string {
switch (aggregateType) {
case '1min':
case '5min':
case '15min':
case 'hour':
return moment(date).format('MMM Do HH:mm');
case 'day':
return moment(date).format('MMM Do');
case 'month':
return moment(date).format('MMM yy');
default:
return 'Unknown aggregate type: ' + aggregateType;
}
}

0 comments on commit d137300

Please sign in to comment.