Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,14 @@ function ManagePartitioningDialog({
const {
control,
handleSubmit,
watch,
trigger,
formState: {errors, isValid},
} = useManagePartitioningForm({
initialValue,
maxSplitSizeBytes: DEFAULT_PARTITION_SIZE_TO_SPLIT_BYTES,
});
const splitSizeEnabled = watch('splitSizeEnabled');

const handleApply = handleSubmit(async (data) => {
setApiError(null);
Expand All @@ -76,6 +78,27 @@ function ManagePartitioningDialog({
<Flex direction="column" gap="3" alignItems="flex-start">
<Text variant="subheader-1">{i18n('title_partitioning')}</Text>

<Flex className={b('row')} gap="3" alignItems="center">
<label htmlFor="splitSizeEnabled" className={b('label')}>
{i18n('field_size')}
</label>

<Controller
name="splitSizeEnabled"
control={control}
render={({field}) => (
<Switch
id="splitSizeEnabled"
checked={field.value}
onUpdate={(next) => {
field.onChange(next);
trigger('splitSize');
}}
/>
)}
/>
</Flex>

<Flex className={b('row')} gap="3" alignItems="center">
<label htmlFor="splitSize" className={b('label')}>
{i18n('field_split-size')}
Expand All @@ -90,6 +113,7 @@ function ManagePartitioningDialog({
type="number"
value={field.value}
onUpdate={field.onChange}
disabled={!splitSizeEnabled}
className={b('input')}
errorMessage={errors.splitSize?.message}
validationState={errors.splitSize ? 'invalid' : undefined}
Expand All @@ -101,6 +125,7 @@ function ManagePartitioningDialog({
<SplitUnitSelect
value={unitField.value}
options={UNIT_OPTIONS}
disabled={!splitSizeEnabled}
onChange={(nextUnit) => {
unitField.onChange(nextUnit);
trigger('splitSize');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const b = cn('split-unit-select');
interface SplitUnitSelectProps {
value: string;
options: UnitOptionsType;
disabled?: boolean;
width?: number;
size?: SelectProps['size'];
onChange: (nextValue: string) => void;
Expand All @@ -22,6 +23,7 @@ interface SplitUnitSelectProps {
export function SplitUnitSelect({
value,
options,
disabled = false,
width = 55,
size = 's',
onChange,
Expand All @@ -44,7 +46,13 @@ export function SplitUnitSelect({

const renderControl: SelectProps['renderControl'] = React.useCallback(
() => (
<Button type="button" className={b('button')} size={size} onClick={handleToggleOpen}>
<Button
type="button"
className={b('button')}
size={size}
onClick={handleToggleOpen}
disabled={disabled}
>
<Flex
justifyContent="space-between"
alignItems="center"
Expand All @@ -56,11 +64,12 @@ export function SplitUnitSelect({
</Flex>
</Button>
),
[handleToggleOpen, selectedLabel, size],
[disabled, handleToggleOpen, selectedLabel, size],
);

return (
<Select
disabled={disabled}
size={size}
className={b('select')}
options={options}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {managePartitioningSchema} from '../utils';

describe('managePartitioningSchema', () => {
test('allows submitting without split size when split-by-size is disabled', () => {
const result = managePartitioningSchema().safeParse({
splitSizeEnabled: false,
splitSize: '',
splitUnit: 'gb',
loadEnabled: true,
minimum: '10',
maximum: '20',
});

expect(result.success).toBe(true);
});

test('requires split size when split-by-size is enabled', () => {
const result = managePartitioningSchema().safeParse({
splitSizeEnabled: true,
splitSize: '',
splitUnit: 'gb',
loadEnabled: true,
minimum: '10',
maximum: '20',
});

expect(result.success).toBe(false);
expect(result.error?.flatten().fieldErrors.splitSize).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const DEFAULT_MAX_SPLIT_SIZE_GB = formatBytes({
});

export const DEFAULT_MANAGE_PARTITIONING_VALUE: ManagePartitioningFormState = {
splitSizeEnabled: true,
splitSize: DEFAULT_MAX_SPLIT_SIZE_GB,
splitUnit: 'gb',
loadEnabled: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"title_limits": "Limits",

"field_split-size": "Split Size",
"field_size": "Size",
"context_split-size-maximum": "{{maxGb}} GB maximum",
"field_load": "Load",
"field_minimum": "Minimum",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type {BytesSizes} from '../../../../../../utils/bytesParsers';

// Raw form model: stored and used for initialValues (TextInput works with strings)
export interface ManagePartitioningFormState {
splitSizeEnabled: boolean;
splitSize: string;
splitUnit: BytesSizes;
loadEnabled: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ export const splitUnitSchema = z.custom<BytesSizes>((value) => {
return value in sizes;
}, i18n('error_invalid-unit'));

// Required positive number (> 0)
const requiredPositiveNumber = (requiredMessage: string) =>
// Optional positive number (> 0)
const optionalPositiveNumber = () =>
preprocessEmptyStringToUndefined(
z.coerce
.number({
required_error: requiredMessage,
invalid_type_error: requiredMessage,
invalid_type_error: i18n('error_required'),
})
.gt(0),
.gt(0)
.optional(),
);

// Required positive integer (> 0)
Expand All @@ -56,7 +56,8 @@ export const managePartitioningSchema = (
) =>
z
.object({
splitSize: requiredPositiveNumber(i18n('error_required')),
splitSizeEnabled: z.boolean(),
splitSize: optionalPositiveNumber(),
splitUnit: splitUnitSchema,

loadEnabled: z.boolean(),
Expand All @@ -65,22 +66,35 @@ export const managePartitioningSchema = (
maximum: requiredPositiveInt(i18n('error_required')),
})
.superRefine((data, ctx) => {
const {bytes, partitionSizeMb} = splitToPartitionSizeMb(data.splitSize, data.splitUnit);

if (bytes > maxSplitSizeBytes) {
if (data.splitSizeEnabled && data.splitSize === undefined) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ['splitSize'],
message: i18n('error_value-greater-maximum'),
message: i18n('error_required'),
});
}

if (partitionSizeMb < 1) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ['splitSize'],
message: i18n('error_value-too-small'),
});
if (data.splitSizeEnabled && data.splitSize !== undefined) {
const {bytes, partitionSizeMb} = splitToPartitionSizeMb(
data.splitSize,
data.splitUnit,
);

if (bytes > maxSplitSizeBytes) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ['splitSize'],
message: i18n('error_value-greater-maximum'),
});
}

if (partitionSizeMb < 1) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ['splitSize'],
message: i18n('error_value-too-small'),
});
}
}

if (data.minimum > data.maximum) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {alterPartitioningSQL} from '../../../../../../store/reducers/tablePartitioning/tablePartitioning';
import {sizes} from '../../../../../../utils/bytesParsers';
import {DEFAULT_PARTITION_SIZE_TO_SPLIT_BYTES} from '../constants';
import {prepareUpdatePartitioningRequest} from '../utils';

describe('prepareUpdatePartitioningRequest', () => {
test('sets splitBySize=false and uses fallback partition size when split-by-size is disabled', () => {
const result = prepareUpdatePartitioningRequest(
{
splitSizeEnabled: false,
splitSize: '',
splitUnit: 'gb',
loadEnabled: true,
minimum: '3',
maximum: '10',
},
'/Root',
'/Root/table',
);

expect(result).toEqual({
database: '/Root',
path: '/Root/table',
value: {
splitBySize: false,
splitByLoad: true,
partitionSizeMb: Math.round(DEFAULT_PARTITION_SIZE_TO_SPLIT_BYTES / sizes.mb.value),
minPartitions: 3,
maxPartitions: 10,
},
});
});
});

describe('alterPartitioningSQL', () => {
test('generates AUTO_PARTITIONING_BY_SIZE = DISABLED when split-by-size is disabled', () => {
const query = alterPartitioningSQL('/Root/table', {
splitBySize: false,
splitByLoad: true,
partitionSizeMb: 2048,
minPartitions: 3,
maxPartitions: 10,
});

expect(query).toContain('AUTO_PARTITIONING_BY_SIZE = DISABLED');
expect(query).toContain('AUTO_PARTITIONING_BY_LOAD = ENABLED');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,12 @@ const prepareManagePartitioningDialogConfig = (
) => {
const policy = partitionConfig?.PartitioningPolicy;

const splitBySizeEnabled = Number(policy?.SizeToSplit) > 0;
const splitByLoadEnabled = Boolean(policy?.SplitByLoadSettings?.Enabled);

const bytes = Number(policy?.SizeToSplit ?? DEFAULT_PARTITION_SIZE_TO_SPLIT_BYTES);
const bytes = Number(
splitBySizeEnabled ? policy?.SizeToSplit : DEFAULT_PARTITION_SIZE_TO_SPLIT_BYTES,
);
const size = formatBytes({
value: bytes,
withSizeLabel: false,
Expand All @@ -253,7 +256,8 @@ const prepareManagePartitioningDialogConfig = (
const unit = getBytesSizeUnit(bytes);

return {
splitSize: size,
splitSizeEnabled: splitBySizeEnabled,
splitSize: splitBySizeEnabled ? size : '',
splitUnit: unit,
loadEnabled: splitByLoadEnabled,
minimum: String(progress?.minPartitions),
Expand Down
13 changes: 12 additions & 1 deletion src/containers/Tenant/Diagnostics/Overview/TableInfo/utils.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
import type {UpdateTablePartitioningParams} from '../../../../../types/store/partitioning';
import {sizes} from '../../../../../utils/bytesParsers';

import type {ManagePartitioningFormState} from './ManagePartitioningDialog/types';
import {splitToPartitionSizeMb} from './ManagePartitioningDialog/utils';
import {DEFAULT_PARTITION_SIZE_TO_SPLIT_BYTES} from './constants';

export function prepareUpdatePartitioningRequest(
value: ManagePartitioningFormState,
database: string,
path: string,
): UpdateTablePartitioningParams {
const {partitionSizeMb} = splitToPartitionSizeMb(Number(value.splitSize), value.splitUnit);
const splitSizeValue = Number(value.splitSize);
const hasValidSplitSize = Number.isFinite(splitSizeValue) && splitSizeValue > 0;
const fallbackPartitionSizeMb = Math.round(
DEFAULT_PARTITION_SIZE_TO_SPLIT_BYTES / sizes.mb.value,
);
const {partitionSizeMb} =
value.splitSizeEnabled && hasValidSplitSize
? splitToPartitionSizeMb(splitSizeValue, value.splitUnit)
: {partitionSizeMb: fallbackPartitionSizeMb};

return {
value: {
splitBySize: value.splitSizeEnabled,
partitionSizeMb,
minPartitions: Number(value.minimum),
maxPartitions: Number(value.maximum),
Expand Down
5 changes: 3 additions & 2 deletions src/store/reducers/tablePartitioning/tablePartitioning.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import {QUERY_TECHNICAL_MARK} from '../../../utils/constants';
import {isQueryErrorResponse} from '../../../utils/query';
import {api} from '../api';

function alterPartitioningSQL(path: string, values: UpdateTablePartitioningValues) {
export function alterPartitioningSQL(path: string, values: UpdateTablePartitioningValues) {
const safePath = path.replace(/`/g, '``');

const bySize = values.splitBySize ? 'ENABLED' : 'DISABLED';
const byLoad = values.splitByLoad ? 'ENABLED' : 'DISABLED';

return `${QUERY_TECHNICAL_MARK}
ALTER TABLE \`${safePath}\` SET (
AUTO_PARTITIONING_BY_SIZE = ENABLED,
AUTO_PARTITIONING_BY_SIZE = ${bySize},
AUTO_PARTITIONING_PARTITION_SIZE_MB = ${values.partitionSizeMb},
AUTO_PARTITIONING_MIN_PARTITIONS_COUNT = ${values.minPartitions},
AUTO_PARTITIONING_MAX_PARTITIONS_COUNT = ${values.maxPartitions},
Expand Down
1 change: 1 addition & 0 deletions src/types/store/partitioning.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface UpdateTablePartitioningValues {
splitBySize: boolean;
partitionSizeMb: number;
minPartitions: number;
maxPartitions: number;
Expand Down