Added logic

This commit is contained in:
Sung-Hyun Kang 2025-01-21 09:28:49 -06:00
parent 07c4ca9c50
commit 152c995ec0
4 changed files with 72 additions and 28 deletions

View File

@ -39,7 +39,10 @@ import {
migrateTableToManualThroughput, migrateTableToManualThroughput,
updateTableThroughput, updateTableThroughput,
} from "../../Utils/arm/generatedClients/cosmos/tableResources"; } from "../../Utils/arm/generatedClients/cosmos/tableResources";
import { ThroughputSettingsUpdateParameters } from "../../Utils/arm/generatedClients/cosmos/types"; import {
ThroughputSettingsGetResults,
ThroughputSettingsUpdateParameters,
} from "../../Utils/arm/generatedClients/cosmos/types";
import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils"; import { logConsoleInfo, logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
import { HttpHeaders } from "../Constants"; import { HttpHeaders } from "../Constants";
import { client } from "../CosmosClient"; import { client } from "../CosmosClient";
@ -146,23 +149,28 @@ const updateSqlContainerOffer = async (params: UpdateOfferParams): Promise<void>
const { subscriptionId, resourceGroup, databaseAccount } = userContext; const { subscriptionId, resourceGroup, databaseAccount } = userContext;
const accountName = databaseAccount.name; const accountName = databaseAccount.name;
let updatedOffer: ThroughputSettingsGetResults;
if (params.migrateToAutoPilot) { if (params.migrateToAutoPilot) {
await migrateSqlContainerToAutoscale( updatedOffer = (await migrateSqlContainerToAutoscale(
subscriptionId, subscriptionId,
resourceGroup, resourceGroup,
accountName, accountName,
params.databaseId, params.databaseId,
params.collectionId, params.collectionId,
); )) as ThroughputSettingsGetResults;
params.autopilotThroughput = updatedOffer.properties?.resource?.autoscaleSettings?.maxThroughput;
} else if (params.migrateToManual) { } else if (params.migrateToManual) {
await migrateSqlContainerToManualThroughput( updatedOffer = (await migrateSqlContainerToManualThroughput(
subscriptionId, subscriptionId,
resourceGroup, resourceGroup,
accountName, accountName,
params.databaseId, params.databaseId,
params.collectionId, params.collectionId,
); )) as ThroughputSettingsGetResults;
} else { params.manualThroughput = updatedOffer.properties?.resource?.throughput;
}
if (params.throughputBuckets || !(params.migrateToAutoPilot || params.migrateToManual)) {
const body: ThroughputSettingsUpdateParameters = createUpdateOfferBody(params); const body: ThroughputSettingsUpdateParameters = createUpdateOfferBody(params);
await updateSqlContainerThroughput( await updateSqlContainerThroughput(
subscriptionId, subscriptionId,

View File

@ -106,6 +106,7 @@ export interface SettingsComponentState {
changeFeedPolicyBaseline: ChangeFeedPolicyState; changeFeedPolicyBaseline: ChangeFeedPolicyState;
isSubSettingsSaveable: boolean; isSubSettingsSaveable: boolean;
isSubSettingsDiscardable: boolean; isSubSettingsDiscardable: boolean;
isThroughputBucketsSaveable: boolean;
vectorEmbeddingPolicy: DataModels.VectorEmbeddingPolicy; vectorEmbeddingPolicy: DataModels.VectorEmbeddingPolicy;
vectorEmbeddingPolicyBaseline: DataModels.VectorEmbeddingPolicy; vectorEmbeddingPolicyBaseline: DataModels.VectorEmbeddingPolicy;
@ -179,7 +180,9 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
this.changeFeedPolicyVisible = userContext.features.enableChangeFeedPolicy; this.changeFeedPolicyVisible = userContext.features.enableChangeFeedPolicy;
this.throughputBucketsEnabled = this.throughputBucketsEnabled =
userContext.features.enableThroughputBuckets && userContext.authType === AuthType.AAD; userContext.apiType === "SQL" &&
userContext.features.enableThroughputBuckets &&
userContext.authType === AuthType.AAD;
// Mongo container with system partition key still treat as "Fixed" // Mongo container with system partition key still treat as "Fixed"
this.isFixedContainer = this.isFixedContainer =
@ -218,6 +221,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
changeFeedPolicyBaseline: undefined, changeFeedPolicyBaseline: undefined,
isSubSettingsSaveable: false, isSubSettingsSaveable: false,
isSubSettingsDiscardable: false, isSubSettingsDiscardable: false,
isThroughputBucketsSaveable: false,
vectorEmbeddingPolicy: undefined, vectorEmbeddingPolicy: undefined,
vectorEmbeddingPolicyBaseline: undefined, vectorEmbeddingPolicyBaseline: undefined,
@ -450,6 +454,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
isScaleSaveable: false, isScaleSaveable: false,
isScaleDiscardable: false, isScaleDiscardable: false,
isSubSettingsSaveable: false, isSubSettingsSaveable: false,
isThroughputBucketsSaveable: false,
isSubSettingsDiscardable: false, isSubSettingsDiscardable: false,
isContainerPolicyDirty: false, isContainerPolicyDirty: false,
isIndexingPolicyDirty: false, isIndexingPolicyDirty: false,
@ -488,6 +493,10 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
private onIndexingPolicyContentChange = (newIndexingPolicy: DataModels.IndexingPolicy): void => private onIndexingPolicyContentChange = (newIndexingPolicy: DataModels.IndexingPolicy): void =>
this.setState({ indexingPolicyContent: newIndexingPolicy }); this.setState({ indexingPolicyContent: newIndexingPolicy });
private onThroughputBucketsSaveableChange = (isSaveable: boolean): void => {
this.setState({ isThroughputBucketsSaveable: isSaveable });
};
private resetShouldDiscardContainerPolicies = (): void => this.setState({ shouldDiscardContainerPolicies: false }); private resetShouldDiscardContainerPolicies = (): void => this.setState({ shouldDiscardContainerPolicies: false });
private resetShouldDiscardIndexingPolicy = (): void => this.setState({ shouldDiscardIndexingPolicy: false }); private resetShouldDiscardIndexingPolicy = (): void => this.setState({ shouldDiscardIndexingPolicy: false });
@ -1053,7 +1062,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
currentOffer: this.collection.offer(), currentOffer: this.collection.offer(),
autopilotThroughput: this.state.isAutoPilotSelected ? this.state.autoPilotThroughput : undefined, autopilotThroughput: this.state.isAutoPilotSelected ? this.state.autoPilotThroughput : undefined,
manualThroughput: this.state.isAutoPilotSelected ? undefined : this.state.throughput, manualThroughput: this.state.isAutoPilotSelected ? undefined : this.state.throughput,
...(this.state.throughputBuckets && { throughputBuckets: this.state.throughputBuckets }), ...(this.throughputBucketsEnabled &&
this.state.isThroughputBucketsSaveable && { throughputBuckets: this.state.throughputBuckets }),
}; };
if (this.hasProvisioningTypeChanged()) { if (this.hasProvisioningTypeChanged()) {
if (this.state.isAutoPilotSelected) { if (this.state.isAutoPilotSelected) {
@ -1124,6 +1134,7 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
throughputBuckets: this.state.throughputBuckets, throughputBuckets: this.state.throughputBuckets,
enableThroughputBuckets: this.isCollectionSettingsTab && this.throughputBucketsEnabled, enableThroughputBuckets: this.isCollectionSettingsTab && this.throughputBucketsEnabled,
onThroughputBucketChange: this.onThroughputBucketChange, onThroughputBucketChange: this.onThroughputBucketChange,
onThroughputBucketsSaveableChange: this.onThroughputBucketsSaveableChange,
throughputError: this.state.throughputError, throughputError: this.state.throughputError,
}; };

View File

@ -38,14 +38,15 @@ export interface ScaleComponentProps {
throughputBucketsBaseline: DataModels.ThroughputBucket[]; throughputBucketsBaseline: DataModels.ThroughputBucket[];
enableThroughputBuckets: boolean; enableThroughputBuckets: boolean;
onThroughputBucketChange: (throughputBuckets: DataModels.ThroughputBucket[]) => void; onThroughputBucketChange: (throughputBuckets: DataModels.ThroughputBucket[]) => void;
onThroughputBucketsSaveableChange: (isSaveable: boolean) => void;
throughputError?: string; throughputError?: string;
} }
interface ScaleComponentState { interface ScaleComponentState {
isThroughputSaveable: boolean; isThroughputSaveable: boolean;
isBucketsSaveable: boolean; isThroughputBucketsSaveable: boolean;
isThroughputDiscardable: boolean; isThroughputDiscardable: boolean;
isBucketsDiscardable: boolean; isThroughputBucketsDiscardable: boolean;
} }
export class ScaleComponent extends React.Component<ScaleComponentProps, ScaleComponentState> { export class ScaleComponent extends React.Component<ScaleComponentProps, ScaleComponentState> {
@ -62,9 +63,9 @@ export class ScaleComponent extends React.Component<ScaleComponentProps, ScaleCo
this.collectionId = this.props.collection?.id(); this.collectionId = this.props.collection?.id();
this.state = { this.state = {
isThroughputSaveable: false, isThroughputSaveable: false,
isBucketsSaveable: false, isThroughputBucketsSaveable: false,
isThroughputDiscardable: false, isThroughputDiscardable: false,
isBucketsDiscardable: false, isThroughputBucketsDiscardable: false,
}; };
} }
@ -98,7 +99,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps, ScaleCo
if (userContext.isTryCosmosDBSubscription) { if (userContext.isTryCosmosDBSubscription) {
return SharedConstants.CollectionCreation.DefaultCollectionRUs400; return SharedConstants.CollectionCreation.DefaultCollectionRUs400;
} }
return this.offer?.minimumThroughput || SharedConstants.CollectionCreation.DefaultCollectionRUs400; return this.offer?.minimumThroughput || SharedConstants.CollectionCreation.DefaultCollectionRUs400;
}; };
@ -193,9 +193,12 @@ export class ScaleComponent extends React.Component<ScaleComponentProps, ScaleCo
return hasChanges ? { ...prevState, ...updates } : null; return hasChanges ? { ...prevState, ...updates } : null;
}, },
() => { () => {
const isSaveable = this.state.isThroughputSaveable || this.state.isBucketsSaveable; const isSaveable = this.state.isThroughputSaveable
const isDiscardable = this.state.isThroughputDiscardable || this.state.isBucketsDiscardable; ? this.state.isThroughputDiscardable || this.state.isThroughputBucketsSaveable
: this.state.isThroughputBucketsSaveable;
const isDiscardable = this.state.isThroughputDiscardable || this.state.isThroughputBucketsDiscardable;
this.props.onScaleSaveableChange(isSaveable); this.props.onScaleSaveableChange(isSaveable);
this.props.onThroughputBucketsSaveableChange(this.state.isThroughputBucketsSaveable);
this.props.onScaleDiscardableChange(isDiscardable); this.props.onScaleDiscardableChange(isDiscardable);
}, },
); );
@ -209,12 +212,12 @@ export class ScaleComponent extends React.Component<ScaleComponentProps, ScaleCo
this.updateScaleSettingsState({ isThroughputDiscardable: isDiscardable }); this.updateScaleSettingsState({ isThroughputDiscardable: isDiscardable });
}; };
private handleBucketsSaveableChange = (isSaveable: boolean) => { private handleThroughputBucketsSaveableChange = (isSaveable: boolean) => {
this.updateScaleSettingsState({ isBucketsSaveable: isSaveable }); this.updateScaleSettingsState({ isThroughputBucketsSaveable: isSaveable });
}; };
private handleBucketsDiscardableChange = (isDiscardable: boolean) => { private handleThroughputBucketsDiscardableChange = (isDiscardable: boolean) => {
this.updateScaleSettingsState({ isBucketsDiscardable: isDiscardable }); this.updateScaleSettingsState({ isThroughputBucketsDiscardable: isDiscardable });
}; };
public render(): JSX.Element { public render(): JSX.Element {
@ -232,13 +235,13 @@ export class ScaleComponent extends React.Component<ScaleComponentProps, ScaleCo
<MessageBar messageBarType={MessageBarType.warning}>{this.getInitialNotificationElement()}</MessageBar> <MessageBar messageBarType={MessageBarType.warning}>{this.getInitialNotificationElement()}</MessageBar>
)} )}
{!this.isAutoScaleEnabled() && <Stack {...subComponentStackProps}>{this.getThroughputInputComponent()}</Stack>} {!this.isAutoScaleEnabled() && <Stack {...subComponentStackProps}>{this.getThroughputInputComponent()}</Stack>}
{this.props.enableThroughputBuckets && !this.props.isAutoPilotSelected && ( {this.props.enableThroughputBuckets && (
<ThroughputBucketsComponent <ThroughputBucketsComponent
currentBuckets={this.props.throughputBuckets} currentBuckets={this.props.throughputBuckets}
throughputBucketsBaseline={this.props.throughputBucketsBaseline} throughputBucketsBaseline={this.props.throughputBucketsBaseline}
onBucketsChange={this.props.onThroughputBucketChange} onBucketsChange={this.props.onThroughputBucketChange}
onSaveableChange={this.handleBucketsSaveableChange} onSaveableChange={this.handleThroughputBucketsSaveableChange}
onDiscardableChange={this.handleBucketsDiscardableChange} onDiscardableChange={this.handleThroughputBucketsDiscardableChange}
/> />
)} )}

