Files
supabase/apps/studio/components/interfaces/Database/Replication/BatchRestartDialog.tsx
T

184 lines
6.0 KiB
TypeScript

import { useParams } from 'common'
import { useMemo } from 'react'
import { toast } from 'sonner'
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from 'ui'
import { PipelineStatusName } from './Replication.constants'
import { RestartCostEstimate } from './RestartCostEstimate'
import { ReplicationPipelineTableStatus } from '@/data/replication/pipeline-replication-status-query'
import { useRollbackTablesMutation } from '@/data/replication/rollback-tables-mutation'
interface BatchRestartDialogProps {
open: boolean
onOpenChange: (open: boolean) => void
mode: 'all' | 'errored'
tables: ReplicationPipelineTableStatus[]
sourceId?: number
publicationName?: string
pipelineStatusName?: PipelineStatusName
onRestartStart?: (tableIds: number[]) => void
onRestartComplete?: (tableIds: number[]) => void
}
export const BatchRestartDialog = ({
open,
onOpenChange,
mode,
tables,
sourceId,
publicationName,
pipelineStatusName,
onRestartStart,
onRestartComplete,
}: BatchRestartDialogProps) => {
const { ref: projectRef, pipelineId: _pipelineId } = useParams()
const pipelineId = Number(_pipelineId)
const affectedTables = useMemo(() => {
if (mode === 'all') {
return tables
} else {
return tables.filter(
(t) =>
t.state.name === 'error' &&
'retry_policy' in t.state &&
t.state.retry_policy?.policy === 'manual_retry'
)
}
}, [mode, tables])
const affectedTableIds = useMemo(
() => affectedTables.map((table) => table.table_id),
[affectedTables]
)
const affectedTableNames = useMemo(
() => affectedTables.map((table) => table.table_name),
[affectedTables]
)
const { mutateAsync: rollbackTables, isPending: isResetting } = useRollbackTablesMutation({
onSuccess: (data) => {
const count = data.tables.length
toast.success(
`Restarting replication for ${count} table${count > 1 ? 's' : ''}. Pipeline will restart automatically.`
)
},
onSettled: () => {
onRestartComplete?.(affectedTableIds)
onOpenChange(false)
},
onError: (error) => {
toast.error(`Failed to restart replication: ${error.message}`)
},
})
const handleReset = async () => {
if (!projectRef) return toast.error('Project ref is required')
onRestartStart?.(affectedTableIds)
try {
await rollbackTables({
projectRef,
pipelineId,
target: mode === 'all' ? { type: 'all_tables' } : { type: 'all_errored_tables' },
rollbackType: 'full',
pipelineStatusName,
})
} catch (error) {}
}
const dialogContent =
mode === 'all'
? {
title: 'Restart all tables',
description: (
<div className="space-y-3 text-sm">
<p>
This will restart replication for all {affectedTables.length} table
{affectedTables.length === 1 ? '' : 's'} in this pipeline from scratch:
</p>
<ul className="list-disc list-inside space-y-1.5 pl-2">
<li>
<strong>Every table's initial sync will restart.</strong> All table data will be
copied again from the source. This initial sync is billed in addition to previous
initial syncs.
</li>
<li>
<strong>All downstream data will be deleted.</strong> All replicated data will be
removed.
</li>
<li>
<strong>The pipeline will restart automatically.</strong> This is required to
apply this change.
</li>
</ul>
</div>
),
action: 'Restart all tables',
}
: {
title: 'Restart failed tables',
description: (
<div className="space-y-3 text-sm">
<p>
This will restart replication for{' '}
<strong>all {affectedTables.length} failed tables</strong> from scratch:
</p>
<ul className="list-disc list-inside space-y-1.5 pl-2">
<li>
<strong>Each failed table's initial sync will restart.</strong> These tables will
be copied again from the source. This initial sync is billed in addition to
previous initial syncs.
</li>
<li>
<strong>Existing downstream data will be deleted.</strong> Replicated data for
these tables will be removed.
</li>
<li>
<strong>All other tables remain untouched.</strong> Only failed tables are
affected.
</li>
<li>
<strong>The pipeline will restart automatically.</strong> This is required to
apply this change.
</li>
</ul>
</div>
),
action: 'Restart failed tables',
}
return (
<AlertDialog open={open} onOpenChange={onOpenChange}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>{dialogContent.title}</AlertDialogTitle>
<AlertDialogDescription asChild>{dialogContent.description}</AlertDialogDescription>
</AlertDialogHeader>
<RestartCostEstimate
open={open}
projectRef={projectRef}
sourceId={sourceId}
publicationName={publicationName}
tableNames={affectedTableNames}
/>
<AlertDialogFooter>
<AlertDialogCancel disabled={isResetting}>Cancel</AlertDialogCancel>
<AlertDialogAction disabled={isResetting} onClick={handleReset} variant="warning">
{isResetting ? 'Restarting replication...' : dialogContent.action}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)
}