AdvancedSettings.tsx 11 KB

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