CronJobsTab.useCronJobsData.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import { keepPreviousData } from '@tanstack/react-query'
  2. import { useMemo } from 'react'
  3. import { CRON_JOBS_THRESHOLD } from './CronJobsTab.constants'
  4. import type { ConnectionVars } from '@/data/common.types'
  5. import { useCronJobsCountEstimateQuery } from '@/data/database-cron-jobs/database-cron-jobs-count-estimate-query'
  6. import { useCronJobsCountQuery } from '@/data/database-cron-jobs/database-cron-jobs-count-query'
  7. import {
  8. CronJob,
  9. useCronJobsInfiniteQuery,
  10. } from '@/data/database-cron-jobs/database-cron-jobs-infinite-query'
  11. import { useCronJobsMinimalInfiniteQuery } from '@/data/database-cron-jobs/database-cron-jobs-minimal-infinite-query'
  12. import { COST_THRESHOLD_ERROR } from '@/data/sql/execute-sql-query'
  13. import type { ResponseError } from '@/types'
  14. // =============================================================================
  15. // Input types
  16. // =============================================================================
  17. type UseCronJobsDataParams = ConnectionVars & {
  18. searchQuery: string | null
  19. }
  20. // =============================================================================
  21. // Shared state types (available in all states)
  22. // =============================================================================
  23. interface CronJobsGridState {
  24. rows: Array<CronJob>
  25. queryCost?: number
  26. isSuccess: boolean
  27. isLoading: boolean
  28. error: ResponseError | null
  29. isRefetching: boolean
  30. isFetchingNextPage: boolean
  31. hasNextPage: boolean
  32. isMinimal: boolean
  33. refetch: () => void
  34. fetchNextPage: () => void
  35. }
  36. interface CronJobsCountState {
  37. value: number | undefined
  38. isEstimate: boolean
  39. isLoading: boolean
  40. }
  41. // =============================================================================
  42. // Result type
  43. // =============================================================================
  44. interface UseCronJobsDataResult {
  45. /** State for the cron jobs grid */
  46. grid: CronJobsGridState
  47. /** State for the cron jobs count (exact or estimate) */
  48. count: CronJobsCountState
  49. }
  50. export function useCronJobsData({
  51. projectRef,
  52. connectionString,
  53. searchQuery,
  54. }: UseCronJobsDataParams): UseCronJobsDataResult {
  55. const isProjectReady = !!projectRef
  56. const {
  57. data: cronJobsData,
  58. error: cronJobsError,
  59. isSuccess: isCronJobsSuccess,
  60. isLoading: isCronJobsLoading,
  61. isRefetching: isCronJobsRefetching,
  62. isFetchingNextPage,
  63. hasNextPage = false,
  64. refetch: refetchCronJobs,
  65. fetchNextPage,
  66. } = useCronJobsInfiniteQuery(
  67. { projectRef, connectionString, searchTerm: searchQuery ?? undefined },
  68. {
  69. placeholderData: Boolean(searchQuery) ? keepPreviousData : undefined,
  70. staleTime: Infinity,
  71. }
  72. )
  73. const useMinimalQuery = cronJobsError?.message === COST_THRESHOLD_ERROR
  74. const {
  75. data: cronJobsMinimalData,
  76. error: cronJobsMinimalError,
  77. isSuccess: isCronJobsMinimalSuccess,
  78. isLoading: isCronJobsMinimalLoading,
  79. isRefetching: isCronJobsMinimalRefetching,
  80. isFetchingNextPage: isFetchingNextPageMinimal,
  81. hasNextPage: hasNextPageMinimal = false,
  82. refetch: refetchCronJobsMinimal,
  83. fetchNextPage: fetchNextPageMinimal,
  84. } = useCronJobsMinimalInfiniteQuery(
  85. { projectRef, connectionString, searchTerm: searchQuery ?? undefined },
  86. {
  87. placeholderData: Boolean(searchQuery) ? keepPreviousData : undefined,
  88. staleTime: Infinity,
  89. enabled: useMinimalQuery,
  90. }
  91. )
  92. const cronJobs = useMemo(() => {
  93. if (useMinimalQuery) {
  94. return cronJobsMinimalData?.pages.flatMap((page) => page) ?? []
  95. } else {
  96. return cronJobsData?.pages.flatMap((page) => page) ?? []
  97. }
  98. }, [useMinimalQuery, cronJobsData?.pages, cronJobsMinimalData?.pages])
  99. // Fetch count - gated on cron.job table size
  100. // Always fetch the estimate first (it's fast since it uses pg_stat)
  101. const {
  102. data: estimatedCount,
  103. isPending: isLoadingEstimatedCount,
  104. isError: isEstimatedCountError,
  105. } = useCronJobsCountEstimateQuery({ projectRef, connectionString }, { enabled: isProjectReady })
  106. const hasLargeCronJobsTable =
  107. typeof estimatedCount === 'number' && estimatedCount > CRON_JOBS_THRESHOLD
  108. // Exact count is enabled when the cron.job table is small enough
  109. const isCountQueryEnabled =
  110. isProjectReady && !isLoadingEstimatedCount && !isEstimatedCountError && !hasLargeCronJobsTable
  111. const { data: exactCount, isPending: isLoadingExactCount } = useCronJobsCountQuery(
  112. { projectRef, connectionString },
  113. { enabled: isCountQueryEnabled }
  114. )
  115. return {
  116. grid: {
  117. rows: cronJobs,
  118. queryCost: cronJobsError?.metadata?.cost,
  119. error: useMinimalQuery ? cronJobsMinimalError : cronJobsError,
  120. isSuccess: useMinimalQuery ? isCronJobsMinimalSuccess : isCronJobsSuccess,
  121. isLoading: useMinimalQuery ? isCronJobsMinimalLoading : isCronJobsLoading,
  122. isRefetching: useMinimalQuery ? isCronJobsMinimalRefetching : isCronJobsRefetching,
  123. isFetchingNextPage: useMinimalQuery ? isFetchingNextPageMinimal : isFetchingNextPage,
  124. hasNextPage: useMinimalQuery ? hasNextPageMinimal : hasNextPage,
  125. isMinimal: useMinimalQuery,
  126. refetch: useMinimalQuery ? refetchCronJobsMinimal : refetchCronJobs,
  127. fetchNextPage: useMinimalQuery ? fetchNextPageMinimal : fetchNextPage,
  128. },
  129. count: {
  130. value: isCountQueryEnabled ? exactCount : estimatedCount,
  131. isEstimate: !isCountQueryEnabled,
  132. isLoading: isCountQueryEnabled ? isLoadingExactCount : isLoadingEstimatedCount,
  133. },
  134. }
  135. }