@postgres.ai/shared 4.1.0 → 4.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitlab-ci.yml +2 -0
- package/dist/package.json +1 -1
- package/dist/pages/Clone/index.js +2 -2
- package/dist/pages/CreateClone/index.js +2 -2
- package/dist/pages/CreateClone/styles.module.scss +1 -0
- package/dist/pages/Instance/Configuration/index.js +1 -1
- package/dist/pages/Instance/Configuration/tooltipText.d.ts +1 -0
- package/dist/pages/Instance/Configuration/tooltipText.js +1 -0
- package/dist/pages/Instance/Configuration/useForm.d.ts +1 -0
- package/dist/pages/Instance/Configuration/useForm.js +1 -0
- package/dist/pages/Instance/stores/Main.d.ts +1 -0
- package/dist/types/api/endpoints/testDbSource.js +4 -2
- package/dist/types/api/entities/config.d.ts +4 -0
- package/dist/types/api/entities/config.js +9 -0
- package/package.json +1 -1
- package/pages/Clone/index.tsx +2 -2
- package/pages/CreateClone/index.tsx +1 -1
- package/pages/CreateClone/styles.module.scss +1 -0
- package/pages/Instance/Configuration/index.tsx +10 -0
- package/pages/Instance/Configuration/tooltipText.tsx +20 -0
- package/pages/Instance/Configuration/useForm.ts +2 -0
- package/postgres.ai-shared-4.1.1.tgz +0 -0
- package/types/api/endpoints/testDbSource.ts +4 -2
- package/types/api/entities/config.ts +9 -0
- package/postgres.ai-shared-4.1.0.tgz +0 -0
package/.gitlab-ci.yml
CHANGED
package/dist/package.json
CHANGED
|
@@ -286,10 +286,10 @@ export const Clone = observer((props) => {
|
|
|
286
286
|
style: styles.inputFieldLabel,
|
|
287
287
|
}, FormHelperTextProps: {
|
|
288
288
|
style: styles.inputFieldHelper,
|
|
289
|
-
} }), _jsx(IconButton, { className: classes.copyButton, "aria-label": "Copy", onClick: () => copyToClipboard(jdbcConnectionStr), children: icons.copyIcon })] }), "\u00A0", _jsx(Tooltip, { content: _jsx(_Fragment, { children: "Used to connect to PostgreSQL using JDBC. Change DBNAME to the name of the database you want to connect to, and change DBPASSWORD to the password you used when creating the clone." }), children: _jsx("span", { className: classes.textFieldInfo, children: icons.infoIcon }) })] }))] })), _jsx("br", {}), _jsx("div", { className: classes.fieldBlock, children: _jsxs("span", { className: classes.remark, children: ["Password was set during clone creation. It\u2019s not being stored.", _jsx("br", {}), "You would need to recreate a clone if the password is lost."] }) }), _jsx("br", {}), _jsx("p", { children: _jsx("strong", { children: "Protection" }) }), _jsxs("div", { style: { marginTop: '8px', marginBottom: '16px' }, children: [_jsx(Select, { label: "Deletion
|
|
289
|
+
} }), _jsx(IconButton, { className: classes.copyButton, "aria-label": "Copy", onClick: () => copyToClipboard(jdbcConnectionStr), children: icons.copyIcon })] }), "\u00A0", _jsx(Tooltip, { content: _jsx(_Fragment, { children: "Used to connect to PostgreSQL using JDBC. Change DBNAME to the name of the database you want to connect to, and change DBPASSWORD to the password you used when creating the clone." }), children: _jsx("span", { className: classes.textFieldInfo, children: icons.infoIcon }) })] }))] })), _jsx("br", {}), _jsx("div", { className: classes.fieldBlock, children: _jsxs("span", { className: classes.remark, children: ["Password was set during clone creation. It\u2019s not being stored.", _jsx("br", {}), "You would need to recreate a clone if the password is lost."] }) }), _jsx("br", {}), _jsx("p", { children: _jsx("strong", { children: "Protection" }) }), _jsxs("div", { style: { marginTop: '8px', marginBottom: '16px' }, children: [_jsx(Select, { label: "Deletion\u00a0protection", items: clone.protected && clone.protectedTillDate
|
|
290
290
|
? [
|
|
291
291
|
{ value: 'current', children: `Protected until ${clone.protectedTillDate.toLocaleString()}` },
|
|
292
292
|
...protectionOptions,
|
|
293
293
|
]
|
|
294
|
-
: protectionOptions, value: getCurrentProtectionValue(), onChange: handleProtectionChange, disabled: isDisabledControls, style: { minWidth:
|
|
294
|
+
: protectionOptions, value: getCurrentProtectionValue(), onChange: handleProtectionChange, disabled: isDisabledControls, style: { minWidth: 200 } }), isUpdatingClone && _jsx(Spinner, { size: "sm", className: classes.spinner })] }), _jsxs("span", { className: classes.remark, children: ["Select how long this clone should be protected from deletion. Protected clones cannot be deleted manually or automatically.", _jsx("br", {}), "Check disk space regularly and delete this clone once your work is done."] }), stores.main.updateCloneError && (_jsx(ErrorStub, { title: "Updating error", message: stores.main.updateCloneError, className: classes.errorStub }))] }), _jsxs("div", { className: classes.snippetContainer, children: [_jsx(SectionTitle, { tag: "h2", level: 2, text: 'Reset clone using CLI' }), _jsx("p", { className: classes.tooltip, children: "You can reset the clone using CLI using the following command:" }), _jsx(SyntaxHighlight, { content: getCliResetCloneCommand(props.cloneId) }), _jsx(SectionTitle, { className: classes.title, tag: "h2", level: 2, text: 'Delete clone using CLI' }), _jsx("p", { className: classes.tooltip, children: "You can delete the clone using CLI using the following command:" }), _jsx(SyntaxHighlight, { content: getCliDestroyCloneCommand(props.cloneId) }), _jsx(SectionTitle, { className: classes.title, tag: "h2", level: 2, text: 'Toggle deletion protection using CLI' }), _jsx("p", { className: classes.tooltip, children: "You can toggle deletion protection using CLI for this clone using the following command:" }), _jsx(SyntaxHighlight, { content: getCliProtectedCloneCommand(true) }), _jsx(SyntaxHighlight, { content: getCliProtectedCloneCommand(false) }), _jsx(SectionTitle, { className: classes.title, tag: "h2", level: 2, text: 'Create snapshot for this clone using CLI' }), _jsx("p", { className: classes.tooltip, children: "You can create a snapshot for this clone using CLI using the following command:" }), _jsx(SyntaxHighlight, { content: getCreateSnapshotCommand(props.cloneId) })] }), _jsxs(_Fragment, { children: [_jsx(DestroyCloneRestrictionModal, { isOpen: isOpenRestrictionModal, onClose: () => setIsOpenRestrictionModal(false), cloneId: clone.id }), _jsx(DestroyCloneModal, { isOpen: isOpenDestroyModal, onClose: () => setIsOpenDestroyModal(false), cloneId: clone.id, onDestroyClone: destroyClone }), _jsx(ResetCloneModal, { isOpen: isOpenResetModal, onClose: () => setIsOpenResetModal(false), clone: clone, snapshots: snapshots.data, onResetClone: resetClone, version: (_k = instance.state) === null || _k === void 0 ? void 0 : _k.engine.version })] })] })] }));
|
|
295
295
|
});
|
|
@@ -166,7 +166,7 @@ export const CreateClone = observer((props) => {
|
|
|
166
166
|
if (formik.errors.dbPassword) {
|
|
167
167
|
formik.setFieldError('dbPassword', '');
|
|
168
168
|
}
|
|
169
|
-
}, isDisabled: isCreatingClone, children: "Generate" }), passwordGenerated && (_jsx("span", { className: styles.passwordHint, children: "New password created. Copy and save it securely." }))] }), _jsx("p", { className: cn(formik.errors.dbPassword && styles.error, styles.remark), children: formik.errors.dbPassword })] }), _jsxs("div", { className: styles.section, children: [_jsx("h2", { className: styles.title, children: "Clone protection" }), _jsx(Select, { label: "Deletion protection", items: (() => {
|
|
169
|
+
}, isDisabled: isCreatingClone, children: "Generate" }), passwordGenerated && (_jsx("span", { className: styles.passwordHint, children: "New password created. Copy and save it securely." }))] }), _jsx("p", { className: cn(formik.errors.dbPassword && styles.error, styles.remark), children: formik.errors.dbPassword })] }), _jsxs("div", { className: styles.section, children: [_jsx("h2", { className: styles.title, children: "Clone protection" }), _jsx(Select, { fullWidth: true, label: "Deletion protection", items: (() => {
|
|
170
170
|
var _a, _b, _c, _d;
|
|
171
171
|
const maxDurationMinutes = (_d = (_c = (_b = (_a = stores.main.instance) === null || _a === void 0 ? void 0 : _a.state) === null || _b === void 0 ? void 0 : _b.cloning) === null || _c === void 0 ? void 0 : _c.protectionMaxDurationMinutes) !== null && _d !== void 0 ? _d : 0;
|
|
172
172
|
const allowForever = maxDurationMinutes === 0;
|
|
@@ -190,7 +190,7 @@ export const CreateClone = observer((props) => {
|
|
|
190
190
|
.map(({ value, children }) => ({ value, children })),
|
|
191
191
|
...(allowForever ? [{ value: '0', children: 'Forever' }] : []),
|
|
192
192
|
];
|
|
193
|
-
})(), value: formik.values.protectionDurationMinutes, onChange: (e) => formik.setFieldValue('protectionDurationMinutes', e.target.value), disabled: isCreatingClone
|
|
193
|
+
})(), value: formik.values.protectionDurationMinutes, onChange: (e) => formik.setFieldValue('protectionDurationMinutes', e.target.value), disabled: isCreatingClone }), _jsxs("p", { className: styles.remark, children: ["Select how long this clone should be protected from deletion. Protected clones cannot be deleted manually or automatically.", _jsx("br", {}), "Check disk space regularly and delete this clone once the work is done."] })] }), _jsxs("div", { className: cn(styles.marginBottom, styles.section), children: [_jsxs(Paper, { className: styles.summary, children: [_jsx(InfoIcon, { className: styles.summaryIcon }), _jsxs("div", { className: styles.params, children: [_jsxs("p", { className: styles.param, children: [_jsx("span", { children: "Data size:" }), _jsx("strong", { children: ((_j = stores.main.instance.state) === null || _j === void 0 ? void 0 : _j.dataSize)
|
|
194
194
|
? formatBytesIEC(stores.main.instance.state.dataSize)
|
|
195
195
|
: '-' })] }), _jsxs("p", { className: styles.param, children: [_jsx("span", { children: "Expected cloning time:" }), _jsxs("strong", { children: [round((_k = stores.main.instance.state) === null || _k === void 0 ? void 0 : _k.cloning.expectedCloningTime, 2), ' ', "s"] })] })] })] }), stores.main.cloneError && (_jsx("div", { className: cn(styles.marginBottom, styles.section), children: _jsx(ErrorStub, { message: stores.main.cloneError }) })), _jsx("div", { className: styles.controls, children: _jsxs(Button, { onClick: formik.submitForm, variant: "primary", size: "medium", isDisabled: isCreatingClone, children: ["Create clone", isCreatingClone && (_jsx(Spinner, { size: "sm", className: styles.spinner }))] }) })] })] }), _jsx("div", { className: styles.form, children: _jsxs("div", { className: styles.snippetContainer, children: [_jsx(SectionTitle, { className: styles.title, tag: "h1", level: 1, text: "The same using CLI" }), _jsx("p", { className: styles.text, children: "Alternatively, you can create a new clone using CLI. Fill the form, copy the command below and paste it into your terminal." }), _jsx(SyntaxHighlight, { wrapLines: true, content: getCliCreateCloneCommand(formik.values, showPassword) }), _jsx(SectionTitle, { className: styles.title, tag: "h2", level: 2, text: "Check clone status" }), _jsx("p", { className: styles.text, children: "To check the status of your newly created clone, use the command below." }), _jsx(SyntaxHighlight, { content: getCliCloneStatus(formik.values.cloneId) })] }) })] })] }));
|
|
196
196
|
});
|
|
@@ -571,7 +571,7 @@ export const Configuration = observer(({ instanceId, switchActiveTab, reload, is
|
|
|
571
571
|
testConnectionState.fetchTuning.message
|
|
572
572
|
? 'error'
|
|
573
573
|
: '', message: testConnectionState.fetchTuning.error ||
|
|
574
|
-
testConnectionState.fetchTuning.message.message })) : null] }), _jsxs(Box, { children: [_jsxs(Box, { children: [_jsx(Typography, { className: styles.subsection, children: "Subsection \"retrieval.spec.logicalRestore\"" }), _jsx("span", { className: classes.grayText, children: "Restoring options." })] }), _jsx(InputWithTooltip, { label: "pg_restore jobs", value: formik.values.restoreParallelJobs, tooltipText: tooltipText.restoreParallelJobs, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('restoreParallelJobs', e.target.value) }), _jsx(InputWithChip, { value: formik.values.pgRestoreCustomOptions, label: "pg_restore customOptions", id: "pgRestoreCustomOptions", tooltipText: tooltipText.pgRestoreCustomOptions, handleDeleteChip: handleDeleteChip, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('pgRestoreCustomOptions', e.target.value) }), _jsx(FormControlLabel, { style: { maxWidth: 'max-content' }, control: _jsx(Checkbox, { name: "restoreIgnoreErrors", checked: formik.values.restoreIgnoreErrors, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('restoreIgnoreErrors', e.target.checked), classes: {
|
|
574
|
+
testConnectionState.fetchTuning.message.message })) : null] }), _jsxs(Box, { children: [_jsxs(Box, { children: [_jsx(Typography, { className: styles.subsection, children: "Subsection \"retrieval.spec.logicalRestore\"" }), _jsx("span", { className: classes.grayText, children: "Restoring options." })] }), _jsx(InputWithTooltip, { label: "pg_restore jobs", value: formik.values.restoreParallelJobs, tooltipText: tooltipText.restoreParallelJobs, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('restoreParallelJobs', e.target.value) }), _jsx(InputWithChip, { value: formik.values.pgRestoreCustomOptions, label: "pg_restore customOptions", id: "pgRestoreCustomOptions", tooltipText: tooltipText.pgRestoreCustomOptions, handleDeleteChip: handleDeleteChip, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('pgRestoreCustomOptions', e.target.value) }), _jsx(InputWithTooltip, { type: "textarea", label: "Restore PostgreSQL configs", value: formik.values.restoreConfigs, tooltipText: tooltipText.restoreConfigs, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('restoreConfigs', e.target.value) }), _jsx(FormControlLabel, { style: { maxWidth: 'max-content' }, control: _jsx(Checkbox, { name: "restoreIgnoreErrors", checked: formik.values.restoreIgnoreErrors, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('restoreIgnoreErrors', e.target.checked), classes: {
|
|
575
575
|
root: classes.checkboxRoot,
|
|
576
576
|
} }), label: 'Ignore errors during logical data restore' })] }), _jsx(Box, { mt: 1, children: _jsx(Typography, { className: styles.subsection, children: "Subsection \"retrieval.refresh\"" }) }), _jsxs("span", { className: classes.grayText, children: ["Define full data refresh on schedule. The process requires at least one additional filesystem mount point. The schedule is to be specified using", ' ', _jsxs("a", { href: "https://en.wikipedia.org/wiki/Cron#Overview", target: "_blank", className: styles.externalLink, children: ["crontab format", _jsx(ExternalIcon, { className: styles.externalIcon })] }), "."] }), _jsx(InputWithTooltip, { label: "timetable", value: formik.values.timetable, tooltipText: tooltipText.timetable, disabled: isConfigurationDisabled, onChange: (e) => formik.setFieldValue('timetable', e.target.value) })] }), _jsxs(Box, { mt: 2, mb: 2, sx: {
|
|
577
577
|
display: 'flex',
|
|
@@ -15,6 +15,7 @@ export declare const tooltipText: {
|
|
|
15
15
|
pgDumpCustomOptions: () => JSX.Element;
|
|
16
16
|
restoreParallelJobs: () => JSX.Element;
|
|
17
17
|
pgRestoreCustomOptions: () => JSX.Element;
|
|
18
|
+
restoreConfigs: () => JSX.Element;
|
|
18
19
|
timetable: () => JSX.Element;
|
|
19
20
|
tuningParams: () => JSX.Element;
|
|
20
21
|
};
|
|
@@ -16,6 +16,7 @@ export const tooltipText = {
|
|
|
16
16
|
pgDumpCustomOptions: () => (_jsx("div", { children: "pg_dump options to be used to create a database dump, for example: '--exclude-schema=repack --exclude-schema=\"camelStyleSchemaName\"'. Note that due to security reasons, the current implementation supports only letters, numbers, hyphen, underscore, equal sign, and double quotes." })),
|
|
17
17
|
restoreParallelJobs: () => (_jsx("div", { children: "Number of parallel workers used to restore databases from dump to PostgreSQL managed by DBLab. For initial data retrieval (the first data refresh), it is recommended to match the number of available vCPUs on the machine running DBLab. This yields faster restore times but can increase CPU and disk I/O usage on that machine (up to temporary resource saturation). For subsequent refreshes, if DBLab is in continuous use, it is recommended to reduce this value by 50% to reserve capacity for normal DBLab operations (such as working with clones)." })),
|
|
18
18
|
pgRestoreCustomOptions: () => (_jsx("div", { children: "pg_restore options to be used to restore from a database dump, for example: '--exclude-schema=repack --exclude-schema=\"camelStyleSchemaName\"'. Note that due to security reasons, the current implementation supports only letters, numbers, hyphen, underscore, equal sign, and double quotes." })),
|
|
19
|
+
restoreConfigs: () => (_jsxs("div", { children: ["PostgreSQL configuration parameters applied during logical restore (one", ' ', _jsx("span", { className: styles.firaCodeFont, children: "parameter=value" }), " per line). These settings are written to", ' ', _jsx("span", { className: styles.firaCodeFont, children: "postgresql.conf" }), " before restore starts and do not affect clones. Useful for tuning restore performance, for example:", _jsx("br", {}), _jsx("span", { className: styles.firaCodeFont, children: "maintenance_work_mem=8GB" }), _jsx("br", {}), _jsx("span", { className: styles.firaCodeFont, children: "max_parallel_maintenance_workers=7" }), _jsx("br", {}), _jsx("span", { className: styles.firaCodeFont, children: "shared_preload_libraries=" }), _jsx("br", {}), _jsx("span", { className: styles.firaCodeFont, children: "fsync=off" })] })),
|
|
19
20
|
timetable: () => (_jsxs("div", { children: ["Schedule for full data refreshes, in", ' ', _jsx("a", { target: '_blank', href: 'https://en.wikipedia.org/wiki/Cron#Overview', className: styles.externalLink, children: "crontab format" }), "."] })),
|
|
20
21
|
tuningParams: () => (_jsxs("div", { children: ["Query tuning parameters. These are essential to ensure that cloned PostgreSQL instances generate the same plans as the source (specifically, they are crucial for query performance troubleshooting and optimization, including working with EXPLAIN plans). For details, see the", ' ', _jsx("a", { target: '_blank', href: 'https://postgres.ai/docs/how-to-guides/administration/postgresql-configuration#postgresql-configuration-in-clones', className: styles.externalLink, children: "docs" }), "."] })),
|
|
21
22
|
};
|
|
@@ -98,6 +98,7 @@ export declare class MainStore {
|
|
|
98
98
|
dumpIgnoreErrors: boolean | undefined;
|
|
99
99
|
restoreParallelJobs: string | number | undefined;
|
|
100
100
|
restoreIgnoreErrors: boolean | undefined;
|
|
101
|
+
restoreConfigs: string;
|
|
101
102
|
pgDumpCustomOptions: string;
|
|
102
103
|
pgRestoreCustomOptions: string;
|
|
103
104
|
dockerTag?: string | undefined;
|
|
@@ -15,8 +15,10 @@ export const formatTuningParamsToObj = (tuningParams) => {
|
|
|
15
15
|
if (tuningParams) {
|
|
16
16
|
const tuningParamsArr = tuningParams.split('\n');
|
|
17
17
|
tuningParamsArr.forEach((param) => {
|
|
18
|
-
const
|
|
19
|
-
|
|
18
|
+
const eqIndex = param.indexOf('=');
|
|
19
|
+
if (eqIndex !== -1) {
|
|
20
|
+
formattedTuningParams[param.substring(0, eqIndex)] = param.substring(eqIndex + 1);
|
|
21
|
+
}
|
|
20
22
|
});
|
|
21
23
|
}
|
|
22
24
|
return formattedTuningParams;
|
|
@@ -41,6 +41,9 @@ export declare type configTypes = {
|
|
|
41
41
|
customOptions?: string[];
|
|
42
42
|
parallelJobs?: string | number;
|
|
43
43
|
ignoreErrors?: boolean;
|
|
44
|
+
configs?: {
|
|
45
|
+
[key: string]: string;
|
|
46
|
+
};
|
|
44
47
|
};
|
|
45
48
|
};
|
|
46
49
|
};
|
|
@@ -62,6 +65,7 @@ export declare const formatConfig: (config: configTypes) => {
|
|
|
62
65
|
dumpIgnoreErrors: boolean | undefined;
|
|
63
66
|
restoreParallelJobs: string | number | undefined;
|
|
64
67
|
restoreIgnoreErrors: boolean | undefined;
|
|
68
|
+
restoreConfigs: string;
|
|
65
69
|
pgDumpCustomOptions: string;
|
|
66
70
|
pgRestoreCustomOptions: string;
|
|
67
71
|
dockerTag?: string | undefined;
|
|
@@ -32,6 +32,15 @@ export const formatConfig = (config) => {
|
|
|
32
32
|
dumpIgnoreErrors: (_27 = (_26 = (_25 = (_24 = config.retrieval) === null || _24 === void 0 ? void 0 : _24.spec) === null || _25 === void 0 ? void 0 : _25.logicalDump) === null || _26 === void 0 ? void 0 : _26.options) === null || _27 === void 0 ? void 0 : _27.ignoreErrors,
|
|
33
33
|
restoreParallelJobs: (_31 = (_30 = (_29 = (_28 = config.retrieval) === null || _28 === void 0 ? void 0 : _28.spec) === null || _29 === void 0 ? void 0 : _29.logicalRestore) === null || _30 === void 0 ? void 0 : _30.options) === null || _31 === void 0 ? void 0 : _31.parallelJobs,
|
|
34
34
|
restoreIgnoreErrors: (_35 = (_34 = (_33 = (_32 = config.retrieval) === null || _32 === void 0 ? void 0 : _32.spec) === null || _33 === void 0 ? void 0 : _33.logicalRestore) === null || _34 === void 0 ? void 0 : _34.options) === null || _35 === void 0 ? void 0 : _35.ignoreErrors,
|
|
35
|
+
restoreConfigs: (() => {
|
|
36
|
+
var _a, _b, _c, _d;
|
|
37
|
+
const configs = (_d = (_c = (_b = (_a = config.retrieval) === null || _a === void 0 ? void 0 : _a.spec) === null || _b === void 0 ? void 0 : _b.logicalRestore) === null || _c === void 0 ? void 0 : _c.options) === null || _d === void 0 ? void 0 : _d.configs;
|
|
38
|
+
if (!configs || Object.keys(configs).length === 0)
|
|
39
|
+
return '';
|
|
40
|
+
return Object.entries(configs)
|
|
41
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
42
|
+
.join('\n');
|
|
43
|
+
})(),
|
|
35
44
|
pgDumpCustomOptions: formatDumpCustomOptions((_40 = (_39 = (_38 = (_37 = (_36 = config.retrieval) === null || _36 === void 0 ? void 0 : _36.spec) === null || _37 === void 0 ? void 0 : _37.logicalDump) === null || _38 === void 0 ? void 0 : _38.options) === null || _39 === void 0 ? void 0 : _39.customOptions) !== null && _40 !== void 0 ? _40 : null),
|
|
36
45
|
pgRestoreCustomOptions: formatDumpCustomOptions((_45 = (_44 = (_43 = (_42 = (_41 = config.retrieval) === null || _41 === void 0 ? void 0 : _41.spec) === null || _42 === void 0 ? void 0 : _42.logicalRestore) === null || _43 === void 0 ? void 0 : _43.options) === null || _44 === void 0 ? void 0 : _44.customOptions) !== null && _45 !== void 0 ? _45 : null),
|
|
37
46
|
};
|
package/package.json
CHANGED
package/pages/Clone/index.tsx
CHANGED
|
@@ -651,7 +651,7 @@ export const Clone = observer((props: Props) => {
|
|
|
651
651
|
</p>
|
|
652
652
|
<div style={{ marginTop: '8px', marginBottom: '16px' }}>
|
|
653
653
|
<Select
|
|
654
|
-
label="Deletion
|
|
654
|
+
label={"Deletion\u00a0protection"}
|
|
655
655
|
items={
|
|
656
656
|
clone.protected && clone.protectedTillDate
|
|
657
657
|
? [
|
|
@@ -663,7 +663,7 @@ export const Clone = observer((props: Props) => {
|
|
|
663
663
|
value={getCurrentProtectionValue()}
|
|
664
664
|
onChange={handleProtectionChange}
|
|
665
665
|
disabled={isDisabledControls}
|
|
666
|
-
style={{ minWidth:
|
|
666
|
+
style={{ minWidth: 200 }}
|
|
667
667
|
/>
|
|
668
668
|
{isUpdatingClone && <Spinner size="sm" className={classes.spinner} />}
|
|
669
669
|
</div>
|
|
@@ -393,6 +393,7 @@ export const CreateClone = observer((props: Props) => {
|
|
|
393
393
|
<h2 className={styles.title}>Clone protection</h2>
|
|
394
394
|
|
|
395
395
|
<Select
|
|
396
|
+
fullWidth
|
|
396
397
|
label="Deletion protection"
|
|
397
398
|
items={(() => {
|
|
398
399
|
const maxDurationMinutes = stores.main.instance?.state?.cloning?.protectionMaxDurationMinutes ?? 0
|
|
@@ -423,7 +424,6 @@ export const CreateClone = observer((props: Props) => {
|
|
|
423
424
|
formik.setFieldValue('protectionDurationMinutes', e.target.value)
|
|
424
425
|
}
|
|
425
426
|
disabled={isCreatingClone}
|
|
426
|
-
style={{ minWidth: 100 }}
|
|
427
427
|
/>
|
|
428
428
|
|
|
429
429
|
<p className={styles.remark}>
|
|
@@ -1120,6 +1120,16 @@ export const Configuration = observer(
|
|
|
1120
1120
|
)
|
|
1121
1121
|
}
|
|
1122
1122
|
/>
|
|
1123
|
+
<InputWithTooltip
|
|
1124
|
+
type="textarea"
|
|
1125
|
+
label="Restore PostgreSQL configs"
|
|
1126
|
+
value={formik.values.restoreConfigs}
|
|
1127
|
+
tooltipText={tooltipText.restoreConfigs}
|
|
1128
|
+
disabled={isConfigurationDisabled}
|
|
1129
|
+
onChange={(e) =>
|
|
1130
|
+
formik.setFieldValue('restoreConfigs', e.target.value)
|
|
1131
|
+
}
|
|
1132
|
+
/>
|
|
1123
1133
|
<FormControlLabel
|
|
1124
1134
|
style={{ maxWidth: 'max-content' }}
|
|
1125
1135
|
control={
|
|
@@ -138,6 +138,26 @@ export const tooltipText = {
|
|
|
138
138
|
hyphen, underscore, equal sign, and double quotes.
|
|
139
139
|
</div>
|
|
140
140
|
),
|
|
141
|
+
restoreConfigs: () => (
|
|
142
|
+
<div>
|
|
143
|
+
PostgreSQL configuration parameters applied during logical restore (one{' '}
|
|
144
|
+
<span className={styles.firaCodeFont}>parameter=value</span> per line).
|
|
145
|
+
These settings are written to{' '}
|
|
146
|
+
<span className={styles.firaCodeFont}>postgresql.conf</span> before
|
|
147
|
+
restore starts and do not affect clones. Useful for tuning restore
|
|
148
|
+
performance, for example:
|
|
149
|
+
<br />
|
|
150
|
+
<span className={styles.firaCodeFont}>maintenance_work_mem=8GB</span>
|
|
151
|
+
<br />
|
|
152
|
+
<span className={styles.firaCodeFont}>
|
|
153
|
+
max_parallel_maintenance_workers=7
|
|
154
|
+
</span>
|
|
155
|
+
<br />
|
|
156
|
+
<span className={styles.firaCodeFont}>shared_preload_libraries=</span>
|
|
157
|
+
<br />
|
|
158
|
+
<span className={styles.firaCodeFont}>fsync=off</span>
|
|
159
|
+
</div>
|
|
160
|
+
),
|
|
141
161
|
timetable: () => (
|
|
142
162
|
<div>
|
|
143
163
|
Schedule for full data refreshes, in{' '}
|
|
@@ -28,6 +28,7 @@ export type FormValues = {
|
|
|
28
28
|
dumpIgnoreErrors: boolean
|
|
29
29
|
restoreParallelJobs: string
|
|
30
30
|
restoreIgnoreErrors: boolean
|
|
31
|
+
restoreConfigs: string
|
|
31
32
|
pgDumpCustomOptions: string
|
|
32
33
|
pgRestoreCustomOptions: string
|
|
33
34
|
}
|
|
@@ -60,6 +61,7 @@ export const useForm = (onSubmit: (values: FormValues) => void) => {
|
|
|
60
61
|
databases: '',
|
|
61
62
|
dumpParallelJobs: '',
|
|
62
63
|
restoreParallelJobs: '',
|
|
64
|
+
restoreConfigs: '',
|
|
63
65
|
pgDumpCustomOptions: '',
|
|
64
66
|
pgRestoreCustomOptions: '',
|
|
65
67
|
dumpIgnoreErrors: false,
|
|
Binary file
|
|
@@ -39,8 +39,10 @@ export const formatTuningParamsToObj = (tuningParams: string | undefined) => {
|
|
|
39
39
|
if (tuningParams) {
|
|
40
40
|
const tuningParamsArr = tuningParams.split('\n')
|
|
41
41
|
tuningParamsArr.forEach((param) => {
|
|
42
|
-
const
|
|
43
|
-
|
|
42
|
+
const eqIndex = param.indexOf('=')
|
|
43
|
+
if (eqIndex !== -1) {
|
|
44
|
+
formattedTuningParams[param.substring(0, eqIndex)] = param.substring(eqIndex + 1)
|
|
45
|
+
}
|
|
44
46
|
})
|
|
45
47
|
}
|
|
46
48
|
|
|
@@ -51,6 +51,7 @@ export type configTypes = {
|
|
|
51
51
|
customOptions?: string[]
|
|
52
52
|
parallelJobs?: string | number
|
|
53
53
|
ignoreErrors?: boolean
|
|
54
|
+
configs?: { [key: string]: string }
|
|
54
55
|
}
|
|
55
56
|
}
|
|
56
57
|
}
|
|
@@ -103,6 +104,14 @@ export const formatConfig = (config: configTypes) => {
|
|
|
103
104
|
config.retrieval?.spec?.logicalRestore?.options?.parallelJobs,
|
|
104
105
|
restoreIgnoreErrors:
|
|
105
106
|
config.retrieval?.spec?.logicalRestore?.options?.ignoreErrors,
|
|
107
|
+
restoreConfigs: (() => {
|
|
108
|
+
const configs =
|
|
109
|
+
config.retrieval?.spec?.logicalRestore?.options?.configs
|
|
110
|
+
if (!configs || Object.keys(configs).length === 0) return ''
|
|
111
|
+
return Object.entries(configs)
|
|
112
|
+
.map(([k, v]) => `${k}=${v}`)
|
|
113
|
+
.join('\n')
|
|
114
|
+
})(),
|
|
106
115
|
pgDumpCustomOptions: formatDumpCustomOptions(
|
|
107
116
|
(config.retrieval?.spec?.logicalDump?.options
|
|
108
117
|
?.customOptions as string[] | undefined) ?? null,
|
|
Binary file
|