HighQueryCost.tsx183 lines · main
1import {
2 Button,
3 Dialog,
4 DialogClose,
5 DialogContent,
6 DialogDescription,
7 DialogFooter,
8 DialogHeader,
9 DialogSection,
10 DialogSectionSeparator,
11 DialogTitle,
12 DialogTrigger,
13 Tooltip,
14 TooltipContent,
15 TooltipTrigger,
16} from 'ui'
17import { Admonition } from 'ui-patterns'
18
19import { DocsButton } from './DocsButton'
20import { InlineLinkClassName } from './InlineLink'
21import { DOCS_URL } from '@/lib/constants'
22import { ResponseError } from '@/types'
23
24interface HighQueryCostErrorProps {
25 error: ResponseError
26 suggestions?: string[]
27 onSelectLoadData?: () => void
28}
29
30export const HighCostError = ({
31 error,
32 suggestions,
33 onSelectLoadData,
34}: HighQueryCostErrorProps) => {
35 return (
36 <Admonition
37 type="default"
38 title="Data not loaded to protect database performance"
39 description="The query to retrieve the data was not run as it could place heavy load on the database and impact performance"
40 >
41 <div className="mt-2 flex items-center gap-x-2 items-center">
42 {!!onSelectLoadData && (
43 <LoadDataWarningDialog error={error} onSelectLoadData={onSelectLoadData} />
44 )}
45 <HighQueryCostDialog error={error} suggestions={suggestions} />
46 </div>
47 </Admonition>
48 )
49}
50
51const HighQueryCostDialog = ({ error, suggestions = [] }: HighQueryCostErrorProps) => {
52 const metadata = error.metadata
53
54 return (
55 <Dialog>
56 <DialogTrigger asChild>
57 <Button type="outline">Learn more</Button>
58 </DialogTrigger>
59 <DialogContent onOpenAutoFocus={(event) => event.preventDefault()}>
60 <DialogHeader>
61 <DialogTitle>Estimated query cost exceeds safety thresholds</DialogTitle>
62 <DialogDescription>
63 Preventive measure to mitigate impacting the database
64 </DialogDescription>
65 </DialogHeader>
66 <DialogSectionSeparator />
67 <DialogSection className="flex flex-col gap-y-2 text-sm">
68 <p>
69 The dashboard runs optimized SQL queries on your project’s database to load data for
70 this interface.
71 </p>
72 <p>
73 However, the query was skipped as its{' '}
74 <Tooltip>
75 <TooltipTrigger className={InlineLinkClassName}>estimated cost</TooltipTrigger>
76 <TooltipContent side="bottom" className="flex flex-col gap-y-1">
77 <p>Estimated cost: {metadata?.cost.toLocaleString()}</p>
78 <p className="text-foreground-light">
79 Determined via the <code className="text-code-inline">EXPLAIN</code> command
80 </p>
81 </TooltipContent>
82 </Tooltip>{' '}
83 is high and could place significant load on the database with high disk I/O or CPU
84 usage.
85 </p>
86 </DialogSection>
87
88 {suggestions.length > 0 && (
89 <>
90 <DialogSectionSeparator />
91 <DialogSection className="flex flex-col gap-y-4 text-sm">
92 <p className="font-mono text-foreground-lighter uppercase tracking-tight text-sm">
93 Suggested steps
94 </p>
95
96 {suggestions.length > 0 && (
97 <div className="flex flex-col gap-y-1">
98 <p>You may check the following to lower the cost of the query</p>
99 <ul className="list-disc pl-6">
100 {suggestions.map((x) => (
101 <li key={x}>{x}</li>
102 ))}
103 </ul>
104 </div>
105 )}
106 </DialogSection>
107 </>
108 )}
109
110 <DialogFooter>
111 <DocsButton
112 href={`${DOCS_URL}/guides/troubleshooting/understanding-postgresql-explain-output-Un9dqX`}
113 />
114 <DialogClose asChild>
115 <Button type="default" className="opacity-100">
116 Understood
117 </Button>
118 </DialogClose>
119 </DialogFooter>
120 </DialogContent>
121 </Dialog>
122 )
123}
124
125const LoadDataWarningDialog = ({
126 error,
127 onSelectLoadData,
128}: {
129 error: ResponseError
130 onSelectLoadData: () => void
131}) => {
132 const metadata = error.metadata
133
134 return (
135 <Dialog>
136 <DialogTrigger asChild>
137 <Button type="default">Load data</Button>
138 </DialogTrigger>
139 <DialogContent onOpenAutoFocus={(event) => event.preventDefault()}>
140 <DialogHeader>
141 <DialogTitle>Confirm to proceed loading data</DialogTitle>
142 <DialogDescription>
143 Preventive measure to mitigate impacting the database
144 </DialogDescription>
145 </DialogHeader>
146 <DialogSectionSeparator />
147 <DialogSection className="flex flex-col gap-y-2 text-sm">
148 <p>
149 The query to load your table's data was initially skipped as its{' '}
150 <Tooltip>
151 <TooltipTrigger className={InlineLinkClassName}>estimated cost</TooltipTrigger>
152 <TooltipContent side="bottom" className="flex flex-col gap-y-1">
153 <p>Estimated cost: {metadata?.cost.toLocaleString()}</p>
154 <p className="text-foreground-light">
155 Determined via the <code className="text-code-inline">EXPLAIN</code> command
156 </p>
157 </TooltipContent>
158 </Tooltip>{' '}
159 is high and could place significant load on the database with high disk I/O or CPU
160 usage.
161 </p>
162
163 <p>
164 You may proceed to run the query, and we'll suppress this warning for this table for the
165 rest of this browser session.
166 </p>
167 </DialogSection>
168 <DialogFooter>
169 <DialogClose asChild>
170 <Button type="default" className="opacity-100">
171 Cancel
172 </Button>
173 </DialogClose>
174 <DialogClose asChild>
175 <Button type="warning" onClick={() => onSelectLoadData()}>
176 I understand, proceed
177 </Button>
178 </DialogClose>
179 </DialogFooter>
180 </DialogContent>
181 </Dialog>
182 )
183}