Remove RU Max Limits

This commit is contained in:
Steve Faulkner 2020-11-11 13:50:17 -06:00
parent a133134b8b
commit 415ebc505b
28 changed files with 145 additions and 846 deletions

View File

@ -1,45 +0,0 @@
import * as DataModels from "../../Contracts/DataModels";
import * as HeadersUtility from "../HeadersUtility";
import * as ViewModels from "../../Contracts/ViewModels";
import { ContainerDefinition, Resource } from "@azure/cosmos";
import { HttpHeaders } from "../Constants";
import { RequestOptions } from "@azure/cosmos/dist-esm";
import { client } from "../CosmosClient";
import { handleError } from "../ErrorHandlingUtils";
import { logConsoleProgress } from "../../Utils/NotificationConsoleUtils";
interface ResourceWithStatistics {
statistics: DataModels.Statistic[];
}
export const readCollectionQuotaInfo = async (
collection: ViewModels.Collection
): Promise<DataModels.CollectionQuotaInfo> => {
const clearMessage = logConsoleProgress(`Querying containers for database ${collection.id}`);
const options: RequestOptions = {};
options.populateQuotaInfo = true;
options.initialHeaders = options.initialHeaders || {};
options.initialHeaders[HttpHeaders.populatePartitionStatistics] = true;
try {
const response = await client()
.database(collection.databaseId)
.container(collection.id())
.read(options);
const quota: DataModels.CollectionQuotaInfo = HeadersUtility.getQuota(response.headers);
const resource = response.resource as ContainerDefinition & Resource & ResourceWithStatistics;
quota["usageSizeInKB"] = resource.statistics.reduce(
(previousValue: number, currentValue: DataModels.Statistic) => previousValue + currentValue.sizeInKB,
0
);
quota["numPartitions"] = resource.statistics.length;
quota["uniqueKeyPolicy"] = collection.uniqueKeyPolicy; // TODO: Remove after refactoring (#119617)
return quota;
} catch (error) {
handleError(error, "ReadCollectionQuotaInfo", `Error while querying quota info for container ${collection.id}`);
throw error;
} finally {
clearMessage();
}
};

View File

@ -1,25 +0,0 @@
import { updateOfferThroughputBeyondLimit } from "./updateOfferThroughputBeyondLimit";
describe("updateOfferThroughputBeyondLimit", () => {
it("should call fetch", async () => {
window.fetch = jest.fn(() => {
return {
ok: true
};
});
window.dataExplorer = {
logConsoleData: jest.fn(),
deleteInProgressConsoleDataWithId: jest.fn()
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any;
await updateOfferThroughputBeyondLimit({
subscriptionId: "foo",
resourceGroup: "foo",
databaseAccountName: "foo",
databaseName: "foo",
throughput: 1000000000,
offerIsRUPerMinuteThroughputEnabled: false
});
expect(window.fetch).toHaveBeenCalled();
});
});

View File

@ -1,57 +0,0 @@
import { Platform, configContext } from "../../ConfigContext";
import { getAuthorizationHeader } from "../../Utils/AuthorizationUtils";
import { AutoPilotOfferSettings } from "../../Contracts/DataModels";
import { logConsoleProgress, logConsoleInfo } from "../../Utils/NotificationConsoleUtils";
import { HttpHeaders } from "../Constants";
import { handleError } from "../ErrorHandlingUtils";
interface UpdateOfferThroughputRequest {
subscriptionId: string;
resourceGroup: string;
databaseAccountName: string;
databaseName: string;
collectionName?: string;
throughput: number;
offerIsRUPerMinuteThroughputEnabled: boolean;
offerAutopilotSettings?: AutoPilotOfferSettings;
}
export async function updateOfferThroughputBeyondLimit(request: UpdateOfferThroughputRequest): Promise<void> {
if (configContext.platform !== Platform.Portal) {
throw new Error("Updating throughput beyond specified limit is not supported on this platform");
}
const resourceDescriptionInfo = request.collectionName
? `database ${request.databaseName} and container ${request.collectionName}`
: `database ${request.databaseName}`;
const clearMessage = logConsoleProgress(
`Requesting increase in throughput to ${request.throughput} for ${resourceDescriptionInfo}`
);
const url = `${configContext.BACKEND_ENDPOINT}/api/offerthroughputrequest/updatebeyondspecifiedlimit`;
const authorizationHeader = getAuthorizationHeader();
const response = await fetch(url, {
method: "POST",
body: JSON.stringify(request),
headers: { [authorizationHeader.header]: authorizationHeader.token, [HttpHeaders.contentType]: "application/json" }
});
if (response.ok) {
logConsoleInfo(
`Successfully requested an increase in throughput to ${request.throughput} for ${resourceDescriptionInfo}`
);
clearMessage();
return undefined;
}
const error = await response.json();
handleError(
error,
"updateOfferThroughputBeyondLimit",
`Failed to request an increase in throughput for ${request.throughput}`
);
clearMessage();
throw error;
}

View File

@ -191,18 +191,6 @@ export interface OfferWithHeaders extends Offer {
headers: any; headers: any;
} }
export interface CollectionQuotaInfo {
storedProcedures: number;
triggers: number;
functions: number;
documentsSize: number;
collectionSize: number;
documentsCount: number;
usageSizeInKB: number;
numPartitions: number;
uniqueKeyPolicy?: UniqueKeyPolicy; // TODO: This should ideally not be a part of the collection quota. Remove after refactoring. (#119617)
}
export interface OfferThroughputInfo { export interface OfferThroughputInfo {
minimumRUForCollection: number; minimumRUForCollection: number;
numPhysicalPartitions: number; numPhysicalPartitions: number;

View File

@ -117,7 +117,6 @@ export interface Collection extends CollectionBase {
analyticalStorageTtl: ko.Observable<number>; analyticalStorageTtl: ko.Observable<number>;
indexingPolicy: ko.Observable<DataModels.IndexingPolicy>; indexingPolicy: ko.Observable<DataModels.IndexingPolicy>;
uniqueKeyPolicy: DataModels.UniqueKeyPolicy; uniqueKeyPolicy: DataModels.UniqueKeyPolicy;
quotaInfo: ko.Observable<DataModels.CollectionQuotaInfo>;
offer: ko.Observable<DataModels.Offer>; offer: ko.Observable<DataModels.Offer>;
conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>; conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>;
changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>; changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>;

View File

@ -1,54 +1,49 @@
import { RequestOptions } from "@azure/cosmos/dist-esm";
import { IPivotItemProps, IPivotProps, Pivot, PivotItem } from "office-ui-fabric-react";
import * as React from "react"; import * as React from "react";
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
import * as Constants from "../../../Common/Constants";
import * as DataModels from "../../../Contracts/DataModels";
import * as SharedConstants from "../../../Shared/Constants";
import * as ViewModels from "../../../Contracts/ViewModels";
import DiscardIcon from "../../../../images/discard.svg"; import DiscardIcon from "../../../../images/discard.svg";
import SaveIcon from "../../../../images/save-cosmos.svg"; import SaveIcon from "../../../../images/save-cosmos.svg";
import { traceStart, traceFailure, traceSuccess, trace } from "../../../Shared/Telemetry/TelemetryProcessor"; import * as Constants from "../../../Common/Constants";
import { Action, ActionModifiers } from "../../../Shared/Telemetry/TelemetryConstants"; import { getIndexTransformationProgress } from "../../../Common/dataAccess/getIndexTransformationProgress";
import { RequestOptions } from "@azure/cosmos/dist-esm"; import { readMongoDBCollectionThroughRP } from "../../../Common/dataAccess/readMongoDBCollection";
import Explorer from "../../Explorer";
import { updateOffer } from "../../../Common/dataAccess/updateOffer";
import { updateCollection, updateMongoDBCollectionThroughRP } from "../../../Common/dataAccess/updateCollection"; import { updateCollection, updateMongoDBCollectionThroughRP } 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 { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/2020-04-01/types";
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils";
import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponentProps } from "../../Controls/CommandButton/CommandButtonComponent";
import { userContext } from "../../../UserContext"; import Explorer from "../../Explorer";
import { updateOfferThroughputBeyondLimit } from "../../../Common/dataAccess/updateOfferThroughputBeyondLimit";
import SettingsTab from "../../Tabs/SettingsTabV2"; import SettingsTab from "../../Tabs/SettingsTabV2";
import { throughputUnit } from "./SettingsRenderUtils"; import "./SettingsComponent.less";
import { ScaleComponent, ScaleComponentProps } from "./SettingsSubComponents/ScaleComponent";
import {
MongoIndexingPolicyComponent,
MongoIndexingPolicyComponentProps
} from "./SettingsSubComponents/MongoIndexingPolicy/MongoIndexingPolicyComponent";
import {
getMaxRUs,
hasDatabaseSharedThroughput,
GeospatialConfigType,
TtlType,
ChangeFeedPolicyState,
SettingsV2TabTypes,
getTabTitle,
isDirty,
AddMongoIndexProps,
MongoIndexTypes,
parseConflictResolutionMode,
parseConflictResolutionProcedure,
getMongoNotification
} from "./SettingsUtils";
import { import {
ConflictResolutionComponent, ConflictResolutionComponent,
ConflictResolutionComponentProps ConflictResolutionComponentProps
} from "./SettingsSubComponents/ConflictResolutionComponent"; } from "./SettingsSubComponents/ConflictResolutionComponent";
import { SubSettingsComponent, SubSettingsComponentProps } from "./SettingsSubComponents/SubSettingsComponent";
import { Pivot, PivotItem, IPivotProps, IPivotItemProps } from "office-ui-fabric-react";
import "./SettingsComponent.less";
import { IndexingPolicyComponent, IndexingPolicyComponentProps } from "./SettingsSubComponents/IndexingPolicyComponent"; import { IndexingPolicyComponent, IndexingPolicyComponentProps } from "./SettingsSubComponents/IndexingPolicyComponent";
import { MongoDBCollectionResource, MongoIndex } from "../../../Utils/arm/generatedClients/2020-04-01/types"; import {
import { readMongoDBCollectionThroughRP } from "../../../Common/dataAccess/readMongoDBCollection"; MongoIndexingPolicyComponent,
import { getIndexTransformationProgress } from "../../../Common/dataAccess/getIndexTransformationProgress"; MongoIndexingPolicyComponentProps
import { getErrorMessage, getErrorStack } from "../../../Common/ErrorHandlingUtils"; } from "./SettingsSubComponents/MongoIndexingPolicy/MongoIndexingPolicyComponent";
import { ScaleComponent, ScaleComponentProps } from "./SettingsSubComponents/ScaleComponent";
import { SubSettingsComponent, SubSettingsComponentProps } from "./SettingsSubComponents/SubSettingsComponent";
import {
AddMongoIndexProps,
ChangeFeedPolicyState,
GeospatialConfigType,
getMongoNotification,
getTabTitle,
hasDatabaseSharedThroughput,
isDirty,
MongoIndexTypes,
parseConflictResolutionMode,
parseConflictResolutionProcedure,
SettingsV2TabTypes,
TtlType
} from "./SettingsUtils";
interface SettingsV2TabInfo { interface SettingsV2TabInfo {
tab: SettingsV2TabTypes; tab: SettingsV2TabTypes;
@ -450,7 +445,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
if (this.state.isScaleSaveable) { if (this.state.isScaleSaveable) {
const newThroughput = this.state.throughput; const newThroughput = this.state.throughput;
const newOffer: DataModels.Offer = { ...this.collection.offer() }; const newOffer: DataModels.Offer = { ...this.collection.offer() };
const originalThroughputValue: number = this.state.throughput;
if (newOffer.content) { if (newOffer.content) {
newOffer.content.offerThroughput = newThroughput; newOffer.content.offerThroughput = newThroughput;
@ -488,34 +482,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
} }
} }
if (
getMaxRUs(this.collection, this.container) <=
SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
newThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
this.container
) {
const requestPayload = {
subscriptionId: userContext.subscriptionId,
databaseAccountName: userContext.databaseAccount.name,
resourceGroup: userContext.resourceGroup,
databaseName: this.collection.databaseId,
collectionName: this.collection.id(),
throughput: newThroughput,
offerIsRUPerMinuteThroughputEnabled: false
};
await updateOfferThroughputBeyondLimit(requestPayload);
this.collection.offer().content.offerThroughput = originalThroughputValue;
this.setState({
isScaleSaveable: false,
isScaleDiscardable: false,
throughput: originalThroughputValue,
throughputBaseline: originalThroughputValue,
initialNotification: {
description: `Throughput update for ${newThroughput} ${throughputUnit}`
} as DataModels.Notification
});
} else {
const updateOfferParams: DataModels.UpdateOfferParams = { const updateOfferParams: DataModels.UpdateOfferParams = {
databaseId: this.collection.databaseId, databaseId: this.collection.databaseId,
collectionId: this.collection.id(), collectionId: this.collection.id(),
@ -545,7 +511,6 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
}); });
} }
} }
}
this.container.isRefreshingExplorer(false); this.container.isRefreshingExplorer(false);
this.setBaseline(); this.setBaseline();
this.setState({ wasAutopilotOriginallySet: this.state.isAutoPilotSelected }); this.setState({ wasAutopilotOriginallySet: this.state.isAutoPilotSelected });

