mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-06-30 02:28:44 +01:00
Initial version of Hot Partition Key Rate Limiting Policy
This commit is contained in:
Vendored
+6
-1
@@ -17,12 +17,17 @@
|
||||
"test/out/**": true,
|
||||
"workers/libs/**": true
|
||||
},
|
||||
"js/ts.tsdk.path": "node_modules/typescript/lib",
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "explicit"
|
||||
},
|
||||
"js/ts.preferences.importModuleSpecifier": "non-relative",
|
||||
"typescript.preferences.importModuleSpecifier": "non-relative",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ export class CapabilityNames {
|
||||
public static readonly EnableDynamicDataMasking: string = "EnableDynamicDataMasking";
|
||||
public static readonly EnableNoSQLFullTextSearchPreviewFeatures: string = "EnableNoSQLFullTextSearchPreviewFeatures";
|
||||
public static readonly EnableOnlineCopyFeature: string = "EnableOnlineContainerCopy";
|
||||
public static readonly EnableHotPartitionKeyThrottling: string = "EnableHotPartitionKeyThrottling";
|
||||
}
|
||||
|
||||
export enum CapacityMode {
|
||||
|
||||
@@ -112,6 +112,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
|
||||
: resource.softAllowedMaximumThroughput;
|
||||
|
||||
const throughputBuckets = resource?.throughputBuckets;
|
||||
const hotPartitionKeyRateLimitingPolicy = resource?.hotPartitionKeyRateLimitingPolicy;
|
||||
|
||||
if (autoscaleSettings) {
|
||||
return {
|
||||
@@ -123,6 +124,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
|
||||
instantMaximumThroughput,
|
||||
softAllowedMaximumThroughput,
|
||||
throughputBuckets,
|
||||
hotPartitionKeyRateLimitingPolicy,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -135,6 +137,7 @@ const readCollectionOfferWithARM = async (databaseId: string, collectionId: stri
|
||||
instantMaximumThroughput,
|
||||
softAllowedMaximumThroughput,
|
||||
throughputBuckets,
|
||||
hotPartitionKeyRateLimitingPolicy,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -367,6 +367,10 @@ const createUpdateOfferBody = (params: UpdateOfferParams): ThroughputSettingsUpd
|
||||
body.properties.resource.throughputBuckets = throughputBuckets;
|
||||
}
|
||||
|
||||
if (params.hotPartitionKeyRateLimitingPolicy !== undefined) {
|
||||
body.properties.resource.hotPartitionKeyRateLimitingPolicy = params.hotPartitionKeyRateLimitingPolicy;
|
||||
}
|
||||
|
||||
return body;
|
||||
};
|
||||
|
||||
@@ -409,6 +413,10 @@ const updateOfferWithSDK = async (params: UpdateOfferParams): Promise<Offer> =>
|
||||
newOffer.content.offerAutopilotSettings = { maxThroughput: 0 };
|
||||
}
|
||||
|
||||
if (params.hotPartitionKeyRateLimitingPolicy !== undefined) {
|
||||
newOffer.content.hotPartitionKeyRateLimitingPolicy = params.hotPartitionKeyRateLimitingPolicy;
|
||||
}
|
||||
|
||||
const sdkResponse = await client()
|
||||
.offer(params.currentOffer.id)
|
||||
// TODO Remove casting when SDK types are fixed (https://github.com/Azure/azure-sdk-for-js/issues/10660)
|
||||
|
||||
@@ -343,6 +343,11 @@ export interface Offer {
|
||||
instantMaximumThroughput?: number;
|
||||
softAllowedMaximumThroughput?: number;
|
||||
throughputBuckets?: ThroughputBucket[];
|
||||
hotPartitionKeyRateLimitingPolicy?: HotPartitionKeyRateLimitingPolicy;
|
||||
}
|
||||
|
||||
export interface HotPartitionKeyRateLimitingPolicy {
|
||||
maximumPerPartitionKeyThroughputUtilizationPercent: number;
|
||||
}
|
||||
|
||||
export interface ThroughputBucket {
|
||||
@@ -359,6 +364,7 @@ export interface SDKOfferDefinition extends Resource {
|
||||
offerIsRUPerMinuteThroughputEnabled?: boolean;
|
||||
collectionThroughputInfo?: OfferThroughputInfo;
|
||||
offerAutopilotSettings?: AutoPilotOfferSettings;
|
||||
hotPartitionKeyRateLimitingPolicy?: HotPartitionKeyRateLimitingPolicy;
|
||||
};
|
||||
resource?: string;
|
||||
offerResourceId?: string;
|
||||
@@ -492,6 +498,7 @@ export interface UpdateOfferParams {
|
||||
migrateToAutoPilot?: boolean;
|
||||
migrateToManual?: boolean;
|
||||
throughputBuckets?: ThroughputBucket[];
|
||||
hotPartitionKeyRateLimitingPolicy?: HotPartitionKeyRateLimitingPolicy | null;
|
||||
}
|
||||
|
||||
export interface Notification {
|
||||
|
||||
@@ -17,25 +17,25 @@ import { useIndexingPolicyStore } from "Explorer/Tabs/QueryTab/ResultsView";
|
||||
import { useDatabases } from "Explorer/useDatabases";
|
||||
import { Keys, t } from "Localization";
|
||||
import { isFabricNative } from "Platform/Fabric/FabricUtil";
|
||||
import { isVectorSearchEnabled } from "Utils/CapabilityUtils";
|
||||
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
|
||||
import * as React from "react";
|
||||
import { isHotPartitionKeyThrottlingEnabled, isVectorSearchEnabled } from "Utils/CapabilityUtils";
|
||||
import { isRunningOnPublicCloud } from "Utils/CloudUtils";
|
||||
import DiscardIcon from "../../../../images/discard.svg";
|
||||
import SaveIcon from "../../../../images/save-cosmos.svg";
|
||||
import { AuthType } from "../../../AuthType";
|
||||
import * as Constants from "../../../Common/Constants";
|
||||
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||
import { getIndexTransformationProgress } from "../../../Common/dataAccess/getIndexTransformationProgress";
|
||||
import { readMongoDBCollectionThroughRP } from "../../../Common/dataAccess/readMongoDBCollection";
|
||||
import { updateCollection } from "../../../Common/dataAccess/updateCollection";
|
||||
import { updateOffer } from "../../../Common/dataAccess/updateOffer";
|
||||
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils";
|
||||
import * as DataModels from "../../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants";
|
||||
import { trace, traceFailure, traceStart, traceSuccess } from "../../../Shared/Telemetry/TelemetryProcessor";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||
import { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/cosmos/types";
|
||||
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
|
||||
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
|
||||
import {
|
||||
PartitionKeyComponent,
|
||||
@@ -65,16 +65,16 @@ import {
|
||||
AddMongoIndexProps,
|
||||
ChangeFeedPolicyState,
|
||||
GeospatialConfigType,
|
||||
MongoIndexTypes,
|
||||
SettingsV2TabTypes,
|
||||
TtlType,
|
||||
getMongoNotification,
|
||||
getTabTitle,
|
||||
hasDatabaseSharedThroughput,
|
||||
isDataMaskingEnabled,
|
||||
isDirty,
|
||||
MongoIndexTypes,
|
||||
parseConflictResolutionMode,
|
||||
parseConflictResolutionProcedure,
|
||||
SettingsV2TabTypes,
|
||||
TtlType,
|
||||
} from "./SettingsUtils";
|
||||
interface SettingsV2TabInfo {
|
||||
tab: SettingsV2TabTypes;
|
||||
@@ -103,6 +103,8 @@ export interface SettingsComponentState {
|
||||
throughputBuckets: DataModels.ThroughputBucket[];
|
||||
throughputBucketsBaseline: DataModels.ThroughputBucket[];
|
||||
throughputError: string;
|
||||
hotPartitionKeyRateLimitingPolicy: DataModels.HotPartitionKeyRateLimitingPolicy;
|
||||
hotPartitionKeyRateLimitingPolicyBaseline: DataModels.HotPartitionKeyRateLimitingPolicy;
|
||||
|
||||
timeToLive: TtlType;
|
||||
timeToLiveBaseline: TtlType;
|
||||
@@ -225,6 +227,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
throughputBuckets: undefined,
|
||||
throughputBucketsBaseline: undefined,
|
||||
throughputError: undefined,
|
||||
hotPartitionKeyRateLimitingPolicy: null,
|
||||
hotPartitionKeyRateLimitingPolicyBaseline: null,
|
||||
|
||||
timeToLive: undefined,
|
||||
timeToLiveBaseline: undefined,
|
||||
@@ -495,6 +499,8 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
throughput: this.state.throughputBaseline,
|
||||
throughputBuckets: this.state.throughputBucketsBaseline,
|
||||
throughputBucketsBaseline: this.state.throughputBucketsBaseline,
|
||||
hotPartitionKeyRateLimitingPolicy: this.state.hotPartitionKeyRateLimitingPolicyBaseline,
|
||||
hotPartitionKeyRateLimitingPolicyBaseline: this.state.hotPartitionKeyRateLimitingPolicyBaseline,
|
||||
timeToLive: this.state.timeToLiveBaseline,
|
||||
timeToLiveSeconds: this.state.timeToLiveSecondsBaseline,
|
||||
displayedTtlSeconds: this.state.displayedTtlSecondsBaseline,
|
||||
@@ -877,12 +883,15 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
] as DataModels.ComputedProperties;
|
||||
}
|
||||
const throughputBuckets = this.offer?.throughputBuckets;
|
||||
const hotPartitionKeyRateLimitingPolicy = this.offer?.hotPartitionKeyRateLimitingPolicy ?? null;
|
||||
|
||||
return {
|
||||
throughput: offerThroughput,
|
||||
throughputBaseline: offerThroughput,
|
||||
throughputBuckets,
|
||||
throughputBucketsBaseline: throughputBuckets,
|
||||
hotPartitionKeyRateLimitingPolicy,
|
||||
hotPartitionKeyRateLimitingPolicyBaseline: hotPartitionKeyRateLimitingPolicy,
|
||||
changeFeedPolicy: changeFeedPolicy,
|
||||
changeFeedPolicyBaseline: changeFeedPolicy,
|
||||
timeToLive: timeToLive,
|
||||
@@ -984,6 +993,12 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
this.setState({ throughputBuckets });
|
||||
};
|
||||
|
||||
private onHotPartitionKeyRateLimitingPolicyChange = (
|
||||
hotPartitionKeyRateLimitingPolicy: DataModels.HotPartitionKeyRateLimitingPolicy,
|
||||
): void => {
|
||||
this.setState({ hotPartitionKeyRateLimitingPolicy });
|
||||
};
|
||||
|
||||
private onAutoPilotSelected = (isAutoPilotSelected: boolean): void =>
|
||||
this.setState({ isAutoPilotSelected: isAutoPilotSelected });
|
||||
|
||||
@@ -999,6 +1014,9 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
currentOffer: this.database.offer(),
|
||||
autopilotThroughput: this.state.isAutoPilotSelected ? this.state.autoPilotThroughput : undefined,
|
||||
manualThroughput: this.state.isAutoPilotSelected ? undefined : this.state.throughput,
|
||||
hotPartitionKeyRateLimitingPolicy: isHotPartitionKeyThrottlingEnabled()
|
||||
? this.state.hotPartitionKeyRateLimitingPolicy ?? null
|
||||
: undefined,
|
||||
};
|
||||
if (this.hasProvisioningTypeChanged()) {
|
||||
if (this.state.isAutoPilotSelected) {
|
||||
@@ -1232,6 +1250,9 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
autopilotThroughput: this.state.isAutoPilotSelected ? this.state.autoPilotThroughput : undefined,
|
||||
manualThroughput: this.state.isAutoPilotSelected ? undefined : this.state.throughput,
|
||||
throughputBuckets: this.throughputBucketsEnabled ? this.state.throughputBuckets : undefined,
|
||||
hotPartitionKeyRateLimitingPolicy: isHotPartitionKeyThrottlingEnabled()
|
||||
? this.state.hotPartitionKeyRateLimitingPolicy ?? null
|
||||
: undefined,
|
||||
};
|
||||
if (this.hasProvisioningTypeChanged()) {
|
||||
if (this.state.isAutoPilotSelected) {
|
||||
@@ -1300,6 +1321,9 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
||||
onScaleSaveableChange: this.onScaleSaveableChange,
|
||||
onScaleDiscardableChange: this.onScaleDiscardableChange,
|
||||
throughputError: this.state.throughputError,
|
||||
hotPartitionKeyRateLimitingPolicy: this.state.hotPartitionKeyRateLimitingPolicy,
|
||||
hotPartitionKeyRateLimitingPolicyBaseline: this.state.hotPartitionKeyRateLimitingPolicyBaseline,
|
||||
onHotPartitionKeyRateLimitingPolicyChange: this.onHotPartitionKeyRateLimitingPolicyChange,
|
||||
};
|
||||
if (!this.isCollectionSettingsTab) {
|
||||
return (
|
||||
|
||||
@@ -29,6 +29,9 @@ describe("ScaleComponent", () => {
|
||||
onScaleDiscardableChange: () => {
|
||||
return;
|
||||
},
|
||||
onHotPartitionKeyRateLimitingPolicyChange: () => {
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
it("autoScale disabled", () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Link, MessageBar, MessageBarType, Stack, Text, TextField } from "@fluen
|
||||
import { Keys, t } from "Localization";
|
||||
import * as React from "react";
|
||||
import * as Constants from "../../../../Common/Constants";
|
||||
import { Platform, configContext } from "../../../../ConfigContext";
|
||||
import { configContext, Platform } from "../../../../ConfigContext";
|
||||
import * as DataModels from "../../../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../../../Contracts/ViewModels";
|
||||
import * as SharedConstants from "../../../../Shared/Constants";
|
||||
@@ -36,6 +36,9 @@ export interface ScaleComponentProps {
|
||||
onScaleSaveableChange: (isScaleSaveable: boolean) => void;
|
||||
onScaleDiscardableChange: (isScaleDiscardable: boolean) => void;
|
||||
throughputError?: string;
|
||||
hotPartitionKeyRateLimitingPolicy?: DataModels.HotPartitionKeyRateLimitingPolicy;
|
||||
hotPartitionKeyRateLimitingPolicyBaseline?: DataModels.HotPartitionKeyRateLimitingPolicy;
|
||||
onHotPartitionKeyRateLimitingPolicyChange: (newPolicy: DataModels.HotPartitionKeyRateLimitingPolicy) => void;
|
||||
}
|
||||
|
||||
export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
||||
@@ -148,6 +151,9 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
|
||||
instantMaximumThroughput={this.offer?.instantMaximumThroughput}
|
||||
softAllowedMaximumThroughput={this.offer?.softAllowedMaximumThroughput}
|
||||
isGlobalSecondaryIndex={this.props.isGlobalSecondaryIndex}
|
||||
hotPartitionKeyRateLimitingPolicy={this.props.hotPartitionKeyRateLimitingPolicy}
|
||||
hotPartitionKeyRateLimitingPolicyBaseline={this.props.hotPartitionKeyRateLimitingPolicyBaseline}
|
||||
onHotPartitionKeyRateLimitingPolicyChange={this.props.onHotPartitionKeyRateLimitingPolicyChange}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
+15
@@ -1,12 +1,24 @@
|
||||
import { shallow } from "enzyme";
|
||||
import React from "react";
|
||||
import * as Constants from "../../../../../Common/Constants";
|
||||
import * as DataModels from "../../../../../Contracts/DataModels";
|
||||
import { updateUserContext } from "../../../../../UserContext";
|
||||
import {
|
||||
ThroughputInputAutoPilotV3Component,
|
||||
ThroughputInputAutoPilotV3Props,
|
||||
} from "./ThroughputInputAutoPilotV3Component";
|
||||
|
||||
describe("ThroughputInputAutoPilotV3Component", () => {
|
||||
beforeAll(() => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
capabilities: [{ name: Constants.CapabilityNames.EnableHotPartitionKeyThrottling }],
|
||||
},
|
||||
} as DataModels.DatabaseAccount,
|
||||
});
|
||||
});
|
||||
|
||||
const baseProps: ThroughputInputAutoPilotV3Props = {
|
||||
databaseAccount: {} as DataModels.DatabaseAccount,
|
||||
databaseName: "test",
|
||||
@@ -45,6 +57,9 @@ describe("ThroughputInputAutoPilotV3Component", () => {
|
||||
instantMaximumThroughput: 5000,
|
||||
softAllowedMaximumThroughput: 1000000,
|
||||
isGlobalSecondaryIndex: false,
|
||||
onHotPartitionKeyRateLimitingPolicyChange: () => {
|
||||
return;
|
||||
},
|
||||
};
|
||||
|
||||
it("throughput input visible", () => {
|
||||
|
||||
+59
-2
@@ -12,9 +12,11 @@ import {
|
||||
MessageBarType,
|
||||
ProgressIndicator,
|
||||
Separator,
|
||||
Slider,
|
||||
Stack,
|
||||
Text,
|
||||
TextField,
|
||||
Toggle,
|
||||
} from "@fluentui/react";
|
||||
import { Keys, t } from "Localization";
|
||||
import React from "react";
|
||||
@@ -25,10 +27,10 @@ import * as TelemetryProcessor from "../../../../../Shared/Telemetry/TelemetryPr
|
||||
import { userContext } from "../../../../../UserContext";
|
||||
import * as AutoPilotUtils from "../../../../../Utils/AutoPilotUtils";
|
||||
import { autoPilotThroughput1K } from "../../../../../Utils/AutoPilotUtils";
|
||||
import { isHotPartitionKeyThrottlingEnabled } from "../../../../../Utils/CapabilityUtils";
|
||||
import { calculateEstimateNumber } from "../../../../../Utils/PricingUtils";
|
||||
import { Int32 } from "../../../../Panes/Tables/Validators/EntityPropertyValidationCommon";
|
||||
import {
|
||||
PriceBreakdown,
|
||||
checkBoxAndInputStackProps,
|
||||
getChoiceGroupStyles,
|
||||
getEstimatedSpendingElement,
|
||||
@@ -40,11 +42,12 @@ import {
|
||||
getUpdateThroughputBeyondSupportLimitMessage,
|
||||
manualToAutoscaleDisclaimerElement,
|
||||
noLeftPaddingCheckBoxStyle,
|
||||
PriceBreakdown,
|
||||
relaxedSpacingStackProps,
|
||||
saveThroughputWarningMessage,
|
||||
titleAndInputStackProps,
|
||||
} from "../../SettingsRenderUtils";
|
||||
import { IsComponentDirtyResult, getSanitizedInputValue, isDirty } from "../../SettingsUtils";
|
||||
import { getSanitizedInputValue, IsComponentDirtyResult, isDirty } from "../../SettingsUtils";
|
||||
import { ToolTipLabelComponent } from "../ToolTipLabelComponent";
|
||||
|
||||
export interface ThroughputInputAutoPilotV3Props {
|
||||
@@ -82,6 +85,9 @@ export interface ThroughputInputAutoPilotV3Props {
|
||||
instantMaximumThroughput: number;
|
||||
softAllowedMaximumThroughput: number;
|
||||
isGlobalSecondaryIndex: boolean;
|
||||
hotPartitionKeyRateLimitingPolicy?: DataModels.HotPartitionKeyRateLimitingPolicy;
|
||||
hotPartitionKeyRateLimitingPolicyBaseline?: DataModels.HotPartitionKeyRateLimitingPolicy;
|
||||
onHotPartitionKeyRateLimitingPolicyChange: (newPolicy: DataModels.HotPartitionKeyRateLimitingPolicy) => void;
|
||||
}
|
||||
|
||||
interface ThroughputInputAutoPilotV3State {
|
||||
@@ -137,6 +143,12 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
if (this.hasProvisioningTypeChanged()) {
|
||||
isSaveable = true;
|
||||
isDiscardable = true;
|
||||
} else if (
|
||||
isHotPartitionKeyThrottlingEnabled() &&
|
||||
isDirty(this.props.hotPartitionKeyRateLimitingPolicy, this.props.hotPartitionKeyRateLimitingPolicyBaseline)
|
||||
) {
|
||||
isSaveable = true;
|
||||
isDiscardable = true;
|
||||
} else if (this.props.isAutoPilotSelected) {
|
||||
if (isDirty(this.props.maxAutoPilotThroughput, this.props.maxAutoPilotThroughputBaseline)) {
|
||||
isDiscardable = true;
|
||||
@@ -865,6 +877,50 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
);
|
||||
};
|
||||
|
||||
private renderPartitionKeyRateLimitingPolicy = (): JSX.Element => {
|
||||
return (
|
||||
<Stack {...titleAndInputStackProps} style={{ maxWidth: "700px" }}>
|
||||
<Stack horizontal>
|
||||
<ToolTipLabelComponent
|
||||
label={t(Keys.controls.settings.scale.rateLimitingPolicyTitle)}
|
||||
toolTipElement={null}
|
||||
/>
|
||||
<Toggle
|
||||
onText={t(Keys.common.on)}
|
||||
offText={t(Keys.common.off)}
|
||||
checked={!!this.props.hotPartitionKeyRateLimitingPolicy}
|
||||
onChange={(_ev, checked) => {
|
||||
if (checked) {
|
||||
this.props.onHotPartitionKeyRateLimitingPolicyChange({
|
||||
maximumPerPartitionKeyThroughputUtilizationPercent:
|
||||
this.props.hotPartitionKeyRateLimitingPolicyBaseline
|
||||
?.maximumPerPartitionKeyThroughputUtilizationPercent ?? 75,
|
||||
});
|
||||
} else {
|
||||
this.props.onHotPartitionKeyRateLimitingPolicyChange(null);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Stack>
|
||||
<Slider
|
||||
disabled={!this.props.hotPartitionKeyRateLimitingPolicy}
|
||||
label={t(Keys.controls.settings.scale.rateLimitPolicyMaxThroughputUtilizationLabel)}
|
||||
min={51}
|
||||
max={100}
|
||||
ariaValueText={(value: number) => `${value} percent`}
|
||||
valueFormat={(value: number) => `${value}%`}
|
||||
showValue
|
||||
value={this.props.hotPartitionKeyRateLimitingPolicy?.maximumPerPartitionKeyThroughputUtilizationPercent ?? 75}
|
||||
onChange={(value: number) =>
|
||||
this.props.onHotPartitionKeyRateLimitingPolicyChange({
|
||||
maximumPerPartitionKeyThroughputUtilizationPercent: value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
public render(): JSX.Element {
|
||||
return (
|
||||
<Stack {...checkBoxAndInputStackProps}>
|
||||
@@ -872,6 +928,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
|
||||
{this.renderThroughputModeChoices()}
|
||||
|
||||
{this.renderThroughputComponent()}
|
||||
{isHotPartitionKeyThrottlingEnabled() && this.renderPartitionKeyRateLimitingPolicy()}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
+114
@@ -797,6 +797,44 @@ exports[`ThroughputInputAutoPilotV3Component autopilot input visible 1`] = `
|
||||
</Stack>
|
||||
</StackItem>
|
||||
</Stack>
|
||||
<Stack
|
||||
style={
|
||||
{
|
||||
"maxWidth": "700px",
|
||||
}
|
||||
}
|
||||
tokens={
|
||||
{
|
||||
"childrenGap": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<ToolTipLabelComponent
|
||||
label="Rate limiting policy"
|
||||
toolTipElement={null}
|
||||
/>
|
||||
<StyledToggleBase
|
||||
checked={false}
|
||||
offText="Off"
|
||||
onChange={[Function]}
|
||||
onText="On"
|
||||
/>
|
||||
</Stack>
|
||||
<StyledSliderBase
|
||||
ariaValueText={[Function]}
|
||||
disabled={true}
|
||||
label="Max per partition key Throughput utilization"
|
||||
max={100}
|
||||
min={51}
|
||||
onChange={[Function]}
|
||||
showValue={true}
|
||||
value={75}
|
||||
valueFormat={[Function]}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
`;
|
||||
|
||||
@@ -1372,6 +1410,44 @@ exports[`ThroughputInputAutoPilotV3Component spendAck checkbox visible 1`] = `
|
||||
</Stack>
|
||||
</StackItem>
|
||||
</Stack>
|
||||
<Stack
|
||||
style={
|
||||
{
|
||||
"maxWidth": "700px",
|
||||
}
|
||||
}
|
||||
tokens={
|
||||
{
|
||||
"childrenGap": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<ToolTipLabelComponent
|
||||
label="Rate limiting policy"
|
||||
toolTipElement={null}
|
||||
/>
|
||||
<StyledToggleBase
|
||||
checked={false}
|
||||
offText="Off"
|
||||
onChange={[Function]}
|
||||
onText="On"
|
||||
/>
|
||||
</Stack>
|
||||
<StyledSliderBase
|
||||
ariaValueText={[Function]}
|
||||
disabled={true}
|
||||
label="Max per partition key Throughput utilization"
|
||||
max={100}
|
||||
min={51}
|
||||
onChange={[Function]}
|
||||
showValue={true}
|
||||
value={75}
|
||||
valueFormat={[Function]}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
`;
|
||||
|
||||
@@ -1930,5 +2006,43 @@ exports[`ThroughputInputAutoPilotV3Component throughput input visible 1`] = `
|
||||
</Stack>
|
||||
</StackItem>
|
||||
</Stack>
|
||||
<Stack
|
||||
style={
|
||||
{
|
||||
"maxWidth": "700px",
|
||||
}
|
||||
}
|
||||
tokens={
|
||||
{
|
||||
"childrenGap": 5,
|
||||
}
|
||||
}
|
||||
>
|
||||
<Stack
|
||||
horizontal={true}
|
||||
>
|
||||
<ToolTipLabelComponent
|
||||
label="Rate limiting policy"
|
||||
toolTipElement={null}
|
||||
/>
|
||||
<StyledToggleBase
|
||||
checked={false}
|
||||
offText="Off"
|
||||
onChange={[Function]}
|
||||
onText="On"
|
||||
/>
|
||||
</Stack>
|
||||
<StyledSliderBase
|
||||
ariaValueText={[Function]}
|
||||
disabled={true}
|
||||
label="Max per partition key Throughput utilization"
|
||||
max={100}
|
||||
min={51}
|
||||
onChange={[Function]}
|
||||
showValue={true}
|
||||
value={75}
|
||||
valueFormat={[Function]}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
`;
|
||||
|
||||
@@ -3,8 +3,8 @@ import * as Constants from "../../../Common/Constants";
|
||||
import * as DataModels from "../../../Contracts/DataModels";
|
||||
import * as ViewModels from "../../../Contracts/ViewModels";
|
||||
import { userContext } from "../../../UserContext";
|
||||
import { isCapabilityEnabled } from "../../../Utils/CapabilityUtils";
|
||||
import { MongoIndex } from "../../../Utils/arm/generatedClients/cosmos/types";
|
||||
import { isCapabilityEnabled } from "../../../Utils/CapabilityUtils";
|
||||
|
||||
const zeroValue = 0;
|
||||
export type isDirtyTypes =
|
||||
@@ -17,7 +17,8 @@ export type isDirtyTypes =
|
||||
| DataModels.VectorIndex[]
|
||||
| DataModels.FullTextPolicy
|
||||
| DataModels.ThroughputBucket[]
|
||||
| DataModels.DataMaskingPolicy;
|
||||
| DataModels.DataMaskingPolicy
|
||||
| DataModels.HotPartitionKeyRateLimitingPolicy;
|
||||
export const TtlOff = "off";
|
||||
export const TtlOn = "on";
|
||||
export const TtlOnNoDefault = "on-nodefault";
|
||||
|
||||
@@ -179,10 +179,13 @@ exports[`SettingsComponent renders 1`] = `
|
||||
"vectorEmbeddingPolicy": [Function],
|
||||
}
|
||||
}
|
||||
hotPartitionKeyRateLimitingPolicy={null}
|
||||
hotPartitionKeyRateLimitingPolicyBaseline={null}
|
||||
isAutoPilotSelected={false}
|
||||
isFixedContainer={false}
|
||||
isGlobalSecondaryIndex={true}
|
||||
onAutoPilotSelected={[Function]}
|
||||
onHotPartitionKeyRateLimitingPolicyChange={[Function]}
|
||||
onMaxAutoPilotThroughputChange={[Function]}
|
||||
onScaleDiscardableChange={[Function]}
|
||||
onScaleSaveableChange={[Function]}
|
||||
|
||||
@@ -894,7 +894,9 @@
|
||||
"autoScaleCustomSettings": "Your account has custom settings that prevents setting throughput at the container level. Please work with your Cosmos DB engineering team point of contact to make changes.",
|
||||
"keyspaceSharedThroughput": "This table shared throughput is configured at the keyspace",
|
||||
"throughputRangeLabel": "Throughput ({{min}} - {{max}} RU/s)",
|
||||
"unlimited": "unlimited"
|
||||
"unlimited": "unlimited",
|
||||
"rateLimitingPolicyTitle": "Rate limiting policy",
|
||||
"rateLimitPolicyMaxThroughputUtilizationLabel": "Max per partition key Throughput utilization"
|
||||
},
|
||||
"partitionKeyEditor": {
|
||||
"changePartitionKey": "Change {{partitionKeyName}}",
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import * as Constants from "../Common/Constants";
|
||||
import { DatabaseAccount } from "../Contracts/DataModels";
|
||||
import { updateUserContext } from "../UserContext";
|
||||
import { isHotPartitionKeyThrottlingEnabled } from "./CapabilityUtils";
|
||||
|
||||
describe("CapabilityUtils", () => {
|
||||
describe("isHotPartitionKeyThrottlingEnabled", () => {
|
||||
it("returns true for a SQL account with the EnableHotPartitionKeyThrottling capability", () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
capabilities: [{ name: Constants.CapabilityNames.EnableHotPartitionKeyThrottling }],
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
expect(isHotPartitionKeyThrottlingEnabled()).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false for a SQL account without the capability", () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
capabilities: [{ name: Constants.CapabilityNames.EnableAutoScale }],
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
expect(isHotPartitionKeyThrottlingEnabled()).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false for a non-SQL account even with the capability", () => {
|
||||
updateUserContext({
|
||||
databaseAccount: {
|
||||
properties: {
|
||||
capabilities: [
|
||||
{ name: Constants.CapabilityNames.EnableCassandra },
|
||||
{ name: Constants.CapabilityNames.EnableHotPartitionKeyThrottling },
|
||||
],
|
||||
},
|
||||
} as DatabaseAccount,
|
||||
});
|
||||
|
||||
expect(isHotPartitionKeyThrottlingEnabled()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -34,3 +34,10 @@ export const isFullTextSearchPreviewFeaturesEnabled = (targetAccountOverride?: A
|
||||
isCapabilityEnabled(Constants.CapabilityNames.EnableNoSQLFullTextSearchPreviewFeatures, targetAccountOverride)
|
||||
);
|
||||
};
|
||||
|
||||
export const isHotPartitionKeyThrottlingEnabled = (targetAccountOverride?: AccountOverride): boolean => {
|
||||
return (
|
||||
userContext.apiType === "SQL" &&
|
||||
isCapabilityEnabled(Constants.CapabilityNames.EnableHotPartitionKeyThrottling, targetAccountOverride)
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1096,6 +1096,11 @@ export interface CassandraViewCreateUpdateProperties {
|
||||
options?: CreateUpdateOptions;
|
||||
}
|
||||
|
||||
export interface HotPartitionKeyRateLimitingPolicy {
|
||||
/* Maximum throughput utilization for partition keys (in percent) */
|
||||
maximumPerPartitionKeyThroughputUtilizationPercent: number;
|
||||
}
|
||||
|
||||
/* Cosmos DB resource throughput object. Either throughput is required or autoscaleSettings is required, but not both. */
|
||||
export interface ThroughputSettingsResource {
|
||||
/* Value of the Cosmos DB resource throughput. Either throughput is required or autoscaleSettings is required, but not both. */
|
||||
@@ -1113,6 +1118,8 @@ export interface ThroughputSettingsResource {
|
||||
readonly softAllowedMaximumThroughput?: string;
|
||||
/* Array of throughput bucket limits to be applied to the Cosmos DB container */
|
||||
throughputBuckets?: ThroughputBucketResource[];
|
||||
/* Object describing the Rate Limiting policy for Hot Partition Keys */
|
||||
hotPartitionKeyRateLimitingPolicy?: HotPartitionKeyRateLimitingPolicy | null;
|
||||
}
|
||||
|
||||
/* Cosmos DB provisioned throughput settings object */
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
"./src/Utils/Base64Utils.ts",
|
||||
"./src/Utils/BlobUtils.ts",
|
||||
"./src/Utils/CapabilityUtils.ts",
|
||||
"./src/Utils/CapabilityUtils.test.ts",
|
||||
"./src/Utils/CloudUtils.ts",
|
||||
"./src/Utils/EndpointUtils.ts",
|
||||
"./src/Utils/GitHubUtils.test.ts",
|
||||
|
||||
Reference in New Issue
Block a user