mirror of
https://github.com/supabase/supabase.git
synced 2026-10-08 02:45:07 +03:00
## What kind of change does this PR introduce? Studio UI improvement. ## What is the current behavior? Pipeline health is presented as a dense custom metrics panel with repeated connection information and per-table lag details mixed into the pipeline summary. ## What is the new behavior? Moves the pipeline-level slot status, lag, WAL retention, and last check-in into a standard detail section. It removes repeated connection content and keeps table-specific state with the replicated tables. | Before | After | | --- | --- | | <img width="1816" height="274" alt="CleanShot 2026-09-16 at 13 34 43@2x" src="https://github.com/user-attachments/assets/eceed5bb-8af2-4a3b-83a9-7849f1554fbe" /> | <img width="1830" height="506" alt="CleanShot 2026-09-16 at 13 34 15@2x" src="https://github.com/user-attachments/assets/498c4390-17e2-45e5-a923-cb4a7cfd5978" /> | _Note that the page spacing may feel a bit funny. This is handled in https://github.com/supabase/supabase/pull/49630_ This is an independent slice extracted from #49630. The related review series is #50443, #50444, this PR, #50446, then #49630. ## To test 1. Open `/project/<ref>/database/replication` and select a running pipeline. 2. Confirm **Pipeline health** shows slot status, lag, WAL retention remaining, and last check-in. 3. Confirm unlimited WAL retention is labelled **Unlimited** and a caught-up pipeline is labelled **Caught up**. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## UI Improvements - Added a dedicated Pipeline Health section summarizing WAL status, slot status, and replication lag. - Replaced the inline metrics layout with responsive detail cards and clearer supporting descriptions. - Added tooltips for lag values and relative reply times, including precise timestamps. - Updated lag labels and status indicators for improved clarity. - Added concise explanations for reserved, extended, unreserved, lost, and unknown WAL states. - Improved presentation of pipeline details with optional contextual descriptions. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
61 lines
2.4 KiB
TypeScript
61 lines
2.4 KiB
TypeScript
import dayjs from 'dayjs'
|
||
import { type ReactNode } from 'react'
|
||
|
||
import { SlotLagMetricKey } from './ReplicationPipelineStatus.types'
|
||
import { getFormattedLagValue } from './ReplicationPipelineStatus.utils'
|
||
|
||
export interface SlotLagField {
|
||
key: SlotLagMetricKey
|
||
label: string
|
||
type: 'bytes' | 'duration'
|
||
description: ReactNode
|
||
// Friendly label to show in place of a literal "0 bytes" when there's nothing to report.
|
||
zeroLabel?: string
|
||
// Friendly label to show when the value is null/absent (e.g. unlimited WAL retention).
|
||
nullLabel?: string
|
||
// Optional hover text for the value, derived from the raw value (e.g. an absolute timestamp).
|
||
getValueTooltip?: (value: number) => string
|
||
}
|
||
|
||
export const SLOT_LAG_FIELDS: SlotLagField[] = [
|
||
{
|
||
key: 'confirmed_flush_lsn_bytes',
|
||
// Same label as the list column. Scoped to the main slot's ongoing change stream, so "Caught
|
||
// up" stays true while tables are still doing their initial copy.
|
||
label: 'Lag',
|
||
type: 'bytes',
|
||
description:
|
||
'Changes still on their way to the destination, measured on the pipeline’s main slot. Tables in their initial sync use their own slots and aren’t counted.',
|
||
zeroLabel: 'Caught up',
|
||
},
|
||
{
|
||
key: 'safe_wal_size_bytes',
|
||
label: 'WAL retention remaining',
|
||
type: 'bytes',
|
||
description: (
|
||
<>
|
||
How much more WAL can accumulate before the replication slot is at risk of being lost.
|
||
Controlled by the <code className="text-code-inline">max_slot_wal_keep_size</code> setting.
|
||
</>
|
||
),
|
||
nullLabel: 'Unlimited',
|
||
},
|
||
{
|
||
key: 'reply_time_lag',
|
||
label: 'Last check-in',
|
||
type: 'duration',
|
||
description: 'Time since the pipeline last reported back to your database',
|
||
zeroLabel: 'Just now',
|
||
// reply_time_lag is "milliseconds ago", so the absolute time is now minus that, in local time.
|
||
getValueTooltip: (ms) => dayjs().subtract(ms, 'millisecond').format('MMM D, YYYY, h:mm:ss A'),
|
||
},
|
||
]
|
||
|
||
// Resolves a field's value into a display string (+ optional precise detail), honoring the
|
||
// friendly zero/null labels before falling back to the formatted byte/duration value.
|
||
export const getFieldDisplay = (field: SlotLagField, value: number | null | undefined) => {
|
||
if (value == null) return { display: field.nullLabel ?? 'n/a', detail: undefined }
|
||
if (field.zeroLabel && value === 0) return { display: field.zeroLabel, detail: undefined }
|
||
return getFormattedLagValue(field.type, value)
|
||
}
|