View File

@ -1,14 +1,13 @@
import { shallow } from "enzyme"; import { shallow } from "enzyme";
import ko from "knockout";
import React from "react"; import React from "react";
import { ScaleComponent, ScaleComponentProps } from "./ScaleComponent";
import { container, collection } from "../TestUtils";
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
import Explorer from "../../../Explorer";
import * as Constants from "../../../../Common/Constants"; import * as Constants from "../../../../Common/Constants";
import * as DataModels from "../../../../Contracts/DataModels"; import * as DataModels from "../../../../Contracts/DataModels";
import Explorer from "../../../Explorer";
import { throughputUnit } from "../SettingsRenderUtils"; import { throughputUnit } from "../SettingsRenderUtils";
import * as SharedConstants from "../../../../Shared/Constants"; import { collection, container } from "../TestUtils";
import ko from "knockout"; import { ScaleComponent, ScaleComponentProps } from "./ScaleComponent";
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
describe("ScaleComponent", () => { describe("ScaleComponent", () => {
const nonNationalCloudContainer = new Explorer(); const nonNationalCloudContainer = new Explorer();
@ -48,9 +47,7 @@ describe("ScaleComponent", () => {
let wrapper = shallow(<ScaleComponent {...baseProps} />); let wrapper = shallow(<ScaleComponent {...baseProps} />);
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
expect(wrapper.exists(ThroughputInputAutoPilotV3Component)).toEqual(true); expect(wrapper.exists(ThroughputInputAutoPilotV3Component)).toEqual(true);
expect(wrapper.exists("#throughputApplyLongDelayMessage")).toEqual(true);
expect(wrapper.exists("#throughputApplyShortDelayMessage")).toEqual(false); expect(wrapper.exists("#throughputApplyShortDelayMessage")).toEqual(false);
expect(wrapper.find("#throughputApplyLongDelayMessage").html()).toContain(targetThroughput);
const newCollection = { ...collection }; const newCollection = { ...collection };
const maxThroughput = 5000; const maxThroughput = 5000;
@ -109,11 +106,6 @@ describe("ScaleComponent", () => {
expect(scaleComponent.isAutoScaleEnabled()).toEqual(true); expect(scaleComponent.isAutoScaleEnabled()).toEqual(true);
}); });
it("getMaxRUThroughputInputLimit", () => {
const scaleComponent = new ScaleComponent(baseProps);
expect(scaleComponent.getMaxRUThroughputInputLimit()).toEqual(40000);
});
it("getThroughputTitle", () => { it("getThroughputTitle", () => {
let scaleComponent = new ScaleComponent(baseProps); let scaleComponent = new ScaleComponent(baseProps);
expect(scaleComponent.getThroughputTitle()).toEqual("Throughput (6,000 - unlimited RU/s)"); expect(scaleComponent.getThroughputTitle()).toEqual("Throughput (6,000 - unlimited RU/s)");
@ -126,26 +118,4 @@ describe("ScaleComponent", () => {
scaleComponent = new ScaleComponent(newProps); scaleComponent = new ScaleComponent(newProps);
expect(scaleComponent.getThroughputTitle()).toEqual("Throughput (autoscale)"); expect(scaleComponent.getThroughputTitle()).toEqual("Throughput (autoscale)");
}); });
it("canThroughputExceedMaximumValue", () => {
let scaleComponent = new ScaleComponent(baseProps);
expect(scaleComponent.canThroughputExceedMaximumValue()).toEqual(true);
const newProps = { ...baseProps, container: nonNationalCloudContainer };
scaleComponent = new ScaleComponent(newProps);
expect(scaleComponent.canThroughputExceedMaximumValue()).toEqual(true);
});
it("getThroughputWarningMessage", () => {
const throughputBeyondLimit = SharedConstants.CollectionCreation.DefaultCollectionRUs1Million + 1000;
const throughputBeyondMaxRus = SharedConstants.CollectionCreation.DefaultCollectionRUs1Million - 1000;
const newProps = { ...baseProps, container: nonNationalCloudContainer, throughput: throughputBeyondLimit };
let scaleComponent = new ScaleComponent(newProps);
expect(scaleComponent.getThroughputWarningMessage().props.id).toEqual("updateThroughputBeyondLimitWarningMessage");
newProps.throughput = throughputBeyondMaxRus;
scaleComponent = new ScaleComponent(newProps);
expect(scaleComponent.getThroughputWarningMessage().props.id).toEqual("updateThroughputDelayedApplyWarningMessage");
});
}); });

View File

@ -1,24 +1,20 @@
import { Label, MessageBar, MessageBarType, Stack, Text, TextField } from "office-ui-fabric-react";
import * as React from "react"; import * as React from "react";
import * as Constants from "../../../../Common/Constants"; import * as Constants from "../../../../Common/Constants";
import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component"; import { configContext, Platform } from "../../../../ConfigContext";
import * as ViewModels from "../../../../Contracts/ViewModels";
import * as DataModels from "../../../../Contracts/DataModels"; import * as DataModels from "../../../../Contracts/DataModels";
import * as SharedConstants from "../../../../Shared/Constants"; import * as ViewModels from "../../../../Contracts/ViewModels";
import * as AutoPilotUtils from "../../../../Utils/AutoPilotUtils";
import Explorer from "../../../Explorer"; import Explorer from "../../../Explorer";
import { import {
getTextFieldStyles, getTextFieldStyles,
subComponentStackProps,
titleAndInputStackProps,
throughputUnit,
getThroughputApplyLongDelayMessage,
getThroughputApplyShortDelayMessage, getThroughputApplyShortDelayMessage,
updateThroughputBeyondLimitWarningMessage, subComponentStackProps,
updateThroughputDelayedApplyWarningMessage throughputUnit,
titleAndInputStackProps
} from "../SettingsRenderUtils"; } from "../SettingsRenderUtils";
import { getMaxRUs, getMinRUs, hasDatabaseSharedThroughput } from "../SettingsUtils"; import { getMinRUs, hasDatabaseSharedThroughput } from "../SettingsUtils";
import * as AutoPilotUtils from "../../../../Utils/AutoPilotUtils"; import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component";
import { Text, TextField, Stack, Label, MessageBar, MessageBarType } from "office-ui-fabric-react";
import { configContext, Platform } from "../../../../ConfigContext";
export interface ScaleComponentProps { export interface ScaleComponentProps {
collection: ViewModels.Collection; collection: ViewModels.Collection;
@ -75,40 +71,17 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
); );
}; };
public getMaxRUThroughputInputLimit = (): number => {
if (configContext.platform === Platform.Hosted && this.props.collection.partitionKey) {
return SharedConstants.CollectionCreation.DefaultCollectionRUs1Million;
}
return getMaxRUs(this.props.collection, this.props.container);
};
public getThroughputTitle = (): string => { public getThroughputTitle = (): string => {
if (this.props.isAutoPilotSelected) { if (this.props.isAutoPilotSelected) {
return AutoPilotUtils.getAutoPilotHeaderText(); return AutoPilotUtils.getAutoPilotHeaderText();
} }
const minThroughput: string = getMinRUs(this.props.collection, this.props.container).toLocaleString(); const minThroughput: string = getMinRUs(this.props.collection, this.props.container).toLocaleString();
const maxThroughput: string = const maxThroughput: string = !this.props.isFixedContainer ? "unlimited" : "10000";
this.canThroughputExceedMaximumValue() && !this.props.isFixedContainer
? "unlimited"
: getMaxRUs(this.props.collection, this.props.container).toLocaleString();
return `Throughput (${minThroughput} - ${maxThroughput} RU/s)`; return `Throughput (${minThroughput} - ${maxThroughput} RU/s)`;
}; };
public canThroughputExceedMaximumValue = (): boolean => {
return (
!this.props.isFixedContainer &&
configContext.platform === Platform.Portal &&
!this.props.container.isRunningOnNationalCloud()
);
};
public getInitialNotificationElement = (): JSX.Element => { public getInitialNotificationElement = (): JSX.Element => {
if (this.props.initialNotification) {
return this.getLongDelayMessage();
}
const offer = this.props.collection?.offer && this.props.collection.offer(); const offer = this.props.collection?.offer && this.props.collection.offer();
if ( if (
offer && offer &&
@ -135,47 +108,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
return undefined; return undefined;
}; };
public getThroughputWarningMessage = (): JSX.Element => {
const throughputExceedsBackendLimits: boolean =
this.canThroughputExceedMaximumValue() &&
getMaxRUs(this.props.collection, this.props.container) <=
SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
this.props.throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million;
if (throughputExceedsBackendLimits && !!this.props.collection.partitionKey && !this.props.isFixedContainer) {
return updateThroughputBeyondLimitWarningMessage;
}
const throughputExceedsMaxValue: boolean =
!this.isEmulator && this.props.throughput > getMaxRUs(this.props.collection, this.props.container);
if (throughputExceedsMaxValue && !!this.props.collection.partitionKey && !this.props.isFixedContainer) {
return updateThroughputDelayedApplyWarningMessage;
}
return undefined;
};
public getLongDelayMessage = (): JSX.Element => {
const matches: string[] = this.props.initialNotification?.description.match(
`Throughput update for (.*) ${throughputUnit}`
);
const throughput = this.props.throughputBaseline;
const targetThroughput: number = matches.length > 1 && Number(matches[1]);
if (targetThroughput) {
return getThroughputApplyLongDelayMessage(
this.props.wasAutopilotOriginallySet,
throughput,
throughputUnit,
this.props.collection.databaseId,
this.props.collection.id(),
targetThroughput
);
}
return <></>;
};
private getThroughputInputComponent = (): JSX.Element => ( private getThroughputInputComponent = (): JSX.Element => (
<ThroughputInputAutoPilotV3Component <ThroughputInputAutoPilotV3Component
databaseAccount={this.props.container.databaseAccount()} databaseAccount={this.props.container.databaseAccount()}
@ -184,9 +116,7 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
throughputBaseline={this.props.throughputBaseline} throughputBaseline={this.props.throughputBaseline}
onThroughputChange={this.props.onThroughputChange} onThroughputChange={this.props.onThroughputChange}
minimum={getMinRUs(this.props.collection, this.props.container)} minimum={getMinRUs(this.props.collection, this.props.container)}
maximum={this.getMaxRUThroughputInputLimit()}
isEnabled={!hasDatabaseSharedThroughput(this.props.collection)} isEnabled={!hasDatabaseSharedThroughput(this.props.collection)}
canExceedMaximumValue={this.canThroughputExceedMaximumValue()}
label={this.getThroughputTitle()} label={this.getThroughputTitle()}
isEmulator={this.isEmulator} isEmulator={this.isEmulator}
isFixed={this.props.isFixedContainer} isFixed={this.props.isFixedContainer}
@ -199,7 +129,6 @@ export class ScaleComponent extends React.Component<ScaleComponentProps> {
spendAckChecked={false} spendAckChecked={false}
onScaleSaveableChange={this.props.onScaleSaveableChange} onScaleSaveableChange={this.props.onScaleSaveableChange}
onScaleDiscardableChange={this.props.onScaleDiscardableChange} onScaleDiscardableChange={this.props.onScaleDiscardableChange}
getThroughputWarningMessage={this.getThroughputWarningMessage}
/> />
); );

View File

@ -15,7 +15,6 @@ describe("ThroughputInputAutoPilotV3Component", () => {
throughputBaseline: 100, throughputBaseline: 100,
onThroughputChange: undefined, onThroughputChange: undefined,
minimum: 10000, minimum: 10000,
maximum: 400,
step: 100, step: 100,
isEnabled: true, isEnabled: true,
isEmulator: false, isEmulator: false,
@ -38,8 +37,7 @@ describe("ThroughputInputAutoPilotV3Component", () => {
}, },
onScaleDiscardableChange: () => { onScaleDiscardableChange: () => {
return; return;
}, }
getThroughputWarningMessage: () => undefined
}; };
it("throughput input visible", () => { it("throughput input visible", () => {

View File

@ -1,35 +1,33 @@
import React from "react";
import * as AutoPilotUtils from "../../../../../Utils/AutoPilotUtils";
import { import {
getTextFieldStyles, Checkbox,
getToolTipContainer,
noLeftPaddingCheckBoxStyle,
titleAndInputStackProps,
checkBoxAndInputStackProps,
getChoiceGroupStyles,
messageBarStyles,
getEstimatedSpendElement,
getEstimatedAutoscaleSpendElement,
getAutoPilotV3SpendElement,
manualToAutoscaleDisclaimerElement
} from "../../SettingsRenderUtils";
import {
Text,
TextField,
ChoiceGroup, ChoiceGroup,
IChoiceGroupOption, IChoiceGroupOption,
Checkbox,
Stack,
Label, Label,
Link, Link,
MessageBar, MessageBar,
MessageBarType MessageBarType,
Stack,
Text,
TextField
} from "office-ui-fabric-react"; } from "office-ui-fabric-react";
import { ToolTipLabelComponent } from "../ToolTipLabelComponent"; import React from "react";
import { getSanitizedInputValue, IsComponentDirtyResult, isDirty } from "../../SettingsUtils";
import * as SharedConstants from "../../../../../Shared/Constants";
import * as DataModels from "../../../../../Contracts/DataModels"; import * as DataModels from "../../../../../Contracts/DataModels";
import { Int32 } from "../../../../Panes/Tables/Validators/EntityPropertyValidationCommon"; import * as AutoPilotUtils from "../../../../../Utils/AutoPilotUtils";
import {
checkBoxAndInputStackProps,
getAutoPilotV3SpendElement,
getChoiceGroupStyles,
getEstimatedAutoscaleSpendElement,
getEstimatedSpendElement,
getTextFieldStyles,
getToolTipContainer,
manualToAutoscaleDisclaimerElement,
messageBarStyles,
noLeftPaddingCheckBoxStyle,
titleAndInputStackProps
} from "../../SettingsRenderUtils";
import { getSanitizedInputValue, IsComponentDirtyResult, isDirty } from "../../SettingsUtils";
import { ToolTipLabelComponent } from "../ToolTipLabelComponent";
export interface ThroughputInputAutoPilotV3Props { export interface ThroughputInputAutoPilotV3Props {
databaseAccount: DataModels.DatabaseAccount; databaseAccount: DataModels.DatabaseAccount;
@ -38,7 +36,6 @@ export interface ThroughputInputAutoPilotV3Props {
throughputBaseline: number; throughputBaseline: number;
onThroughputChange: (newThroughput: number) => void; onThroughputChange: (newThroughput: number) => void;
minimum: number; minimum: number;
maximum: number;
step?: number; step?: number;
isEnabled?: boolean; isEnabled?: boolean;
spendAckChecked?: boolean; spendAckChecked?: boolean;
@ -59,7 +56,6 @@ export interface ThroughputInputAutoPilotV3Props {
onMaxAutoPilotThroughputChange: (newThroughput: number) => void; onMaxAutoPilotThroughputChange: (newThroughput: number) => void;
onScaleSaveableChange: (isScaleSaveable: boolean) => void; onScaleSaveableChange: (isScaleSaveable: boolean) => void;
onScaleDiscardableChange: (isScaleDiscardable: boolean) => void; onScaleDiscardableChange: (isScaleDiscardable: boolean) => void;
getThroughputWarningMessage: () => JSX.Element;
} }
interface ThroughputInputAutoPilotV3State { interface ThroughputInputAutoPilotV3State {
@ -119,13 +115,7 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
if (isDirty(this.props.throughput, this.props.throughputBaseline)) { if (isDirty(this.props.throughput, this.props.throughputBaseline)) {
isDiscardable = true; isDiscardable = true;
isSaveable = true; isSaveable = true;
if ( if (!this.props.throughput || this.props.throughput < this.props.minimum) {
!this.props.throughput ||
this.props.throughput < this.props.minimum ||
(this.props.throughput > this.props.maximum && (this.props.isEmulator || this.props.isFixed)) ||
(this.props.throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
!this.props.canExceedMaximumValue)
) {
isSaveable = false; isSaveable = false;
} }
} }
@ -141,8 +131,8 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
}; };
this.step = this.props.step ?? ThroughputInputAutoPilotV3Component.defaultStep; this.step = this.props.step ?? ThroughputInputAutoPilotV3Component.defaultStep;
this.throughputInputMaxValue = this.props.canExceedMaximumValue ? Int32.Max : this.props.maximum; this.throughputInputMaxValue = Number.MAX_SAFE_INTEGER;
this.autoPilotInputMaxValue = this.props.isFixed ? this.props.maximum : Int32.Max; this.autoPilotInputMaxValue = Number.MAX_SAFE_INTEGER;
} }
public hasProvisioningTypeChanged = (): boolean => public hasProvisioningTypeChanged = (): boolean =>
@ -306,12 +296,6 @@ export class ThroughputInputAutoPilotV3Component extends React.Component<
onChange={this.onThroughputChange} onChange={this.onThroughputChange}
/> />
{this.props.getThroughputWarningMessage() && (
<MessageBar messageBarType={MessageBarType.warning} styles={messageBarStyles}>
{this.props.getThroughputWarningMessage()}
</MessageBar>
)}
{!this.props.isEmulator && this.getRequestUnitsUsageCost()} {!this.props.isEmulator && this.getRequestUnitsUsageCost()}
{this.props.spendAckVisible && ( {this.props.spendAckVisible && (

View File

@ -8,29 +8,6 @@ exports[`ScaleComponent renders with correct intiial notification 1`] = `
} }
} }
> >
<StyledMessageBarBase
messageBarType={5}
>
<Text
id="throughputApplyLongDelayMessage"
styles={
Object {
"root": Object {
"fontSize": 12,
},
}
}
>
A request to increase the throughput is currently in progress. This operation will take 1-3 business days to complete. View the latest status in Notifications.
<br />
Database:
test
, Container:
test
, Current autoscale throughput: 100 - 1000 RU/s, Target autoscale throughput: 600 - 6000 RU/s
</Text>
</StyledMessageBarBase>
<Stack <Stack
tokens={ tokens={
Object { Object {
@ -39,8 +16,6 @@ exports[`ScaleComponent renders with correct intiial notification 1`] = `
} }
> >
<ThroughputInputAutoPilotV3Component <ThroughputInputAutoPilotV3Component
canExceedMaximumValue={true}
getThroughputWarningMessage={[Function]}
isAutoPilotSelected={false} isAutoPilotSelected={false}
isEmulator={false} isEmulator={false}
isEnabled={true} isEnabled={true}
@ -48,7 +23,6 @@ exports[`ScaleComponent renders with correct intiial notification 1`] = `
label="Throughput (6,000 - unlimited RU/s)" label="Throughput (6,000 - unlimited RU/s)"
maxAutoPilotThroughput={4000} maxAutoPilotThroughput={4000}
maxAutoPilotThroughputBaseline={4000} maxAutoPilotThroughputBaseline={4000}
maximum={40000}
minimum={6000} minimum={6000}
onAutoPilotSelected={[Function]} onAutoPilotSelected={[Function]}
onMaxAutoPilotThroughputChange={[Function]} onMaxAutoPilotThroughputChange={[Function]}

View File

@ -1,6 +1,5 @@
import { collection, container } from "./TestUtils"; import { collection, container } from "./TestUtils";
import { import {
getMaxRUs,
getMinRUs, getMinRUs,
getMongoIndexType, getMongoIndexType,
getMongoNotification, getMongoNotification,
@ -23,11 +22,6 @@ import * as ViewModels from "../../../Contracts/ViewModels";
import ko from "knockout"; import ko from "knockout";
describe("SettingsUtils", () => { describe("SettingsUtils", () => {
it("getMaxRUs", () => {
expect(collection.offer().content.collectionThroughputInfo.numPhysicalPartitions).toEqual(4);
expect(getMaxRUs(collection, container)).toEqual(40000);
});
it("getMinRUs", () => { it("getMinRUs", () => {
expect(collection.offer().content.collectionThroughputInfo.numPhysicalPartitions).toEqual(4); expect(collection.offer().content.collectionThroughputInfo.numPhysicalPartitions).toEqual(4);
expect(getMinRUs(collection, container)).toEqual(6000); expect(getMinRUs(collection, container)).toEqual(6000);

View File

@ -1,11 +1,9 @@
import * as ViewModels from "../../../Contracts/ViewModels";
import * as DataModels from "../../../Contracts/DataModels";
import * as Constants from "../../../Common/Constants"; import * as Constants from "../../../Common/Constants";
import * as DataModels from "../../../Contracts/DataModels";
import * as ViewModels from "../../../Contracts/ViewModels";
import * as SharedConstants from "../../../Shared/Constants"; import * as SharedConstants from "../../../Shared/Constants";
import * as PricingUtils from "../../../Utils/PricingUtils";
import Explorer from "../../Explorer";
import { MongoIndex } from "../../../Utils/arm/generatedClients/2020-04-01/types"; import { MongoIndex } from "../../../Utils/arm/generatedClients/2020-04-01/types";
import Explorer from "../../Explorer";
const zeroValue = 0; const zeroValue = 0;
export type isDirtyTypes = boolean | string | number | DataModels.IndexingPolicy; export type isDirtyTypes = boolean | string | number | DataModels.IndexingPolicy;
@ -71,22 +69,6 @@ export const hasDatabaseSharedThroughput = (collection: ViewModels.Collection):
return database?.isDatabaseShared() && !collection.offer(); return database?.isDatabaseShared() && !collection.offer();
}; };
export const getMaxRUs = (collection: ViewModels.Collection, container: Explorer): number => {
const isTryCosmosDBSubscription = container?.isTryCosmosDBSubscription() || false;
if (isTryCosmosDBSubscription) {
return Constants.TryCosmosExperience.maxRU;
}
const numPartitionsFromOffer: number =
collection?.offer && collection.offer()?.content?.collectionThroughputInfo?.numPhysicalPartitions;
const numPartitionsFromQuotaInfo: number = collection?.quotaInfo()?.numPartitions;
const numPartitions = numPartitionsFromOffer ?? numPartitionsFromQuotaInfo ?? 1;
return SharedConstants.CollectionCreation.MaxRUPerPartition * numPartitions;
};
export const getMinRUs = (collection: ViewModels.Collection, container: Explorer): number => { export const getMinRUs = (collection: ViewModels.Collection, container: Explorer): number => {
const isTryCosmosDBSubscription = container?.isTryCosmosDBSubscription(); const isTryCosmosDBSubscription = container?.isTryCosmosDBSubscription();
if (isTryCosmosDBSubscription) { if (isTryCosmosDBSubscription) {
@ -105,21 +87,7 @@ export const getMinRUs = (collection: ViewModels.Collection, container: Explorer
return collectionThroughputInfo.minimumRUForCollection; return collectionThroughputInfo.minimumRUForCollection;
} }
const numPartitions = collectionThroughputInfo?.numPhysicalPartitions ?? collection.quotaInfo()?.numPartitions;
if (!numPartitions || numPartitions === 1) {
return SharedConstants.CollectionCreation.DefaultCollectionRUs400; return SharedConstants.CollectionCreation.DefaultCollectionRUs400;
}
const baseRU = SharedConstants.CollectionCreation.DefaultCollectionRUs400;
const quotaInKb = collection.quotaInfo().collectionSize;
const quotaInGb = PricingUtils.usageInGB(quotaInKb);
const perPartitionGBQuota: number = Math.max(10, quotaInGb / numPartitions);
const baseRUbyPartitions: number = ((numPartitions * perPartitionGBQuota) / 10) * 100;
return Math.max(baseRU, baseRUbyPartitions);
}; };
export const parseConflictResolutionMode = (modeFromBackend: string): DataModels.ConflictResolutionMode => { export const parseConflictResolutionMode = (modeFromBackend: string): DataModels.ConflictResolutionMode => {

View File

@ -18,7 +18,6 @@ export const collection = ({
excludedPaths: [] excludedPaths: []
}), }),
uniqueKeyPolicy: {} as DataModels.UniqueKeyPolicy, uniqueKeyPolicy: {} as DataModels.UniqueKeyPolicy,
quotaInfo: ko.observable<DataModels.CollectionQuotaInfo>({} as DataModels.CollectionQuotaInfo),
offer: ko.observable<DataModels.Offer>({ offer: ko.observable<DataModels.Offer>({
content: { content: {
offerThroughput: 10000, offerThroughput: 10000,

View File

@ -43,7 +43,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"databaseCreateNewShared": [Function], "databaseCreateNewShared": [Function],
@ -86,7 +85,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"collectionId": [Function], "collectionId": [Function],
"collectionIdTitle": [Function], "collectionIdTitle": [Function],
"collectionWithThroughputInShared": [Function], "collectionWithThroughputInShared": [Function],
@ -349,7 +347,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"createTableQuery": [Function], "createTableQuery": [Function],
@ -575,7 +572,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"collectionId": [Function], "collectionId": [Function],
"collectionIdTitle": [Function], "collectionIdTitle": [Function],
"collectionWithThroughputInShared": [Function], "collectionWithThroughputInShared": [Function],
@ -657,7 +653,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"databaseCreateNewShared": [Function], "databaseCreateNewShared": [Function],
@ -760,7 +755,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"createTableQuery": [Function], "createTableQuery": [Function],
@ -1301,7 +1295,6 @@ exports[`SettingsComponent renders 1`] = `
"version": 2, "version": 2,
}, },
"partitionKeyProperty": "partitionKey", "partitionKeyProperty": "partitionKey",
"quotaInfo": [Function],
"readSettings": [Function], "readSettings": [Function],
"uniqueKeyPolicy": Object {}, "uniqueKeyPolicy": Object {},
} }
@ -1323,7 +1316,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"databaseCreateNewShared": [Function], "databaseCreateNewShared": [Function],
@ -1366,7 +1358,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"collectionId": [Function], "collectionId": [Function],
"collectionIdTitle": [Function], "collectionIdTitle": [Function],
"collectionWithThroughputInShared": [Function], "collectionWithThroughputInShared": [Function],
@ -1629,7 +1620,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"createTableQuery": [Function], "createTableQuery": [Function],
@ -1855,7 +1845,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"collectionId": [Function], "collectionId": [Function],
"collectionIdTitle": [Function], "collectionIdTitle": [Function],
"collectionWithThroughputInShared": [Function], "collectionWithThroughputInShared": [Function],
@ -1937,7 +1926,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"databaseCreateNewShared": [Function], "databaseCreateNewShared": [Function],
@ -2040,7 +2028,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"createTableQuery": [Function], "createTableQuery": [Function],
@ -2616,7 +2603,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"databaseCreateNewShared": [Function], "databaseCreateNewShared": [Function],
@ -2659,7 +2645,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"collectionId": [Function], "collectionId": [Function],
"collectionIdTitle": [Function], "collectionIdTitle": [Function],
"collectionWithThroughputInShared": [Function], "collectionWithThroughputInShared": [Function],
@ -2922,7 +2907,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"createTableQuery": [Function], "createTableQuery": [Function],
@ -3148,7 +3132,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"collectionId": [Function], "collectionId": [Function],
"collectionIdTitle": [Function], "collectionIdTitle": [Function],
"collectionWithThroughputInShared": [Function], "collectionWithThroughputInShared": [Function],
@ -3230,7 +3213,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"databaseCreateNewShared": [Function], "databaseCreateNewShared": [Function],
@ -3333,7 +3315,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"createTableQuery": [Function], "createTableQuery": [Function],
@ -3874,7 +3855,6 @@ exports[`SettingsComponent renders 1`] = `
"version": 2, "version": 2,
}, },
"partitionKeyProperty": "partitionKey", "partitionKeyProperty": "partitionKey",
"quotaInfo": [Function],
"readSettings": [Function], "readSettings": [Function],
"uniqueKeyPolicy": Object {}, "uniqueKeyPolicy": Object {},
} }
@ -3896,7 +3876,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"databaseCreateNewShared": [Function], "databaseCreateNewShared": [Function],
@ -3939,7 +3918,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"collectionId": [Function], "collectionId": [Function],
"collectionIdTitle": [Function], "collectionIdTitle": [Function],
"collectionWithThroughputInShared": [Function], "collectionWithThroughputInShared": [Function],
@ -4202,7 +4180,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"createTableQuery": [Function], "createTableQuery": [Function],
@ -4428,7 +4405,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"collectionId": [Function], "collectionId": [Function],
"collectionIdTitle": [Function], "collectionIdTitle": [Function],
"collectionWithThroughputInShared": [Function], "collectionWithThroughputInShared": [Function],
@ -4510,7 +4486,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"databaseCreateNewShared": [Function], "databaseCreateNewShared": [Function],
@ -4613,7 +4588,6 @@ exports[`SettingsComponent renders 1`] = `
"autoPilotUsageCost": [Function], "autoPilotUsageCost": [Function],
"canConfigureThroughput": [Function], "canConfigureThroughput": [Function],
"canExceedMaximumValue": [Function], "canExceedMaximumValue": [Function],
"canRequestSupport": [Function],
"container": [Circular], "container": [Circular],
"costsVisible": [Function], "costsVisible": [Function],
"createTableQuery": [Function], "createTableQuery": [Function],

View File

@ -61,7 +61,6 @@ export default class AddCollectionPane extends ContextualPaneBase {
public maxCollectionsReachedMessage: ko.Observable<string>; public maxCollectionsReachedMessage: ko.Observable<string>;
public requestUnitsUsageCost: ko.Computed<string>; public requestUnitsUsageCost: ko.Computed<string>;
public dedicatedRequestUnitsUsageCost: ko.Computed<string>; public dedicatedRequestUnitsUsageCost: ko.Computed<string>;
public canRequestSupport: ko.PureComputed<boolean>;
public largePartitionKey: ko.Observable<boolean> = ko.observable<boolean>(false); public largePartitionKey: ko.Observable<boolean> = ko.observable<boolean>(false);
public useIndexingForSharedThroughput: ko.Observable<boolean> = ko.observable<boolean>(true); public useIndexingForSharedThroughput: ko.Observable<boolean> = ko.observable<boolean>(true);
public costsVisible: ko.PureComputed<boolean>; public costsVisible: ko.PureComputed<boolean>;
@ -314,19 +313,6 @@ export default class AddCollectionPane extends ContextualPaneBase {
} }
}); });
this.canRequestSupport = ko.pureComputed(() => {
if (
configContext.platform !== Platform.Emulator &&
!this.container.isTryCosmosDBSubscription() &&
configContext.platform !== Platform.Portal
) {
const offerThroughput: number = this._getThroughput();
return offerThroughput <= 100000;
}
return false;
});
this.costsVisible = ko.pureComputed(() => { this.costsVisible = ko.pureComputed(() => {
return configContext.platform !== Platform.Emulator; return configContext.platform !== Platform.Emulator;
}); });

View File

@ -117,10 +117,6 @@
showAutoPilot: !isFreeTierAccount() showAutoPilot: !isFreeTierAccount()
}"> }">
</throughput-input-autopilot-v3> </throughput-input-autopilot-v3>
<p data-bind="visible: canRequestSupport">
<!-- TODO: Replace link with call to the Azure Support blade --><a
href="https://aka.ms/cosmosdbfeedback?subject=Cosmos%20DB%20More%20Throughput%20Request">Contact
support</a> for more than <span data-bind="text: maxThroughputRUText"></span> RU/s.</p>
</div> </div>
<!-- /ko --> <!-- /ko -->
<!-- Database provisioned throughput - End --> <!-- Database provisioned throughput - End -->

