Updated to a tab

This commit is contained in:
Sung-Hyun Kang
2025-01-26 18:11:50 -06:00
parent 05e2d0ac29
commit 5dfaa9f0f8
4 changed files with 59 additions and 85 deletions

View File

@@ -7,6 +7,10 @@ import {
ContainerPolicyComponent,
ContainerPolicyComponentProps,
} from "Explorer/Controls/Settings/SettingsSubComponents/ContainerPolicyComponent";
import {
ThroughputBucketsComponent,
ThroughputBucketsComponentProps,
} from "Explorer/Controls/Settings/SettingsSubComponents/ThroughputInputComponents/ThroughputBucketsComponent";
import { useDatabases } from "Explorer/useDatabases";
import { isFullTextSearchEnabled, isVectorSearchEnabled } from "Utils/CapabilityUtils";
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
@@ -338,7 +342,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
this.state.isIndexingPolicyDirty ||
this.state.isConflictResolutionDirty ||
this.state.isComputedPropertiesDirty ||
(!!this.state.currentMongoIndexes && this.state.isMongoIndexingPolicySaveable)
(!!this.state.currentMongoIndexes && this.state.isMongoIndexingPolicySaveable) ||
this.state.isThroughputBucketsSaveable
);
};
@@ -350,7 +355,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
this.state.isIndexingPolicyDirty ||
this.state.isConflictResolutionDirty ||
this.state.isComputedPropertiesDirty ||
(!!this.state.currentMongoIndexes && this.state.isMongoIndexingPolicyDiscardable)
(!!this.state.currentMongoIndexes && this.state.isMongoIndexingPolicyDiscardable) ||
this.state.isThroughputBucketsSaveable
);
};
@@ -1055,6 +1061,24 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
}
}
if (this.throughputBucketsEnabled && this.state.isThroughputBucketsSaveable) {
const updatedOffer: DataModels.Offer = await updateOffer({
databaseId: this.collection.databaseId,
collectionId: this.collection.id(),
currentOffer: this.collection.offer(),
autopilotThroughput: this.collection.offer().autoscaleMaxThroughput
? this.collection.offer().autoscaleMaxThroughput
: undefined,
manualThroughput: this.collection.offer().manualThroughput
? this.collection.offer().manualThroughput
: undefined,
throughputBuckets: this.state.throughputBuckets,
});
this.collection.offer(updatedOffer);
this.offer = updatedOffer;
this.setState({ isThroughputBucketsSaveable: false });
}
if (this.state.isScaleSaveable) {
const updateOfferParams: DataModels.UpdateOfferParams = {
databaseId: this.collection.databaseId,
@@ -1062,8 +1086,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
currentOffer: this.collection.offer(),
autopilotThroughput: this.state.isAutoPilotSelected ? this.state.autoPilotThroughput : undefined,
manualThroughput: this.state.isAutoPilotSelected ? undefined : this.state.throughput,
...(this.throughputBucketsEnabled &&
this.state.isThroughputBucketsSaveable && { throughputBuckets: this.state.throughputBuckets }),
};
if (this.hasProvisioningTypeChanged()) {
if (this.state.isAutoPilotSelected) {
@@ -1130,11 +1152,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
onMaxAutoPilotThroughputChange: this.onMaxAutoPilotThroughputChange,
onScaleSaveableChange: this.onScaleSaveableChange,
onScaleDiscardableChange: this.onScaleDiscardableChange,
throughputBucketsBaseline: this.state.throughputBucketsBaseline,
throughputBuckets: this.state.throughputBuckets,
enableThroughputBuckets: this.isCollectionSettingsTab && this.throughputBucketsEnabled,
onThroughputBucketChange: this.onThroughputBucketChange,
onThroughputBucketsSaveableChange: this.onThroughputBucketsSaveableChange,
throughputError: this.state.throughputError,
};
@@ -1242,6 +1259,13 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
onConflictResolutionDirtyChange: this.onConflictResolutionDirtyChange,
};
const throughputBucketsComponentProps: ThroughputBucketsComponentProps = {
currentBuckets: this.state.throughputBuckets,
throughputBucketsBaseline: this.state.throughputBucketsBaseline,
onBucketsChange: this.onThroughputBucketChange,
onSaveableChange: this.onThroughputBucketsSaveableChange,
};
const partitionKeyComponentProps: PartitionKeyComponentProps = {
database: useDatabases.getState().findDatabaseWithId(this.collection.databaseId),
collection: this.collection,
@@ -1304,6 +1328,13 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
});
}
if (this.throughputBucketsEnabled) {
tabs.push({
tab: SettingsV2TabTypes.ThroughputBucketsTab,
content: <ThroughputBucketsComponent {...throughputBucketsComponentProps} />,
});
}
const pivotProps: IPivotProps = {
onLinkClick: this.onPivotChange,
selectedKey: SettingsV2TabTypes[this.state.selectedTab],

View File

@@ -16,7 +16,6 @@ import {
titleAndInputStackProps,
} from "../SettingsRenderUtils";
import { hasDatabaseSharedThroughput } from "../SettingsUtils";
import { ThroughputBucketsComponent } from "./ThroughputInputComponents/ThroughputBucketsComponent";
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
export interface ScaleComponentProps {
@@ -34,22 +33,10 @@ export interface ScaleComponentProps {
onMaxAutoPilotThroughputChange: (newThroughput: number) => void;
onScaleSaveableChange: (isScaleSaveable: boolean) => void;
onScaleDiscardableChange: (isScaleDiscardable: boolean) => void;
throughputBuckets: DataModels.ThroughputBucket[];
throughputBucketsBaseline: DataModels.ThroughputBucket[];
enableThroughputBuckets: boolean;
onThroughputBucketChange: (throughputBuckets: DataModels.ThroughputBucket[]) => void;
onThroughputBucketsSaveableChange: (isSaveable: boolean) => void;
throughputError?: string;
}
interface ScaleComponentState {
isThroughputSaveable: boolean;
isThroughputBucketsSaveable: boolean;
isThroughputDiscardable: boolean;
isThroughputBucketsDiscardable: boolean;
}
export class ScaleComponent extends React.Component<ScaleComponentProps, ScaleComponentState> {
export class ScaleComponent extends React.Component<ScaleComponentProps> {
private isEmulator: boolean;
private offer: DataModels.Offer;
private databaseId: string;
@@ -61,12 +48,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps, ScaleCo
this.offer = this.props.database?.offer() || this.props.collection?.offer();
this.databaseId = this.props.database?.id() || this.props.collection.databaseId;
this.collectionId = this.props.collection?.id();
this.state = {
isThroughputSaveable: false,
isThroughputBucketsSaveable: false,
isThroughputDiscardable: false,
isThroughputBucketsDiscardable: false,
};
}
public isAutoScaleEnabled = (): boolean => {
@@ -80,6 +61,7 @@ export class ScaleComponent extends React.Component<ScaleComponentProps, ScaleCo
capability.name.toLowerCase() === Constants.CapabilityNames.EnableAutoScale.toLowerCase()
);
});
return !!enableAutoScaleCapability;
};
@@ -99,6 +81,7 @@ export class ScaleComponent extends React.Component<ScaleComponentProps, ScaleCo
if (userContext.isTryCosmosDBSubscription) {
return SharedConstants.CollectionCreation.DefaultCollectionRUs400;
}
return this.offer?.minimumThroughput || SharedConstants.CollectionCreation.DefaultCollectionRUs400;
};
@@ -154,8 +137,8 @@ export class ScaleComponent extends React.Component<ScaleComponentProps, ScaleCo
maxAutoPilotThroughputBaseline={this.props.autoPilotThroughputBaseline}
onMaxAutoPilotThroughputChange={this.props.onMaxAutoPilotThroughputChange}
spendAckChecked={false}
onScaleSaveableChange={this.handleThroughputSaveableChange}
onScaleDiscardableChange={this.handleThroughputDiscardableChange}
onScaleSaveableChange={this.props.onScaleSaveableChange}
onScaleDiscardableChange={this.props.onScaleDiscardableChange}
usageSizeInKB={this.props.collection?.usageSizeInKB()}
throughputError={this.props.throughputError}
instantMaximumThroughput={this.offer?.instantMaximumThroughput}
@@ -184,42 +167,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps, ScaleCo
);
}
private updateScaleSettingsState = (updates: Partial<ScaleComponentState>) => {
this.setState(
(prevState) => {
const hasChanges = Object.keys(updates).some(
(key) => prevState[key as keyof ScaleComponentState] !== updates[key as keyof ScaleComponentState],
);
return hasChanges ? { ...prevState, ...updates } : null;
},
() => {
const isSaveable = this.state.isThroughputSaveable
? this.state.isThroughputDiscardable || this.state.isThroughputBucketsSaveable
: this.state.isThroughputBucketsSaveable;
const isDiscardable = this.state.isThroughputDiscardable || this.state.isThroughputBucketsDiscardable;
this.props.onScaleSaveableChange(isSaveable);
this.props.onThroughputBucketsSaveableChange(this.state.isThroughputBucketsSaveable);
this.props.onScaleDiscardableChange(isDiscardable);
},
);
};
private handleThroughputSaveableChange = (isSaveable: boolean) => {
this.updateScaleSettingsState({ isThroughputSaveable: isSaveable });
};
private handleThroughputDiscardableChange = (isDiscardable: boolean) => {
this.updateScaleSettingsState({ isThroughputDiscardable: isDiscardable });
};
private handleThroughputBucketsSaveableChange = (isSaveable: boolean) => {
this.updateScaleSettingsState({ isThroughputBucketsSaveable: isSaveable });
};
private handleThroughputBucketsDiscardableChange = (isDiscardable: boolean) => {
this.updateScaleSettingsState({ isThroughputBucketsDiscardable: isDiscardable });
};
public render(): JSX.Element {
return (
<Stack {...subComponentStackProps}>
@@ -235,15 +182,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps, ScaleCo
<MessageBar messageBarType={MessageBarType.warning}>{this.getInitialNotificationElement()}</MessageBar>
)}
{!this.isAutoScaleEnabled() && <Stack {...subComponentStackProps}>{this.getThroughputInputComponent()}</Stack>}
{this.props.enableThroughputBuckets && (
<ThroughputBucketsComponent
currentBuckets={this.props.throughputBuckets}
throughputBucketsBaseline={this.props.throughputBucketsBaseline}
onBucketsChange={this.props.onThroughputBucketChange}
onSaveableChange={this.handleThroughputBucketsSaveableChange}
onDiscardableChange={this.handleThroughputBucketsDiscardableChange}
/>
)}
{/* TODO: Replace link with call to the Azure Support blade */}
{this.isAutoScaleEnabled() && (

View File

@@ -10,12 +10,11 @@ const DEFAULT_BUCKETS = Array.from({ length: MAX_BUCKET_SIZES }, (_, i) => ({
maxThroughputPercentage: 100,
}));
interface ThroughputBucketsComponentProps {
export interface ThroughputBucketsComponentProps {
currentBuckets: ThroughputBucket[];
throughputBucketsBaseline: ThroughputBucket[];
onBucketsChange: (updatedBuckets: ThroughputBucket[]) => void;
onSaveableChange: (isSaveable: boolean) => void;
onDiscardableChange: (isDiscardable: boolean) => void;
}
export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = ({
@@ -23,7 +22,6 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
throughputBucketsBaseline,
onBucketsChange,
onSaveableChange,
onDiscardableChange,
}) => {
const getThroughputBuckets = (buckets: ThroughputBucket[]): ThroughputBucket[] => {
if (!buckets || buckets.length === 0) {
@@ -45,13 +43,13 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
useEffect(() => {
setThroughputBuckets(getThroughputBuckets(currentBuckets));
onSaveableChange(false);
onDiscardableChange(false);
// onDiscardableChange(false);
}, [currentBuckets]);
useEffect(() => {
const isChanged = isDirty(throughputBuckets, getThroughputBuckets(throughputBucketsBaseline));
onSaveableChange(isChanged);
onDiscardableChange(isChanged);
// onDiscardableChange(isChanged);
}, [throughputBuckets]);
const handleBucketChange = (id: number, newValue: number) => {
@@ -69,7 +67,7 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
return (
<Stack tokens={{ childrenGap: "m" }} styles={{ root: { width: "70%", maxWidth: 700 } }}>
<Label>Throughput groups</Label>
<Label>Throughput Buckets</Label>
<Stack>
{throughputBuckets?.map((bucket) => (
<Stack key={bucket.id} horizontal tokens={{ childrenGap: 8 }} verticalAlign="center">
@@ -80,7 +78,7 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
value={bucket.maxThroughputPercentage}
onChange={(newValue) => handleBucketChange(bucket.id, newValue)}
showValue={false}
label={`Group ${bucket.id}${bucket.id === 1 ? " (Data Explorer Query Group)" : ""}`}
label={`Group ${bucket.id}${bucket.id === 1 ? " (Data Explorer Query Bucket)" : ""}`}
styles={{ root: { flex: 2, maxWidth: 400 } }}
disabled={bucket.maxThroughputPercentage === 100}
/>
@@ -94,9 +92,13 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
}}
disabled={bucket.maxThroughputPercentage === 100}
/>
{/* <IconButton
iconProps={{ iconName: bucket.maxThroughputPercentage === 100 ? "Add" : "Remove" }}
onClick={() => onToggle(bucket.id, bucket.maxThroughputPercentage === 100)}
></IconButton> */}
<Toggle
onText="Enabled"
offText="Disabled"
onText="Active"
offText="Inactive"
checked={bucket.maxThroughputPercentage !== 100}
onChange={(event, checked) => onToggle(bucket.id, checked)}
styles={{ root: { marginBottom: 0 }, text: { fontSize: 12 } }}

View File

@@ -56,6 +56,7 @@ export enum SettingsV2TabTypes {
PartitionKeyTab,
ComputedPropertiesTab,
ContainerVectorPolicyTab,
ThroughputBucketsTab,
}
export enum ContainerPolicyTabTypes {
@@ -168,6 +169,8 @@ export const getTabTitle = (tab: SettingsV2TabTypes): string => {
return "Computed Properties";
case SettingsV2TabTypes.ContainerVectorPolicyTab:
return "Container Policies";
case SettingsV2TabTypes.ThroughputBucketsTab:
return "Throughput Buckets";
default:
throw new Error(`Unknown tab ${tab}`);
}