Skip to content

Commit

Permalink
UI: Resync button (#1178)
Browse files Browse the repository at this point in the history
Introduces a resync button in the specific mirror page which when
clicked opens a confirmation dialog box.
The resync when done via UI:
- first calls drop mirror and drops the mirror
- kicks off a cdc mirror with the same config as the current mirror,
with `resync:true` added
![Screenshot 2024-01-30 at 6 46
50 PM](https://github.com/PeerDB-io/peerdb/assets/65964360/c6ea2156-2e66-4401-9d3b-a7658e04a5a8)
  • Loading branch information
Amogh-Bharadwaj authored Jan 30, 2024
1 parent 9871d73 commit cfab700
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 26 deletions.
6 changes: 6 additions & 0 deletions flow/cmd/validate_mirror.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import (
func (h *FlowRequestHandler) ValidateCDCMirror(
ctx context.Context, req *protos.CreateCDCFlowRequest,
) (*protos.ValidateCDCMirrorResponse, error) {
if req.ConnectionConfigs == nil {
slog.Error("/validatecdc connection configs is nil")
return &protos.ValidateCDCMirrorResponse{
Ok: false,
}, fmt.Errorf("connection configs is nil")
}
sourcePeerConfig := req.ConnectionConfigs.Source.GetPostgresConfig()
if sourcePeerConfig == nil {
slog.Error("/validatecdc source peer config is nil", slog.Any("peer", req.ConnectionConfigs.Source))
Expand Down
26 changes: 24 additions & 2 deletions ui/app/mirrors/edit/[mirrorId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { SyncStatusRow } from '@/app/dto/MirrorsDTO';
import prisma from '@/app/utils/prisma';
import { ResyncDialog } from '@/components/ResyncDialog';
import { FlowConnectionConfigs } from '@/grpc_generated/flow';
import { MirrorStatusResponse } from '@/grpc_generated/route';
import { Header } from '@/lib/Header';
import { LayoutMain } from '@/lib/Layout';
Expand All @@ -25,7 +27,7 @@ async function getMirrorStatus(mirrorId: string) {
return json;
}

export default async function EditMirror({
export default async function ViewMirror({
params: { mirrorId },
}: EditMirrorProps) {
const mirrorStatus: MirrorStatusResponse = await getMirrorStatus(mirrorId);
Expand All @@ -37,6 +39,7 @@ export default async function EditMirror({
select: {
created_at: true,
workflow_id: true,
config_proto: true,
},
where: {
name: mirrorId,
Expand Down Expand Up @@ -67,6 +70,10 @@ export default async function EditMirror({
return <NoMirror />;
}

if (!mirrorInfo) {
return <div>No mirror info found</div>;
}
const mirrorConfig = FlowConnectionConfigs.decode(mirrorInfo.config_proto!);
let syncStatusChild = <></>;
if (mirrorStatus.cdcStatus) {
let rowsSynced = syncs.reduce((acc, sync) => {
Expand All @@ -84,7 +91,22 @@ export default async function EditMirror({

return (
<LayoutMain alignSelf='flex-start' justifySelf='flex-start' width='full'>
<Header variant='title2'>{mirrorId}</Header>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
paddingRight: '2rem',
}}
>
<div style={{ display: 'flex', alignItems: 'center' }}>
<Header variant='title2'>{mirrorId}</Header>
</div>
<ResyncDialog
mirrorConfig={mirrorConfig}
workflowId={mirrorInfo.workflow_id || ''}
/>
</div>
<CDCMirror
rows={rows}
createdAt={mirrorInfo?.created_at}
Expand Down
63 changes: 39 additions & 24 deletions ui/components/DropDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import { Dialog, DialogClose } from '@/lib/Dialog';
import { Icon } from '@/lib/Icon';
import { Label } from '@/lib/Label';
import { Divider } from '@tremor/react';
import { useState } from 'react';
import { Dispatch, SetStateAction, useState } from 'react';
import { BarLoader } from 'react-spinners';

interface dropMirrorArgs {
workflowId: string | null;
flowJobName: string;
sourcePeer: Peer;
destinationPeer: Peer;
forResync?: boolean;
}

interface dropPeerArgs {
Expand All @@ -25,6 +26,38 @@ interface deleteAlertArgs {
id: number | bigint;
}

export const handleDropMirror = async (
dropArgs: dropMirrorArgs,
setLoading: Dispatch<SetStateAction<boolean>>,
setMsg: Dispatch<SetStateAction<string>>
) => {
if (!dropArgs.workflowId) {
setMsg('Workflow ID not found for this mirror.');
return false;
}
setLoading(true);
const dropRes: UDropMirrorResponse = await fetch('/api/mirrors/drop', {
method: 'POST',
body: JSON.stringify(dropArgs),
}).then((res) => res.json());
setLoading(false);
if (dropRes.dropped !== true) {
setMsg(
`Unable to drop mirror ${dropArgs.flowJobName}. ${
dropRes.errorMessage ?? ''
}`
);
return false;
}

setMsg('Mirror dropped successfully.');
if (!dropArgs.forResync) {
window.location.reload();
}

return true;
};

export const DropDialog = ({
mode,
dropArgs,
Expand All @@ -34,28 +67,6 @@ export const DropDialog = ({
}) => {
const [loading, setLoading] = useState(false);
const [msg, setMsg] = useState('');
const handleDropMirror = async (dropArgs: dropMirrorArgs) => {
if (!dropArgs.workflowId) {
setMsg('Workflow ID not found for this mirror.');
return;
}
setLoading(true);
const dropRes: UDropMirrorResponse = await fetch('api/mirrors/drop', {
method: 'POST',
body: JSON.stringify(dropArgs),
}).then((res) => res.json());
setLoading(false);
if (dropRes.dropped !== true)
setMsg(
`Unable to drop mirror ${dropArgs.flowJobName}. ${
dropRes.errorMessage ?? ''
}`
);
else {
setMsg('Mirror dropped successfully.');
window.location.reload();
}
};

const handleDropPeer = async (dropArgs: dropPeerArgs) => {
if (!dropArgs.peerName) {
Expand Down Expand Up @@ -136,7 +147,11 @@ export const DropDialog = ({
<Button
onClick={() =>
mode === 'MIRROR'
? handleDropMirror(dropArgs as dropMirrorArgs)
? handleDropMirror(
dropArgs as dropMirrorArgs,
setLoading,
setMsg
)
: mode === 'PEER'
? handleDropPeer(dropArgs as dropPeerArgs)
: handleDeleteAlert(dropArgs as deleteAlertArgs)
Expand Down
104 changes: 104 additions & 0 deletions ui/components/ResyncDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
'use client';
import { CDCConfig } from '@/app/dto/MirrorsDTO';
import { Button } from '@/lib/Button';
import { Dialog, DialogClose } from '@/lib/Dialog';
import { Label } from '@/lib/Label';
import { Divider } from '@tremor/react';
import { useState } from 'react';
import { BarLoader, DotLoader } from 'react-spinners';
import { handleDropMirror } from './DropDialog';

export const ResyncDialog = ({
mirrorConfig,
workflowId,
}: {
mirrorConfig: CDCConfig;
workflowId: string;
}) => {
const [syncing, setSyncing] = useState(false);
const [dropping, setDropping] = useState(false);
const [msg, setMsg] = useState('');

const handleResync = async () => {
setMsg('Dropping existing mirror');
const dropStatus = await handleDropMirror(
{
workflowId: workflowId,
flowJobName: mirrorConfig.flowJobName,
sourcePeer: mirrorConfig.source!,
destinationPeer: mirrorConfig.destination!,
forResync: true,
},
setDropping,
setMsg
);

if (!dropStatus) {
return;
}

setSyncing(true);
setMsg('Resyncing...');
mirrorConfig.resync = true;
const createStatus = await fetch('/api/mirrors/cdc', {
method: 'POST',
body: JSON.stringify({
config: mirrorConfig,
}),
}).then((res) => res.json());
setSyncing(false);
if (!createStatus.created) {
setMsg('Resyncing of existing mirror failed');
return;
}

setMsg('Mirror resynced successfully');
window.location.reload();
};

return (
<Dialog
noInteract={true}
size='xLarge'
triggerButton={
<Button variant='normalSolid' style={{ height: '2em', width: '8em' }}>
Resync
</Button>
}
>
<div>
<Label as='label' variant='action'>
Resync {mirrorConfig.flowJobName}
</Label>
<Divider style={{ margin: 0 }} />
<Label as='label' variant='body' style={{ marginTop: '0.3rem' }}>
Are you sure you want to resync this mirror?
<br></br>
This involves <b>dropping the existing mirror</b> and recreating it.
</Label>
<div style={{ display: 'flex', alignItems: 'center' }}>
{syncing || (dropping && <DotLoader size={15} />)}
<Label as='label' style={{ marginTop: '0.3rem' }}>
{msg}
</Label>
</div>
<div style={{ display: 'flex', marginTop: '1rem' }}>
<DialogClose>
<Button style={{ backgroundColor: '#6c757d', color: 'white' }}>
Cancel
</Button>
</DialogClose>
<Button
onClick={handleResync}
variant='normalSolid'
style={{
marginLeft: '1rem',
}}
>
{syncing || dropping ? <BarLoader /> : 'Resync'}
</Button>
</div>
</div>
</Dialog>
);
};

0 comments on commit cfab700

Please sign in to comment.