View File

@ -31,7 +31,6 @@ export default class AddDatabasePane extends ContextualPaneBase {
public throughputSpendAck: ko.Observable<boolean>; public throughputSpendAck: ko.Observable<boolean>;
public throughputSpendAckVisible: ko.Computed<boolean>; public throughputSpendAckVisible: ko.Computed<boolean>;
public requestUnitsUsageCost: ko.Computed<string>; public requestUnitsUsageCost: ko.Computed<string>;
public canRequestSupport: ko.PureComputed<boolean>;
public costsVisible: ko.PureComputed<boolean>; public costsVisible: ko.PureComputed<boolean>;
public upsellMessage: ko.PureComputed<string>; public upsellMessage: ko.PureComputed<string>;
public upsellMessageAriaLabel: ko.PureComputed<string>; public upsellMessageAriaLabel: ko.PureComputed<string>;
@ -168,19 +167,6 @@ export default class AddDatabasePane extends ContextualPaneBase {
return estimatedSpend; return estimatedSpend;
}); });
this.canRequestSupport = ko.pureComputed(() => {
if (
configContext.platform !== Platform.Emulator &&
!this.container.isTryCosmosDBSubscription() &&
configContext.platform !== Platform.Portal
) {
const offerThroughput: number = this.throughput();
return offerThroughput <= 100000;
}
return false;
});
this.isFreeTierAccount = ko.computed<boolean>(() => { this.isFreeTierAccount = ko.computed<boolean>(() => {
const databaseAccount = this.container && this.container.databaseAccount && this.container.databaseAccount(); const databaseAccount = this.container && this.container.databaseAccount && this.container.databaseAccount();
const isFreeTierAccount = const isFreeTierAccount =

View File

@ -33,7 +33,6 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
public keyspaceThroughput: ko.Observable<number>; public keyspaceThroughput: ko.Observable<number>;
public keyspaceCreateNew: ko.Observable<boolean>; public keyspaceCreateNew: ko.Observable<boolean>;
public dedicateTableThroughput: ko.Observable<boolean>; public dedicateTableThroughput: ko.Observable<boolean>;
public canRequestSupport: ko.PureComputed<boolean>;
public throughputSpendAckText: ko.Observable<string>; public throughputSpendAckText: ko.Observable<string>;
public throughputSpendAck: ko.Observable<boolean>; public throughputSpendAck: ko.Observable<boolean>;
public sharedThroughputSpendAck: ko.Observable<boolean>; public sharedThroughputSpendAck: ko.Observable<boolean>;
@ -228,15 +227,6 @@ export default class CassandraAddCollectionPane extends ContextualPaneBase {
return configContext.platform !== Platform.Emulator; return configContext.platform !== Platform.Emulator;
}); });
this.canRequestSupport = ko.pureComputed(() => {
if (configContext.platform !== Platform.Emulator && !this.container.isTryCosmosDBSubscription()) {
const offerThroughput: number = this.throughput();
return offerThroughput <= 100000;
}
return false;
});
this.sharedThroughputSpendAckVisible = ko.computed<boolean>(() => { this.sharedThroughputSpendAckVisible = ko.computed<boolean>(() => {
const autoscaleThroughput = this.sharedAutoPilotThroughput() * 1; const autoscaleThroughput = this.sharedAutoPilotThroughput() * 1;
if (this.isSharedAutoPilotSelected()) { if (this.isSharedAutoPilotSelected()) {

View File

@ -3,11 +3,6 @@ export var Int32 = {
Max: 2147483647 Max: 2147483647
}; };
export var Int64 = {
Min: -9223372036854775808,
Max: 9223372036854775807
};
var yearMonthDay = "\\d{4}[- ][01]\\d[- ][0-3]\\d"; var yearMonthDay = "\\d{4}[- ][01]\\d[- ][0-3]\\d";
var timeOfDay = "T[0-2]\\d:[0-5]\\d(:[0-5]\\d(\\.\\d+)?)?"; var timeOfDay = "T[0-2]\\d:[0-5]\\d(:[0-5]\\d(\\.\\d+)?)?";
var timeZone = "Z|[+-][0-2]\\d:[0-5]\\d"; var timeZone = "Z|[+-][0-2]\\d:[0-5]\\d";

View File

@ -30,7 +30,6 @@
class: 'scaleForm dirty', class: 'scaleForm dirty',
value: throughput, value: throughput,
minimum: minRUs, minimum: minRUs,
maximum: maxRUThroughputInputLimit,
canExceedMaximumValue: canThroughputExceedMaximumValue, canExceedMaximumValue: canThroughputExceedMaximumValue,
step: throughputIncreaseFactor, step: throughputIncreaseFactor,
label: throughputTitle, label: throughputTitle,

View File

@ -17,7 +17,6 @@ import Explorer from "../Explorer";
import { updateOffer } from "../../Common/dataAccess/updateOffer"; import { updateOffer } from "../../Common/dataAccess/updateOffer";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import { userContext } from "../../UserContext"; import { userContext } from "../../UserContext";
import { updateOfferThroughputBeyondLimit } from "../../Common/dataAccess/updateOfferThroughputBeyondLimit";
import { configContext, Platform } from "../../ConfigContext"; import { configContext, Platform } from "../../ConfigContext";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"; import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
@ -59,9 +58,6 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
public saveSettingsButton: ViewModels.Button; public saveSettingsButton: ViewModels.Button;
public discardSettingsChangesButton: ViewModels.Button; public discardSettingsChangesButton: ViewModels.Button;
public canRequestSupport: ko.PureComputed<boolean>;
public canThroughputExceedMaximumValue: ko.Computed<boolean>;
public costsVisible: ko.Computed<boolean>; public costsVisible: ko.Computed<boolean>;
public displayedError: ko.Observable<string>; public displayedError: ko.Observable<string>;
public isTemplateReady: ko.Observable<boolean>; public isTemplateReady: ko.Observable<boolean>;
@ -69,13 +65,11 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
public minRUs: ko.Computed<number>; public minRUs: ko.Computed<number>;
public maxRUs: ko.Computed<number>; public maxRUs: ko.Computed<number>;
public maxRUsText: ko.PureComputed<string>; public maxRUsText: ko.PureComputed<string>;
public maxRUThroughputInputLimit: ko.Computed<number>;
public notificationStatusInfo: ko.Observable<string>; public notificationStatusInfo: ko.Observable<string>;
public pendingNotification: ko.Observable<DataModels.Notification>; public pendingNotification: ko.Observable<DataModels.Notification>;
public requestUnitsUsageCost: ko.PureComputed<string>; public requestUnitsUsageCost: ko.PureComputed<string>;
public autoscaleCost: ko.PureComputed<string>; public autoscaleCost: ko.PureComputed<string>;
public shouldShowNotificationStatusPrompt: ko.Computed<boolean>; public shouldShowNotificationStatusPrompt: ko.Computed<boolean>;
public shouldDisplayPortalUsePrompt: ko.Computed<boolean>;
public shouldShowStatusBar: ko.Computed<boolean>; public shouldShowStatusBar: ko.Computed<boolean>;
public throughputTitle: ko.PureComputed<string>; public throughputTitle: ko.PureComputed<string>;
public throughputAriaLabel: ko.PureComputed<string>; public throughputAriaLabel: ko.PureComputed<string>;
@ -181,22 +175,6 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
return configContext.platform !== Platform.Emulator; return configContext.platform !== Platform.Emulator;
}); });
this.shouldDisplayPortalUsePrompt = ko.pureComputed<boolean>(() => configContext.platform === Platform.Hosted);
this.canThroughputExceedMaximumValue = ko.pureComputed<boolean>(
() => configContext.platform === Platform.Portal && !this.container.isRunningOnNationalCloud()
);
this.canRequestSupport = ko.pureComputed(() => {
if (
configContext.platform === Platform.Emulator ||
configContext.platform === Platform.Hosted ||
this.canThroughputExceedMaximumValue()
) {
return false;
}
return true;
});
this.overrideWithAutoPilotSettings = ko.pureComputed(() => { this.overrideWithAutoPilotSettings = ko.pureComputed(() => {
return this._hasProvisioningTypeChanged() && this._wasAutopilotOriginallySet(); return this._hasProvisioningTypeChanged() && this._wasAutopilotOriginallySet();
}); });
@ -245,14 +223,6 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
return throughputDefaults.unlimitedmax; return throughputDefaults.unlimitedmax;
}); });
this.maxRUThroughputInputLimit = ko.pureComputed<number>(() => {
if (configContext.platform === Platform.Hosted) {
return SharedConstants.CollectionCreation.DefaultCollectionRUs1Million;
}
return this.maxRUs();
});
this.maxRUsText = ko.pureComputed(() => { this.maxRUsText = ko.pureComputed(() => {
return SharedConstants.CollectionCreation.DefaultCollectionRUs1Million.toLocaleString(); return SharedConstants.CollectionCreation.DefaultCollectionRUs1Million.toLocaleString();
}); });
@ -300,8 +270,7 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
if ( if (
this.maxRUs() <= SharedConstants.CollectionCreation.DefaultCollectionRUs1Million && this.maxRUs() <= SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
this.throughput() > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million && this.throughput() > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million
this.canThroughputExceedMaximumValue()
) { ) {
return updateThroughputBeyondLimitWarningMessage; return updateThroughputBeyondLimitWarningMessage;
} }
@ -368,13 +337,6 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
return false; return false;
} }
if (
!this.canThroughputExceedMaximumValue() &&
this.throughput() > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million
) {
return false;
}
if (this.throughput.editableIsDirty()) { if (this.throughput.editableIsDirty()) {
return true; return true;
} }
@ -450,27 +412,6 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
const originalThroughputValue = this.throughput.getEditableOriginalValue(); const originalThroughputValue = this.throughput.getEditableOriginalValue();
const newThroughput = this.throughput(); const newThroughput = this.throughput();
if (
this.canThroughputExceedMaximumValue() &&
this.maxRUs() <= SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
this.throughput() > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million
) {
const requestPayload = {
subscriptionId: userContext.subscriptionId,
databaseAccountName: userContext.databaseAccount.name,
resourceGroup: userContext.resourceGroup,
databaseName: this.database.id(),
throughput: newThroughput,
offerIsRUPerMinuteThroughputEnabled: false
};
await updateOfferThroughputBeyondLimit(requestPayload);
this.database.offer().content.offerThroughput = originalThroughputValue;
this.throughput(originalThroughputValue);
this.notificationStatusInfo(
throughputApplyDelayedMessage(this.isAutoPilotSelected(), newThroughput, this.database.id())
);
this.throughput.valueHasMutated(); // force component re-render
} else {
const updateOfferParams: DataModels.UpdateOfferParams = { const updateOfferParams: DataModels.UpdateOfferParams = {
databaseId: this.database.id(), databaseId: this.database.id(),
currentOffer: this.database.offer(), currentOffer: this.database.offer(),
@ -485,7 +426,6 @@ export default class DatabaseSettingsTab extends TabsBase implements ViewModels.
this.database.offer.valueHasMutated(); this.database.offer.valueHasMutated();
} }
} }
}
} catch (error) { } catch (error) {
this.container.isRefreshingExplorer(false); this.container.isRefreshingExplorer(false);
this.isExecutionError(true); this.isExecutionError(true);

View File

@ -57,9 +57,7 @@
class: 'scaleForm dirty', class: 'scaleForm dirty',
value: throughput, value: throughput,
minimum: minRUs, minimum: minRUs,
maximum: maxRUThroughputInputLimit,
isEnabled: !hasDatabaseSharedThroughput(), isEnabled: !hasDatabaseSharedThroughput(),
canExceedMaximumValue: canThroughputExceedMaximumValue,
label: throughputTitle, label: throughputTitle,
ariaLabel: throughputAriaLabel, ariaLabel: throughputAriaLabel,
costsVisible: costsVisible, costsVisible: costsVisible,

View File

@ -34,17 +34,6 @@ describe("Settings tab", () => {
collections: [baseCollection] collections: [baseCollection]
}; };
const quotaInfo: DataModels.CollectionQuotaInfo = {
storedProcedures: 0,
triggers: 0,
functions: 0,
documentsSize: 0,
documentsCount: 0,
collectionSize: 0,
usageSizeInKB: 0,
numPartitions: 0
};
describe("Conflict Resolution", () => { describe("Conflict Resolution", () => {
describe("should show conflict resolution", () => { describe("should show conflict resolution", () => {
let explorer: Explorer; let explorer: Explorer;
@ -70,7 +59,6 @@ describe("Settings tab", () => {
explorer, explorer,
"mydb", "mydb",
conflictResolution ? baseCollection : baseCollectionWithoutConflict, conflictResolution ? baseCollection : baseCollectionWithoutConflict,
quotaInfo,
null null
), ),
onUpdateTabsButtons: undefined onUpdateTabsButtons: undefined
@ -186,7 +174,7 @@ describe("Settings tab", () => {
tabPath: "", tabPath: "",
hashLocation: "", hashLocation: "",
isActive: ko.observable(false), isActive: ko.observable(false),
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null), collection: new Collection(explorer, "mydb", baseCollection, null),
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {} onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {}
}); });
@ -207,7 +195,7 @@ describe("Settings tab", () => {
tabPath: "", tabPath: "",
hashLocation: "", hashLocation: "",
isActive: ko.observable(false), isActive: ko.observable(false),
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null), collection: new Collection(explorer, "mydb", baseCollection, null),
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {} onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {}
}); });
@ -223,7 +211,7 @@ describe("Settings tab", () => {
tabPath: "", tabPath: "",
hashLocation: "", hashLocation: "",
isActive: ko.observable(false), isActive: ko.observable(false),
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null), collection: new Collection(explorer, "mydb", baseCollection, null),
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {} onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {}
}); });
@ -258,7 +246,7 @@ describe("Settings tab", () => {
tabPath: "", tabPath: "",
hashLocation: "", hashLocation: "",
isActive: ko.observable(false), isActive: ko.observable(false),
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null), collection: new Collection(explorer, "mydb", baseCollection, null),
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {} onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {}
}); });
@ -272,7 +260,7 @@ describe("Settings tab", () => {
tabPath: "", tabPath: "",
hashLocation: "", hashLocation: "",
isActive: ko.observable(false), isActive: ko.observable(false),
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null), collection: new Collection(explorer, "mydb", baseCollection, null),
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {} onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {}
}); });
@ -295,7 +283,7 @@ describe("Settings tab", () => {
tabPath: "", tabPath: "",
hashLocation: "", hashLocation: "",
isActive: ko.observable(false), isActive: ko.observable(false),
collection: new Collection(explorer, "mydb", baseCollection, quotaInfo, null), collection: new Collection(explorer, "mydb", baseCollection, null),
onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {} onUpdateTabsButtons: (buttons: CommandButtonComponentProps[]): void => {}
}); });
@ -354,7 +342,6 @@ describe("Settings tab", () => {
_ts: 0, _ts: 0,
id: "mycoll" id: "mycoll"
}, },
quotaInfo,
offer offer
); );
} }

