SlotLagMetrics.tsx 4.2 KB

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