import { Checkbox, DirectionalHint, Link, Stack, Text, TextField, TooltipHost } from "@fluentui/react"; import { useDatabases } from "Explorer/useDatabases"; import React, { FunctionComponent, useEffect, useState } from "react"; import * as Constants from "../../../Common/Constants"; import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip"; import * as SharedConstants from "../../../Shared/Constants"; import { userContext } from "../../../UserContext"; import { getCollectionName } from "../../../Utils/APITypeUtils"; import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils"; import * as PricingUtils from "../../../Utils/PricingUtils"; import { CostEstimateText } from "./CostEstimateText/CostEstimateText"; import "./ThroughputInput.less"; export interface ThroughputInputProps { isDatabase: boolean; isSharded: boolean; showFreeTierExceedThroughputTooltip: boolean; setThroughputValue: (throughput: number) => void; setIsAutoscale: (isAutoscale: boolean) => void; setIsThroughputCapExceeded: (isThroughputCapExceeded: boolean) => void; onCostAcknowledgeChange: (isAcknowledged: boolean) => void; } export const ThroughputInput: FunctionComponent = ({ isDatabase, showFreeTierExceedThroughputTooltip, setThroughputValue, setIsAutoscale, setIsThroughputCapExceeded, isSharded, onCostAcknowledgeChange, }: ThroughputInputProps) => { const [isAutoscaleSelected, setIsAutoScaleSelected] = useState(true); const [throughput, setThroughput] = useState(AutoPilotUtils.minAutoPilotThroughput); const [isCostAcknowledged, setIsCostAcknowledged] = useState(false); const [throughputError, setThroughputError] = useState(""); const [totalThroughputUsed, setTotalThroughputUsed] = useState(0); setIsAutoscale(isAutoscaleSelected); setThroughputValue(throughput); const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit; useEffect(() => { // throughput cap check for the initial state let totalThroughput = 0; (useDatabases.getState().databases || []).forEach((database) => { if (database.offer()) { const dbThroughput = database.offer().autoscaleMaxThroughput || database.offer().manualThroughput; totalThroughput += dbThroughput; } (database.collections() || []).forEach((collection) => { if (collection.offer()) { const colThroughput = collection.offer().autoscaleMaxThroughput || collection.offer().manualThroughput; totalThroughput += colThroughput; } }); }); setTotalThroughputUsed(totalThroughput); if (throughputCap && throughputCap - totalThroughput < throughput) { setThroughputError( `Your account is currently configured with a total throughput limit of ${throughputCap} RU/s. This update isn't possible because it would increase the total throughput to ${ totalThroughputUsed + throughput } RU/s. Change total throughput limit in cost management.` ); setIsThroughputCapExceeded(true); } }, []); const checkThroughputCap = (newThroughput: number): boolean => { if (throughputCap && throughputCap - totalThroughputUsed < newThroughput) { setThroughputError( `Your account is currently configured with a total throughput limit of ${throughputCap} RU/s. This update isn't possible because it would increase the total throughput to ${ totalThroughputUsed + newThroughput } RU/s. Change total throughput limit in cost management.` ); setIsThroughputCapExceeded(true); return false; } setThroughputError(""); setIsThroughputCapExceeded(false); return true; }; const getThroughputLabelText = (): string => { let throughputHeaderText: string; if (isAutoscaleSelected) { throughputHeaderText = AutoPilotUtils.getAutoPilotHeaderText().toLocaleLowerCase(); } else { const minRU: string = SharedConstants.CollectionCreation.DefaultCollectionRUs400.toLocaleString(); let maxRU: string; if (userContext.isTryCosmosDBSubscription) { maxRU = Constants.TryCosmosExperience.maxRU.toLocaleString(); } else if (!isSharded) { maxRU = "10000"; } else { maxRU = "unlimited"; } throughputHeaderText = `throughput (${minRU} - ${maxRU} RU/s)`; } return `${isDatabase ? "Database" : getCollectionName()} ${throughputHeaderText}`; }; const onThroughputValueChange = (newInput: string): void => { const newThroughput = parseInt(newInput); setThroughput(newThroughput); setThroughputValue(newThroughput); if (!isSharded && newThroughput > 10000) { setThroughputError("Unsharded collections support up to 10,000 RUs"); return; } if (!checkThroughputCap(newThroughput)) { return; } setThroughputError(""); }; const getAutoScaleTooltip = (): string => { const collectionName = getCollectionName().toLocaleLowerCase(); return `Set the max RU/s to the highest RU/s you want your ${collectionName} to scale to. The ${collectionName} will scale between 10% of max RU/s to the max RU/s based on usage.`; }; const getCostAcknowledgeText = (): string => { const databaseAccount = userContext.databaseAccount; if (!databaseAccount || !databaseAccount.properties) { return ""; } const numberOfRegions: number = databaseAccount.properties.readLocations?.length || 1; const multimasterEnabled: boolean = databaseAccount.properties.enableMultipleWriteLocations; return PricingUtils.getEstimatedSpendAcknowledgeString( throughput, userContext.portalEnv, numberOfRegions, multimasterEnabled, isAutoscaleSelected ); }; const handleOnChangeMode = (event: React.ChangeEvent, mode: string): void => { if (mode === "Autoscale") { setThroughput(AutoPilotUtils.minAutoPilotThroughput); setIsAutoScaleSelected(true); setThroughputValue(AutoPilotUtils.minAutoPilotThroughput); setIsAutoscale(true); checkThroughputCap(AutoPilotUtils.minAutoPilotThroughput); } else { setThroughput(SharedConstants.CollectionCreation.DefaultCollectionRUs400); setIsAutoScaleSelected(false); setThroughputValue(SharedConstants.CollectionCreation.DefaultCollectionRUs400); setIsAutoscale(false); checkThroughputCap(SharedConstants.CollectionCreation.DefaultCollectionRUs400); } }; return (
{getThroughputLabelText()} {PricingUtils.getRuToolTipText()} handleOnChangeMode(e, "Autoscale")} /> Autoscale handleOnChangeMode(e, "Manual")} /> Manual {isAutoscaleSelected && ( Estimate your required RU/s with{" "} capacity calculator . {isDatabase ? "Database" : getCollectionName()} Max RU/s {getAutoScaleTooltip()} onThroughputValueChange(newInput)} step={AutoPilotUtils.autoPilotIncrementStep} min={AutoPilotUtils.minAutoPilotThroughput} value={throughput.toString()} aria-label="Max request units per second" required={true} errorMessage={throughputError} /> Your {isDatabase ? "database" : getCollectionName().toLocaleLowerCase()} throughput will automatically scale from{" "} {AutoPilotUtils.getMinRUsBasedOnUserInput(throughput)} RU/s (10% of max RU/s) - {throughput} RU/s {" "} based on usage. )} {!isAutoscaleSelected && ( Estimate your required RU/s with  capacity calculator . SharedConstants.FreeTierLimits.RU ? `The first ${SharedConstants.FreeTierLimits.RU} RU/s in this account are free. Billing will apply to any throughput beyond ${SharedConstants.FreeTierLimits.RU} RU/s.` : undefined } > onThroughputValueChange(newInput)} step={100} min={SharedConstants.CollectionCreation.DefaultCollectionRUs400} max={userContext.isTryCosmosDBSubscription ? Constants.TryCosmosExperience.maxRU : Infinity} value={throughput.toString()} aria-label="Max request units per second" required={true} errorMessage={throughputError} /> )} {throughput > SharedConstants.CollectionCreation.DefaultCollectionRUs100K && ( , isChecked: boolean) => { setIsCostAcknowledged(isChecked); onCostAcknowledgeChange(isChecked); }} /> {getCostAcknowledgeText()} )}
); };