View File

@ -20,7 +20,6 @@ import { updateOffer } from "../../Common/dataAccess/updateOffer";
import { updateCollection } from "../../Common/dataAccess/updateCollection"; import { updateCollection } from "../../Common/dataAccess/updateCollection";
import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent"; import { CommandButtonComponentProps } from "../Controls/CommandButton/CommandButtonComponent";
import { userContext } from "../../UserContext"; import { userContext } from "../../UserContext";
import { updateOfferThroughputBeyondLimit } from "../../Common/dataAccess/updateOfferThroughputBeyondLimit";
import { configContext, Platform } from "../../ConfigContext"; import { configContext, Platform } from "../../ConfigContext";
import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils"; import { getErrorMessage, getErrorStack } from "../../Common/ErrorHandlingUtils";
@ -151,9 +150,6 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
public saveSettingsButton: ViewModels.Button; public saveSettingsButton: ViewModels.Button;
public discardSettingsChangesButton: ViewModels.Button; public discardSettingsChangesButton: ViewModels.Button;
public canRequestSupport: ko.Computed<boolean>;
public canThroughputExceedMaximumValue: ko.Computed<boolean>;
public changeFeedPolicyOffId: string; public changeFeedPolicyOffId: string;
public changeFeedPolicyOnId: string; public changeFeedPolicyOnId: string;
public changeFeedPolicyToggled: ViewModels.Editable<ChangeFeedPolicyToggledState>; public changeFeedPolicyToggled: ViewModels.Editable<ChangeFeedPolicyToggledState>;
@ -174,8 +170,6 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
public indexingPolicyElementFocused: ko.Observable<boolean>; public indexingPolicyElementFocused: ko.Observable<boolean>;
public minRUs: ko.Computed<number>; public minRUs: ko.Computed<number>;
public minRUAnotationVisible: ko.Computed<boolean>; public minRUAnotationVisible: ko.Computed<boolean>;
public maxRUs: ko.Computed<number>;
public maxRUThroughputInputLimit: ko.Computed<number>;
public maxRUsText: ko.PureComputed<string>; public maxRUsText: ko.PureComputed<string>;
public notificationStatusInfo: ko.Observable<string>; public notificationStatusInfo: ko.Observable<string>;
public partitionKeyName: ko.Computed<string>; public partitionKeyName: ko.Computed<string>;
@ -188,7 +182,6 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
public rupmVisible: ko.Computed<boolean>; public rupmVisible: ko.Computed<boolean>;
public scaleExpanded: ko.Observable<boolean>; public scaleExpanded: ko.Observable<boolean>;
public settingsExpanded: ko.Observable<boolean>; public settingsExpanded: ko.Observable<boolean>;
public shouldDisplayPortalUsePrompt: ko.Computed<boolean>;
public shouldShowIndexingPolicyEditor: ko.Computed<boolean>; public shouldShowIndexingPolicyEditor: ko.Computed<boolean>;
public shouldShowNotificationStatusPrompt: ko.Computed<boolean>; public shouldShowNotificationStatusPrompt: ko.Computed<boolean>;
public shouldShowStatusBar: ko.Computed<boolean>; public shouldShowStatusBar: ko.Computed<boolean>;
@ -461,43 +454,6 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
return (this.container && this.container.isTryCosmosDBSubscription()) || false; return (this.container && this.container.isTryCosmosDBSubscription()) || false;
}); });
this.canThroughputExceedMaximumValue = ko.pureComputed<boolean>(() => {
return (
this._isFixedContainer() &&
configContext.platform === Platform.Portal &&
!this.container.isRunningOnNationalCloud()
);
});
this.canRequestSupport = ko.pureComputed(() => {
if (configContext.platform === Platform.Emulator) {
return false;
}
if (this.isTryCosmosDBSubscription()) {
return false;
}
if (this.canThroughputExceedMaximumValue()) {
return false;
}
if (configContext.platform === Platform.Hosted) {
return false;
}
if (this.container.isServerlessEnabled()) {
return false;
}
const numPartitions = this.collection.quotaInfo().numPartitions;
return !!this.collection.partitionKeyProperty || numPartitions > 1;
});
this.shouldDisplayPortalUsePrompt = ko.pureComputed<boolean>(
() => configContext.platform === Platform.Hosted && !!this.collection.partitionKey
);
this.minRUs = ko.computed<number>(() => { this.minRUs = ko.computed<number>(() => {
if (this.isTryCosmosDBSubscription() || this.container.isServerlessEnabled()) { if (this.isTryCosmosDBSubscription() || this.container.isServerlessEnabled()) {
return SharedConstants.CollectionCreation.DefaultCollectionRUs400; return SharedConstants.CollectionCreation.DefaultCollectionRUs400;
@ -507,7 +463,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
this.collection && this.collection.offer && this.collection.offer() && this.collection.offer().content; this.collection && this.collection.offer && this.collection.offer() && this.collection.offer().content;
if (offerContent && offerContent.offerAutopilotSettings) { if (offerContent && offerContent.offerAutopilotSettings) {
return 400; SharedConstants.CollectionCreation.DefaultCollectionRUs400;
} }
const collectionThroughputInfo: DataModels.OfferThroughputInfo = const collectionThroughputInfo: DataModels.OfferThroughputInfo =
@ -521,58 +477,14 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
return collectionThroughputInfo.minimumRUForCollection; return collectionThroughputInfo.minimumRUForCollection;
} }
const numPartitions = // minimumRUForCollection should always be present, but just in case return a default
(collectionThroughputInfo && collectionThroughputInfo.numPhysicalPartitions) ||
this.collection.quotaInfo().numPartitions;
if (!numPartitions || numPartitions === 1) {
return SharedConstants.CollectionCreation.DefaultCollectionRUs400; return SharedConstants.CollectionCreation.DefaultCollectionRUs400;
}
let baseRU = SharedConstants.CollectionCreation.DefaultCollectionRUs400;
const quotaInKb = this.collection.quotaInfo().collectionSize;
const quotaInGb = PricingUtils.usageInGB(quotaInKb);
const perPartitionGBQuota: number = Math.max(10, quotaInGb / numPartitions);
const baseRUbyPartitions: number = ((numPartitions * perPartitionGBQuota) / 10) * 100;
return Math.max(baseRU, baseRUbyPartitions);
}); });
this.minRUAnotationVisible = ko.computed<boolean>(() => { this.minRUAnotationVisible = ko.computed<boolean>(() => {
return PricingUtils.isLargerThanDefaultMinRU(this.minRUs()); return PricingUtils.isLargerThanDefaultMinRU(this.minRUs());
}); });
this.maxRUs = ko.computed<number>(() => {
const isTryCosmosDBSubscription = this.isTryCosmosDBSubscription();
if (isTryCosmosDBSubscription || this.container.isServerlessEnabled()) {
return Constants.TryCosmosExperience.maxRU;
}
const numPartitionsFromOffer: number =
this.collection &&
this.collection.offer &&
this.collection.offer() &&
this.collection.offer().content &&
this.collection.offer().content.collectionThroughputInfo &&
this.collection.offer().content.collectionThroughputInfo.numPhysicalPartitions;
const numPartitionsFromQuotaInfo: number = this.collection && this.collection.quotaInfo().numPartitions;
const numPartitions = numPartitionsFromOffer || numPartitionsFromQuotaInfo || 1;
return SharedConstants.CollectionCreation.MaxRUPerPartition * numPartitions;
});
this.maxRUThroughputInputLimit = ko.pureComputed<number>(() => {
if (configContext.platform === Platform.Hosted && this.collection.partitionKey) {
return SharedConstants.CollectionCreation.DefaultCollectionRUs1Million;
}
return this.maxRUs();
});
this.maxRUsText = ko.pureComputed(() => { this.maxRUsText = ko.pureComputed(() => {
return SharedConstants.CollectionCreation.DefaultCollectionRUs1Million.toLocaleString(); return SharedConstants.CollectionCreation.DefaultCollectionRUs1Million.toLocaleString();
}); });
@ -583,10 +495,7 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
} }
const minThroughput: string = this.minRUs().toLocaleString(); const minThroughput: string = this.minRUs().toLocaleString();
const maxThroughput: string = const maxThroughput: string = !this._isFixedContainer() ? "unlimited" : "10000";
this.canThroughputExceedMaximumValue() && !this._isFixedContainer()
? "unlimited"
: this.maxRUs().toLocaleString();
return `Throughput (${minThroughput} - ${maxThroughput} RU/s)`; return `Throughput (${minThroughput} - ${maxThroughput} RU/s)`;
}); });
@ -675,22 +584,6 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
return false; return false;
} }
const isThroughputGreaterThanMaxRus = this.throughput() > this.maxRUs();
const isEmulator = configContext.platform === Platform.Emulator;
if (isThroughputGreaterThanMaxRus && isEmulator) {
return false;
}
if (isThroughputGreaterThanMaxRus && this._isFixedContainer()) {
return false;
}
const isThroughputMoreThan1Million =
this.throughput() > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million;
if (!this.canThroughputExceedMaximumValue() && isThroughputMoreThan1Million) {
return false;
}
if (this.throughput.editableIsDirty()) { if (this.throughput.editableIsDirty()) {
return true; return true;
} }
@ -714,14 +607,6 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
return false; return false;
} }
if (
this.rupm() === Constants.RUPMStates.on &&
this.throughput() >
SharedConstants.CollectionCreation.MaxRUPMPerPartition * this.collection.quotaInfo()?.numPartitions
) {
return false;
}
if (this.timeToLive.editableIsDirty()) { if (this.timeToLive.editableIsDirty()) {
return true; return true;
} }
@ -841,14 +726,6 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
this.shouldShowNotificationStatusPrompt = ko.computed<boolean>(() => this.notificationStatusInfo().length > 0); this.shouldShowNotificationStatusPrompt = ko.computed<boolean>(() => this.notificationStatusInfo().length > 0);
this.warningMessage = ko.computed<string>(() => { this.warningMessage = ko.computed<string>(() => {
const throughputExceedsBackendLimits: boolean =
this.canThroughputExceedMaximumValue() &&
this.maxRUs() <= SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
this.throughput() > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million;
const throughputExceedsMaxValue: boolean =
configContext.platform !== Platform.Emulator && this.throughput() > this.maxRUs();
const ttlOptionDirty: boolean = this.timeToLive.editableIsDirty(); const ttlOptionDirty: boolean = this.timeToLive.editableIsDirty();
const ttlOrIndexingPolicyFieldsDirty: boolean = const ttlOrIndexingPolicyFieldsDirty: boolean =
this.timeToLive.editableIsDirty() || this.timeToLive.editableIsDirty() ||
@ -890,26 +767,6 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
return AutoPilotUtils.manualToAutoscaleDisclaimer; return AutoPilotUtils.manualToAutoscaleDisclaimer;
} }
if (
throughputExceedsBackendLimits &&
!!this.collection.partitionKey &&
!this._isFixedContainer() &&
!ttlFieldFocused &&
!this.indexingPolicyElementFocused()
) {
return updateThroughputBeyondLimitWarningMessage;
}
if (
throughputExceedsMaxValue &&
!!this.collection.partitionKey &&
!this._isFixedContainer() &&
!ttlFieldFocused &&
!this.indexingPolicyElementFocused()
) {
return updateThroughputDelayedApplyWarningMessage;
}
if (this.pendingNotification()) { if (this.pendingNotification()) {
const throughputUnit: string = this._getThroughputUnit(); const throughputUnit: string = this._getThroughputUnit();
const matches: string[] = this.pendingNotification().description.match( const matches: string[] = this.pendingNotification().description.match(
@ -1099,36 +956,6 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
} }
} }
if (
this.maxRUs() <= SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
newThroughput > SharedConstants.CollectionCreation.DefaultCollectionRUs1Million &&
this.container != null
) {
const requestPayload = {
subscriptionId: userContext.subscriptionId,
databaseAccountName: userContext.databaseAccount.name,
resourceGroup: userContext.resourceGroup,
databaseName: this.collection.databaseId,
collectionName: this.collection.id(),
throughput: newThroughput,
offerIsRUPerMinuteThroughputEnabled: isRUPerMinuteThroughputEnabled
};
await updateOfferThroughputBeyondLimit(requestPayload);
this.collection.offer().content.offerThroughput = originalThroughputValue;
this.throughput(originalThroughputValue);
this.notificationStatusInfo(
throughputApplyDelayedMessage(
this.isAutoPilotSelected(),
originalThroughputValue,
this._getThroughputUnit(),
this.collection.databaseId,
this.collection.id(),
newThroughput
)
);
this.throughput.valueHasMutated(); // force component re-render
} else {
const updateOfferParams: DataModels.UpdateOfferParams = { const updateOfferParams: DataModels.UpdateOfferParams = {
databaseId: this.collection.databaseId, databaseId: this.collection.databaseId,
collectionId: this.collection.id(), collectionId: this.collection.id(),
@ -1147,7 +974,6 @@ export default class SettingsTab extends TabsBase implements ViewModels.WaitsFor
this.collection.offer(updatedOffer); this.collection.offer(updatedOffer);
this.collection.offer.valueHasMutated(); this.collection.offer.valueHasMutated();
} }
}
this.container.isRefreshingExplorer(false); this.container.isRefreshingExplorer(false);
this._setBaseline(); this._setBaseline();

View File

@ -10,10 +10,9 @@ describe("Collection", () => {
container: Explorer, container: Explorer,
databaseId: string, databaseId: string,
data: DataModels.Collection, data: DataModels.Collection,
quotaInfo: DataModels.CollectionQuotaInfo,
offer: DataModels.Offer offer: DataModels.Offer
): Collection { ): Collection {
return new Collection(container, databaseId, data, quotaInfo, offer); return new Collection(container, databaseId, data, offer);
} }
function generateMockCollectionsDataModelWithPartitionKey( function generateMockCollectionsDataModelWithPartitionKey(
@ -50,7 +49,7 @@ describe("Collection", () => {
}); });
mockContainer.deleteCollectionText = ko.observable<string>("delete collection"); mockContainer.deleteCollectionText = ko.observable<string>("delete collection");
return generateCollection(mockContainer, "abc", data, {} as DataModels.CollectionQuotaInfo, {} as DataModels.Offer); return generateCollection(mockContainer, "abc", data, {} as DataModels.Offer);
} }
describe("Partition key path parsing", () => { describe("Partition key path parsing", () => {

View File

@ -10,7 +10,6 @@ import { readTriggers } from "../../Common/dataAccess/readTriggers";
import { readUserDefinedFunctions } from "../../Common/dataAccess/readUserDefinedFunctions"; import { readUserDefinedFunctions } from "../../Common/dataAccess/readUserDefinedFunctions";
import { createDocument } from "../../Common/DocumentClientUtilityBase"; import { createDocument } from "../../Common/DocumentClientUtilityBase";
import { readCollectionOffer } from "../../Common/dataAccess/readCollectionOffer"; import { readCollectionOffer } from "../../Common/dataAccess/readCollectionOffer";
import { readCollectionQuotaInfo } from "../../Common/dataAccess/readCollectionQuotaInfo";
import * as Logger from "../../Common/Logger"; import * as Logger from "../../Common/Logger";
import * as DataModels from "../../Contracts/DataModels"; import * as DataModels from "../../Contracts/DataModels";
import * as ViewModels from "../../Contracts/ViewModels"; import * as ViewModels from "../../Contracts/ViewModels";
@ -55,7 +54,6 @@ export default class Collection implements ViewModels.Collection {
public defaultTtl: ko.Observable<number>; public defaultTtl: ko.Observable<number>;
public indexingPolicy: ko.Observable<DataModels.IndexingPolicy>; public indexingPolicy: ko.Observable<DataModels.IndexingPolicy>;
public uniqueKeyPolicy: DataModels.UniqueKeyPolicy; public uniqueKeyPolicy: DataModels.UniqueKeyPolicy;
public quotaInfo: ko.Observable<DataModels.CollectionQuotaInfo>;
public offer: ko.Observable<DataModels.Offer>; public offer: ko.Observable<DataModels.Offer>;
public conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>; public conflictResolutionPolicy: ko.Observable<DataModels.ConflictResolutionPolicy>;
public changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>; public changeFeedPolicy: ko.Observable<DataModels.ChangeFeedPolicy>;
@ -94,13 +92,7 @@ export default class Collection implements ViewModels.Collection {
public userDefinedFunctionsFocused: ko.Observable<boolean>; public userDefinedFunctionsFocused: ko.Observable<boolean>;
public triggersFocused: ko.Observable<boolean>; public triggersFocused: ko.Observable<boolean>;
constructor( constructor(container: Explorer, databaseId: string, data: DataModels.Collection, offer: DataModels.Offer) {
container: Explorer,
databaseId: string,
data: DataModels.Collection,
quotaInfo: DataModels.CollectionQuotaInfo,
offer: DataModels.Offer
) {
this.nodeKind = "Collection"; this.nodeKind = "Collection";
this.container = container; this.container = container;
this.self = data._self; this.self = data._self;
@ -112,7 +104,6 @@ export default class Collection implements ViewModels.Collection {
this.id = ko.observable(data.id); this.id = ko.observable(data.id);
this.defaultTtl = ko.observable(data.defaultTtl); this.defaultTtl = ko.observable(data.defaultTtl);
this.indexingPolicy = ko.observable(data.indexingPolicy); this.indexingPolicy = ko.observable(data.indexingPolicy);
this.quotaInfo = ko.observable(quotaInfo);
this.offer = ko.observable(offer); this.offer = ko.observable(offer);
this.conflictResolutionPolicy = ko.observable(data.conflictResolutionPolicy); this.conflictResolutionPolicy = ko.observable(data.conflictResolutionPolicy);
this.changeFeedPolicy = ko.observable<DataModels.ChangeFeedPolicy>(data.changeFeedPolicy); this.changeFeedPolicy = ko.observable<DataModels.ChangeFeedPolicy>(data.changeFeedPolicy);
@ -669,14 +660,6 @@ export default class Collection implements ViewModels.Collection {
} }
}; };
private async loadCollectionQuotaInfo(): Promise<void> {
// TODO: Use the collection entity cache to get quota info
const quotaInfoWithUniqueKeyPolicy = await readCollectionQuotaInfo(this);
this.uniqueKeyPolicy = quotaInfoWithUniqueKeyPolicy.uniqueKeyPolicy;
const quotaInfo = _.omit(quotaInfoWithUniqueKeyPolicy, "uniqueKeyPolicy");
this.quotaInfo(quotaInfo);
}
public onNewQueryClick(source: any, event: MouseEvent, queryText?: string) { public onNewQueryClick(source: any, event: MouseEvent, queryText?: string) {
const collection: ViewModels.Collection = source.collection || source; const collection: ViewModels.Collection = source.collection || source;
const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Query).length + 1; const id = this.container.tabsManager.getTabs(ViewModels.CollectionTabKind.Query).length + 1;
@ -1349,7 +1332,6 @@ export default class Collection implements ViewModels.Collection {
try { try {
this.offer(await readCollectionOffer(params)); this.offer(await readCollectionOffer(params));
await this.loadCollectionQuotaInfo();
TelemetryProcessor.traceSuccess( TelemetryProcessor.traceSuccess(
Action.LoadOffers, Action.LoadOffers,

View File

@ -185,7 +185,7 @@ export default class Database implements ViewModels.Database {
const deltaCollections = this.getDeltaCollections(collections); const deltaCollections = this.getDeltaCollections(collections);
deltaCollections.toAdd.forEach((collection: DataModels.Collection) => { deltaCollections.toAdd.forEach((collection: DataModels.Collection) => {
const collectionVM: Collection = new Collection(this.container, this.id(), collection, null, null); const collectionVM: Collection = new Collection(this.container, this.id(), collection, null);
collectionVMs.push(collectionVM); collectionVMs.push(collectionVM);
}); });