import * as React from "react"; import * as Constants from "../../../../Common/Constants"; import { ThroughputInputAutoPilotV3Component } from "./ThroughputInputComponents/ThroughputInputAutoPilotV3Component"; import * as ViewModels from "../../../../Contracts/ViewModels"; import * as DataModels from "../../../../Contracts/DataModels"; import * as SharedConstants from "../../../../Shared/Constants"; import Explorer from "../../../Explorer"; import { PlatformType } from "../../../../PlatformType"; import { getTextFieldStyles, subComponentStackProps, titleAndInputStackProps, throughputUnit, getThroughputApplyLongDelayMessage, getThroughputApplyShortDelayMessage, updateThroughputBeyondLimitWarningMessage, updateThroughputDelayedApplyWarningMessage } from "../SettingsRenderUtils"; import { getMaxRUs, getMinRUs, hasDatabaseSharedThroughput } from "../SettingsUtils"; import * as AutoPilotUtils from "../../../../Utils/AutoPilotUtils"; import { Text, TextField, Stack, Label, MessageBar, MessageBarType } from "office-ui-fabric-react"; export interface ScaleComponentProps { collection: ViewModels.Collection; container: Explorer; isFixedContainer: boolean; autoPilotTiersList: ViewModels.DropdownOption[]; onThroughputChange: (newThroughput: number) => void; throughput: number; throughputBaseline: number; autoPilotThroughput: number; autoPilotThroughputBaseline: number; isAutoPilotSelected: boolean; wasAutopilotOriginallySet: boolean; onAutoPilotSelected: (isAutoPilotSelected: boolean) => void; onMaxAutoPilotThroughputChange: (newThroughput: number) => void; onScaleSaveableChange: (isScaleSaveable: boolean) => void; onScaleDiscardableChange: (isScaleDiscardable: boolean) => void; initialNotification: DataModels.Notification; } export class ScaleComponent extends React.Component { private isEmulator: boolean; constructor(props: ScaleComponentProps) { super(props); this.isEmulator = this.props.container.isEmulator; } public isAutoScaleEnabled = (): boolean => { const accountCapabilities: DataModels.Capability[] = this.props.container?.databaseAccount()?.properties ?.capabilities; const enableAutoScaleCapability = accountCapabilities && accountCapabilities.find((capability: DataModels.Capability) => { return ( capability && capability.name && capability.name.toLowerCase() === Constants.CapabilityNames.EnableAutoScale.toLowerCase() ); }); return !!enableAutoScaleCapability; }; private getStorageCapacityTitle = (): JSX.Element => { // Mongo container with system partition key still treat as "Fixed" const isFixed = !this.props.collection.partitionKey || (this.props.container.isPreferredApiMongoDB() && this.props.collection.partitionKey.systemKey); const capacity: string = isFixed ? "Fixed" : "Unlimited"; return ( {capacity} ); }; public getMaxRUThroughputInputLimit = (): number => { if (this.props.container?.getPlatformType() === PlatformType.Hosted && this.props.collection.partitionKey) { return SharedConstants.CollectionCreation.DefaultCollectionRUs1Million; } return getMaxRUs(this.props.collection, this.props.container); }; public getThroughputTitle = (): string => { if (this.props.isAutoPilotSelected) { return AutoPilotUtils.getAutoPilotHeaderText(false); } const minThroughput: string = getMinRUs(this.props.collection, this.props.container).toLocaleString(); const maxThroughput: string = this.canThroughputExceedMaximumValue() && !this.props.isFixedContainer ? "unlimited" : getMaxRUs(this.props.collection, this.props.container).toLocaleString(); return `Throughput (${minThroughput} - ${maxThroughput} RU/s)`; }; public canThroughputExceedMaximumValue = (): boolean => { const isPublicAzurePortal: boolean = this.props.container.getPlatformType() === PlatformType.Portal && !this.props.container.isRunningOnNationalCloud(); const hasPartitionKey = !!this.props.collection.partitionKey; return isPublicAzurePortal && hasPartitionKey; }; public getInitialNotificationElement = (): JSX.Element => { if (this.props.initialNotification) { return this.getLongDelayMessage(); } const offer = this.props.collection?.offer && this.props.collection.offer(); if ( offer && Object.keys(offer).find(value => { return value === "headers"; }) && !!(offer as DataModels.OfferWithHeaders).headers[Constants.HttpHeaders.offerReplacePending] ) { const throughput = offer?.content?.offerAutopilotSettings?.maxThroughput; const targetThroughput = offer.content?.offerAutopilotSettings?.targetMaxThroughput || offer?.content?.offerThroughput; return getThroughputApplyShortDelayMessage( this.props.isAutoPilotSelected, throughput, throughputUnit, this.props.collection.databaseId, this.props.collection.id(), targetThroughput ); } 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 => ( ); public render(): JSX.Element { return ( {this.getInitialNotificationElement() && ( {this.getInitialNotificationElement()} )} {!this.isAutoScaleEnabled() && ( {this.getThroughputInputComponent()} {this.getStorageCapacityTitle()} )} {/* TODO: Replace link with call to the Azure Support blade */} {this.isAutoScaleEnabled() && ( Throughput (RU/s) 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. )} ); } }