SlotLagMetrics.tsx126 lines · main
1import { Info } from 'lucide-react'
2import { Tooltip, TooltipContent, TooltipTrigger } from 'ui'
3
4import { SlotLagMetricKey, SlotLagMetrics } from './ReplicationPipelineStatus.types'
5import { getFormattedLagValue } from './ReplicationPipelineStatus.utils'
6
7const SLOT_LAG_FIELDS: {
8 key: SlotLagMetricKey
9 label: string
10 type: 'bytes' | 'duration'
11 description: string
12}[] = [
13 {
14 key: 'confirmed_flush_lsn_bytes',
15 label: 'WAL Flush lag (size)',
16 type: 'bytes',
17 description:
18 'Bytes between the newest WAL record applied locally and the latest flushed WAL record acknowledged by ETL.',
19 },
20 {
21 key: 'flush_lag',
22 label: 'WAL Flush lag (time)',
23 type: 'duration',
24 description:
25 'Time between flushing recent WAL locally and receiving notification that ETL has written and flushed it.',
26 },
27 {
28 key: 'safe_wal_size_bytes',
29 label: 'Remaining WAL size',
30 type: 'bytes',
31 description:
32 'Bytes still available to write to WAL before this slot risks entering the "lost" state.',
33 },
34]
35
36export const SlotLagMetricsInline = ({
37 tableName,
38 metrics,
39}: {
40 tableName: string
41 metrics: SlotLagMetrics
42}) => {
43 return (
44 <div className="flex flex-wrap items-center gap-x-3 gap-y-1.5 text-xs text-foreground">
45 <span className="truncate font-medium" title={tableName}>
46 {tableName}
47 </span>
48 <span className="text-foreground-lighter">•</span>
49 <div className="flex flex-wrap items-center gap-x-3 gap-y-1.5 text-[11px] text-foreground-light">
50 {SLOT_LAG_FIELDS.map(({ key, label, type }) => {
51 const { display } = getFormattedLagValue(type, metrics[key])
52 return (
53 <span key={`${tableName}-${key}`} className="flex items-center gap-1">
54 <span className="uppercase tracking-wide text-[10px] text-foreground-lighter">
55 {label}
56 </span>
57 <span className="text-foreground">{display}</span>
58 </span>
59 )
60 })}
61 </div>
62 </div>
63 )
64}
65
66export const SlotLagMetricsList = ({
67 metrics,
68 size = 'default',
69 showMetricInfo = true,
70}: {
71 metrics: SlotLagMetrics
72 size?: 'default' | 'compact'
73 showMetricInfo?: boolean
74}) => {
75 const gridClasses =
76 size === 'default'
77 ? 'grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 gap-y-4 gap-x-6'
78 : 'grid-cols-2 gap-y-2 gap-x-4'
79
80 const labelClasses =
81 size === 'default' ? 'text-xs text-foreground-light' : 'text-[11px] text-foreground-lighter'
82
83 const valueClasses =
84 size === 'default'
85 ? 'text-sm font-medium text-foreground'
86 : 'text-xs font-medium text-foreground'
87
88 return (
89 <dl className={`grid ${gridClasses}`}>
90 {SLOT_LAG_FIELDS.map(({ key, label, type, description }) => (
91 <div key={key} className="flex flex-col gap-0.5">
92 <dt className={labelClasses}>
93 <span className="inline-flex items-center gap-1">
94 {label}
95 {showMetricInfo && (
96 <Tooltip>
97 <TooltipTrigger asChild>
98 <button
99 type="button"
100 aria-label={`What is ${label}`}
101 className="inline-flex h-4 w-4 items-center justify-center rounded-full bg-surface-200 text-foreground-lighter transition-colors hover:bg-surface-300 hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-offset-1 focus-visible:ring-foreground-lighter"
102 >
103 <Info size={12} />
104 </button>
105 </TooltipTrigger>
106 <TooltipContent side="top" align="start" className="max-w-xs text-xs">
107 {description}
108 </TooltipContent>
109 </Tooltip>
110 )}
111 </span>
112 </dt>
113 {(() => {
114 const { display, detail } = getFormattedLagValue(type, metrics[key])
115 return (
116 <dd className={`flex flex-col ${valueClasses}`}>
117 <span>{display}</span>
118 {detail && <span className="text-[11px] text-foreground-lighter">{detail}</span>}
119 </dd>
120 )
121 })()}
122 </div>
123 ))}
124 </dl>
125 )
126}