Initial version of Hot Partition Key Rate Limiting Policy

This commit is contained in:
Chuck Skelton
2026-06-16 18:12:04 -07:00
parent 0e175c8a9c
commit 4cdcbf1f6f
18 changed files with 325 additions and 14 deletions
+5
View File
@@ -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",
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
+1
View File
@@ -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,
};
}
+8
View File
@@ -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)
+7
View File
@@ -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}
/>
);
@@ -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", () => {
@@ -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>
);
}
@@ -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]}
+3 -1
View File
@@ -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}}",
+47
View File
@@ -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);
});
});
});
+7
View File
@@ -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 */
+1
View File
@@ -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",