AdvancedSettings.tsx283 lines · main
1import type { ChangeEvent } from 'react'
2import type { UseFormReturn } from 'react-hook-form'
3import {
4 Accordion,
5 AccordionContent,
6 AccordionItem,
7 AccordionTrigger,
8 Badge,
9 FormControl,
10 FormField,
11 FormInputGroupInput,
12 InputGroup,
13 InputGroupAddon,
14 InputGroupText,
15 Select,
16 SelectContent,
17 SelectItem,
18 SelectTrigger,
19} from 'ui'
20import { FormItemLayout } from 'ui-patterns/form/FormItemLayout/FormItemLayout'
21
22import { DestinationType } from '../DestinationPanel.types'
23import { type DestinationPanelSchemaType } from './DestinationForm.schema'
24
25export const AdvancedSettings = ({
26 type,
27 form,
28}: {
29 type: DestinationType
30 form: UseFormReturn<DestinationPanelSchemaType>
31}) => {
32 const handleNumberChange =
33 (field: { onChange: (value?: number) => void }) => (e: ChangeEvent<HTMLInputElement>) => {
34 const val = e.target.value
35 field.onChange(val === '' ? undefined : Number(val))
36 }
37
38 return (
39 <div className="px-5">
40 <Accordion type="single" collapsible>
41 <AccordionItem value="item-1" className="border-none">
42 <AccordionTrigger className="font-normal gap-2 justify-between text-sm py-3 hover:no-underline">
43 <div className="flex flex-col items-start gap-0.5">
44 <span className="text-sm font-medium">Advanced settings</span>
45 <span className="text-sm text-foreground-lighter font-normal">
46 Optional performance tuning
47 </span>
48 </div>
49 </AccordionTrigger>
50 <AccordionContent className="pb-0! pt-3 [&>div]:flex [&>div]:flex-col [&>div]:gap-y-4">
51 {/* Batch wait time - applies to all destinations */}
52 <FormField
53 control={form.control}
54 name="maxFillMs"
55 render={({ field }) => (
56 <FormItemLayout
57 layout="horizontal"
58 label="Batch wait time"
59 description={
60 <>
61 <p>
62 Maximum time pipeline waits to collect additional changes before flushing a
63 batch.
64 </p>
65 <p>
66 Lower values reduce replication latency, higher values improve batching
67 efficiency.
68 </p>
69 </>
70 }
71 >
72 <FormControl>
73 <InputGroup>
74 <FormInputGroupInput
75 {...field}
76 type="number"
77 value={field.value ?? ''}
78 onChange={handleNumberChange(field)}
79 placeholder="Default: 10000"
80 />
81 <InputGroupAddon align="inline-end">
82 <InputGroupText>milliseconds</InputGroupText>
83 </InputGroupAddon>
84 </InputGroup>
85 </FormControl>
86 </FormItemLayout>
87 )}
88 />
89
90 <FormField
91 control={form.control}
92 name="maxTableSyncWorkers"
93 render={({ field }) => (
94 <FormItemLayout
95 label="Table sync workers"
96 layout="horizontal"
97 description={
98 <>
99 <p>Number of tables copied in parallel during the initial snapshot phase.</p>
100 <p>
101 Each worker uses one replication slot (up to N + 1 total while syncing).
102 </p>
103 </>
104 }
105 >
106 <FormControl>
107 <InputGroup>
108 <FormInputGroupInput
109 {...field}
110 type="number"
111 value={field.value ?? ''}
112 onChange={handleNumberChange(field)}
113 placeholder="Default: 4"
114 />
115 <InputGroupAddon align="inline-end">
116 <InputGroupText>workers</InputGroupText>
117 </InputGroupAddon>
118 </InputGroup>
119 </FormControl>
120 </FormItemLayout>
121 )}
122 />
123
124 <FormField
125 control={form.control}
126 name="maxCopyConnectionsPerTable"
127 render={({ field }) => (
128 <FormItemLayout
129 label="Copy connections per table"
130 layout="horizontal"
131 description={
132 <>
133 <p>
134 Number of parallel connections each table copy can use during initial sync.
135 </p>
136 <p>
137 More connections speed up large table copies, but use more database
138 connections.
139 </p>
140 </>
141 }
142 >
143 <FormControl>
144 <InputGroup>
145 <FormInputGroupInput
146 {...field}
147 type="number"
148 value={field.value ?? ''}
149 onChange={handleNumberChange(field)}
150 placeholder="Default: 2"
151 />
152 <InputGroupAddon align="inline-end">
153 <InputGroupText>connections</InputGroupText>
154 </InputGroupAddon>
155 </InputGroup>
156 </FormControl>
157 </FormItemLayout>
158 )}
159 />
160
161 <FormField
162 control={form.control}
163 name="invalidatedSlotBehavior"
164 render={({ field }) => (
165 <FormItemLayout
166 label="Invalidated slot behavior"
167 layout="horizontal"
168 description="Behavior when the replication slot is invalidated"
169 >
170 <FormControl>
171 <Select value={field.value ?? 'error'} onValueChange={field.onChange}>
172 <SelectTrigger className="capitalize">{field.value ?? 'error'}</SelectTrigger>
173 <SelectContent>
174 <SelectItem value="error" className="[&>span]:top-2.5">
175 <p>Error</p>
176 <p className="text-foreground-lighter">
177 Blocks startup for manual recovery
178 </p>
179 </SelectItem>
180 <SelectItem value="recreate" className="[&>span]:top-2.5">
181 <p>Recreate</p>
182 <p className="text-foreground-lighter">
183 Rebuilds the slot and restarts replication from scratch
184 </p>
185 </SelectItem>
186 </SelectContent>
187 </Select>
188 </FormControl>
189 </FormItemLayout>
190 )}
191 />
192
193 {type === 'BigQuery' && (
194 <>
195 <FormField
196 control={form.control}
197 name="connectionPoolSize"
198 render={({ field }) => (
199 <FormItemLayout
200 label={
201 <div className="flex flex-col gap-y-2">
202 <span>Connection pool size</span>
203 <Badge className="w-min">BigQuery only</Badge>
204 </div>
205 }
206 layout="horizontal"
207 description={
208 <>
209 <p>Size of the BigQuery Storage Write API connection pool.</p>
210 <p>
211 More connections allow more parallel writes, but consume more resources.
212 </p>
213 </>
214 }
215 >
216 <FormControl>
217 <InputGroup>
218 <FormInputGroupInput
219 {...field}
220 type="number"
221 value={field.value ?? ''}
222 onChange={handleNumberChange(field)}
223 placeholder="Default: 4"
224 />
225 <InputGroupAddon align="inline-end">
226 <InputGroupText>connections</InputGroupText>
227 </InputGroupAddon>
228 </InputGroup>
229 </FormControl>
230 </FormItemLayout>
231 )}
232 />
233
234 <FormField
235 control={form.control}
236 name="maxStalenessMins"
237 render={({ field }) => (
238 <FormItemLayout
239 label={
240 <div className="flex flex-col gap-y-2">
241 <span>Maximum staleness</span>
242 <Badge className="w-min">BigQuery only</Badge>
243 </div>
244 }
245 layout="horizontal"
246 description={
247 <>
248 <p>
249 Maximum allowed age for BigQuery cached metadata before reading base
250 tables.
251 </p>
252 <p>
253 Lower values improve freshness, higher values can reduce query cost and
254 latency.
255 </p>
256 </>
257 }
258 >
259 <FormControl>
260 <InputGroup>
261 <FormInputGroupInput
262 {...field}
263 type="number"
264 value={field.value ?? ''}
265 onChange={handleNumberChange(field)}
266 placeholder="Default: None (No staleness limit)"
267 />
268 <InputGroupAddon align="inline-end">
269 <InputGroupText>minutes</InputGroupText>
270 </InputGroupAddon>
271 </InputGroup>
272 </FormControl>
273 </FormItemLayout>
274 )}
275 />
276 </>
277 )}
278 </AccordionContent>
279 </AccordionItem>
280 </Accordion>
281 </div>
282 )
283}