View File

@ -1,4 +1,4 @@
import { Icon, Label, Slider, Stack, TextField } from "@fluentui/react"; import { Label, Slider, Stack, TextField, Toggle } from "@fluentui/react";
import { ThroughputBucket } from "Contracts/DataModels"; import { ThroughputBucket } from "Contracts/DataModels";
import React, { FC, useEffect, useState } from "react"; import React, { FC, useEffect, useState } from "react";
import { isDirty } from "../../SettingsUtils"; import { isDirty } from "../../SettingsUtils";
@ -26,7 +26,16 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
onDiscardableChange, onDiscardableChange,
}) => { }) => {
const getThroughputBuckets = (buckets: ThroughputBucket[]): ThroughputBucket[] => { const getThroughputBuckets = (buckets: ThroughputBucket[]): ThroughputBucket[] => {
return DEFAULT_BUCKETS.map( if (!buckets || buckets.length === 0) {
return DEFAULT_BUCKETS;
}
const maxBuckets = Math.max(DEFAULT_BUCKETS.length, buckets.length);
const adjustedDefaultBuckets = Array.from({ length: maxBuckets }, (_, i) => ({
id: i + 1,
maxThroughputPercentage: 100,
}));
return adjustedDefaultBuckets.map(
(defaultBucket) => buckets?.find((bucket) => bucket.id === defaultBucket.id) || defaultBucket, (defaultBucket) => buckets?.find((bucket) => bucket.id === defaultBucket.id) || defaultBucket,
); );
}; };
@ -54,9 +63,13 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
settingsChanged && onBucketsChange(updatedBuckets); settingsChanged && onBucketsChange(updatedBuckets);
}; };
const onToggle = (id: number, checked: boolean) => {
handleBucketChange(id, checked ? 50 : 100);
};
return ( return (
<Stack tokens={{ childrenGap: "m" }} styles={{ root: { width: "70%", maxWidth: 700 } }}> <Stack tokens={{ childrenGap: "m" }} styles={{ root: { width: "70%", maxWidth: 700 } }}>
<Label>Throughput buckets</Label> <Label>Throughput groups</Label>
<Stack> <Stack>
{throughputBuckets?.map((bucket) => ( {throughputBuckets?.map((bucket) => (
<Stack key={bucket.id} horizontal tokens={{ childrenGap: 8 }} verticalAlign="center"> <Stack key={bucket.id} horizontal tokens={{ childrenGap: 8 }} verticalAlign="center">
@ -67,8 +80,9 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
value={bucket.maxThroughputPercentage} value={bucket.maxThroughputPercentage}
onChange={(newValue) => handleBucketChange(bucket.id, newValue)} onChange={(newValue) => handleBucketChange(bucket.id, newValue)}
showValue={false} showValue={false}
label={`Bucket ${bucket.id}`} label={`Group ${bucket.id}${bucket.id === 1 ? " (Data Explorer Query Bucket)" : ""}`}
styles={{ root: { flex: 2, maxWidth: 400 } }} styles={{ root: { flex: 2, maxWidth: 400 } }}
disabled={bucket.maxThroughputPercentage === 100}
/> />
<TextField <TextField
value={bucket.maxThroughputPercentage.toString()} value={bucket.maxThroughputPercentage.toString()}
@ -78,13 +92,21 @@ export const ThroughputBucketsComponent: FC<ThroughputBucketsComponentProps> = (
styles={{ styles={{
fieldGroup: { width: 80 }, fieldGroup: { width: 80 },
}} }}
disabled={bucket.maxThroughputPercentage === 100}
/> />
{bucket.id === 1 && ( <Toggle
onText="Enabled"
offText="Disabled"
checked={bucket.maxThroughputPercentage !== 100}
onChange={(event, checked) => onToggle(bucket.id, checked)}
styles={{ root: { marginBottom: 0 }, text: { fontSize: 12 } }}
></Toggle>
{/* {bucket.id === 1 && (
<Stack horizontal tokens={{ childrenGap: 4 }} verticalAlign="center"> <Stack horizontal tokens={{ childrenGap: 4 }} verticalAlign="center">
<Icon iconName="TagSolid" /> <Icon iconName="TagSolid" />
<span>Data Explorer Query Bucket</span> <span>Data Explorer Query Bucket</span>
</Stack> </Stack>
)} )} */}
</Stack> </Stack>
))} ))}
</Stack> </Stack>