2021-05-11 00:32:14 +05:30
import { Checkbox , DirectionalHint , Link , Stack , Text , TextField , TooltipHost } from "@fluentui/react" ;
2021-10-30 19:45:16 -07:00
import { useDatabases } from "Explorer/useDatabases" ;
import React , { FunctionComponent , useEffect , useState } from "react" ;
2021-03-18 18:06:13 -07:00
import * as Constants from "../../../Common/Constants" ;
2021-05-11 00:32:14 +05:30
import { InfoTooltip } from "../../../Common/Tooltip/InfoTooltip" ;
2021-03-18 18:06:13 -07:00
import * as SharedConstants from "../../../Shared/Constants" ;
import { userContext } from "../../../UserContext" ;
2021-04-30 10:23:34 -07:00
import { getCollectionName } from "../../../Utils/APITypeUtils" ;
2021-03-18 18:06:13 -07:00
import * as AutoPilotUtils from "../../../Utils/AutoPilotUtils" ;
import * as PricingUtils from "../../../Utils/PricingUtils" ;
2021-05-11 00:32:14 +05:30
import { CostEstimateText } from "./CostEstimateText/CostEstimateText" ;
import "./ThroughputInput.less" ;
2021-03-18 18:06:13 -07:00
export interface ThroughputInputProps {
isDatabase : boolean ;
2021-04-30 10:23:34 -07:00
isSharded : boolean ;
2021-03-18 18:06:13 -07:00
showFreeTierExceedThroughputTooltip : boolean ;
setThroughputValue : ( throughput : number ) = > void ;
setIsAutoscale : ( isAutoscale : boolean ) = > void ;
2021-10-30 19:45:16 -07:00
setIsThroughputCapExceeded : ( isThroughputCapExceeded : boolean ) = > void ;
2021-03-18 18:06:13 -07:00
onCostAcknowledgeChange : ( isAcknowledged : boolean ) = > void ;
}
2021-05-11 00:32:14 +05:30
export const ThroughputInput : FunctionComponent < ThroughputInputProps > = ( {
isDatabase ,
showFreeTierExceedThroughputTooltip ,
setThroughputValue ,
setIsAutoscale ,
2021-10-30 19:45:16 -07:00
setIsThroughputCapExceeded ,
2021-05-11 00:32:14 +05:30
isSharded ,
onCostAcknowledgeChange ,
} : ThroughputInputProps ) = > {
2021-05-12 11:56:24 -07:00
const [ isAutoscaleSelected , setIsAutoScaleSelected ] = useState < boolean > ( true ) ;
const [ throughput , setThroughput ] = useState < number > ( AutoPilotUtils . minAutoPilotThroughput ) ;
2021-05-11 00:32:14 +05:30
const [ isCostAcknowledged , setIsCostAcknowledged ] = useState < boolean > ( false ) ;
const [ throughputError , setThroughputError ] = useState < string > ( "" ) ;
2021-10-30 19:45:16 -07:00
const [ totalThroughputUsed , setTotalThroughputUsed ] = useState < number > ( 0 ) ;
2021-05-12 11:56:24 -07:00
setIsAutoscale ( isAutoscaleSelected ) ;
setThroughputValue ( throughput ) ;
2021-10-30 19:45:16 -07:00
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 ;
} ;
2021-05-11 00:32:14 +05:30
const getThroughputLabelText = ( ) : string = > {
2021-04-30 10:23:34 -07:00
let throughputHeaderText : string ;
2021-05-11 00:32:14 +05:30
if ( isAutoscaleSelected ) {
2021-04-30 10:23:34 -07:00
throughputHeaderText = AutoPilotUtils . getAutoPilotHeaderText ( ) . toLocaleLowerCase ( ) ;
} else {
const minRU : string = SharedConstants . CollectionCreation . DefaultCollectionRUs400 . toLocaleString ( ) ;
2021-05-28 18:11:31 -07:00
let maxRU : string ;
if ( userContext . isTryCosmosDBSubscription ) {
maxRU = Constants . TryCosmosExperience . maxRU . toLocaleString ( ) ;
} else if ( ! isSharded ) {
maxRU = "10000" ;
} else {
maxRU = "unlimited" ;
}
2021-04-30 10:23:34 -07:00
throughputHeaderText = ` throughput ( ${ minRU } - ${ maxRU } RU/s) ` ;
2021-03-18 18:06:13 -07:00
}
2021-05-11 00:32:14 +05:30
return ` ${ isDatabase ? "Database" : getCollectionName ( ) } ${ throughputHeaderText } ` ;
} ;
2021-03-18 18:06:13 -07:00
2021-05-11 00:32:14 +05:30
const onThroughputValueChange = ( newInput : string ) : void = > {
2021-03-18 18:06:13 -07:00
const newThroughput = parseInt ( newInput ) ;
2021-05-12 11:56:24 -07:00
setThroughput ( newThroughput ) ;
2021-05-11 00:32:14 +05:30
setThroughputValue ( newThroughput ) ;
2021-10-30 19:45:16 -07:00
2021-05-11 00:32:14 +05:30
if ( ! isSharded && newThroughput > 10000 ) {
setThroughputError ( "Unsharded collections support up to 10,000 RUs" ) ;
2021-10-30 19:45:16 -07:00
return ;
}
if ( ! checkThroughputCap ( newThroughput ) ) {
return ;
2021-04-30 10:23:34 -07:00
}
2021-10-30 19:45:16 -07:00
setThroughputError ( "" ) ;
2021-05-11 00:32:14 +05:30
} ;
2021-03-18 18:06:13 -07:00
2021-05-11 00:32:14 +05:30
const getAutoScaleTooltip = ( ) : string = > {
2021-04-30 10:23:34 -07:00
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. ` ;
2021-05-11 00:32:14 +05:30
} ;
2021-03-18 18:06:13 -07:00
2021-05-11 00:32:14 +05:30
const getCostAcknowledgeText = ( ) : string = > {
const databaseAccount = userContext . databaseAccount ;
2021-03-18 18:06:13 -07:00
if ( ! databaseAccount || ! databaseAccount . properties ) {
return "" ;
}
const numberOfRegions : number = databaseAccount . properties . readLocations ? . length || 1 ;
const multimasterEnabled : boolean = databaseAccount . properties . enableMultipleWriteLocations ;
return PricingUtils . getEstimatedSpendAcknowledgeString (
2021-05-11 00:32:14 +05:30
throughput ,
2021-03-18 18:06:13 -07:00
userContext . portalEnv ,
numberOfRegions ,
multimasterEnabled ,
2021-05-11 00:32:14 +05:30
isAutoscaleSelected
2021-03-18 18:06:13 -07:00
) ;
2021-05-11 00:32:14 +05:30
} ;
2021-03-18 18:06:13 -07:00
2021-05-11 00:32:14 +05:30
const handleOnChangeMode = ( event : React.ChangeEvent < HTMLInputElement > , mode : string ) : void = > {
if ( mode === "Autoscale" ) {
2021-05-12 11:56:24 -07:00
setThroughput ( AutoPilotUtils . minAutoPilotThroughput ) ;
setIsAutoScaleSelected ( true ) ;
2021-05-11 00:32:14 +05:30
setThroughputValue ( AutoPilotUtils . minAutoPilotThroughput ) ;
setIsAutoscale ( true ) ;
2021-10-30 19:45:16 -07:00
checkThroughputCap ( AutoPilotUtils . minAutoPilotThroughput ) ;
2021-05-11 00:32:14 +05:30
} else {
2021-05-12 11:56:24 -07:00
setThroughput ( SharedConstants . CollectionCreation . DefaultCollectionRUs400 ) ;
setIsAutoScaleSelected ( false ) ;
2021-05-11 00:32:14 +05:30
setThroughputValue ( SharedConstants . CollectionCreation . DefaultCollectionRUs400 ) ;
setIsAutoscale ( false ) ;
2021-10-30 19:45:16 -07:00
checkThroughputCap ( SharedConstants . CollectionCreation . DefaultCollectionRUs400 ) ;
2021-03-18 18:06:13 -07:00
}
2021-05-11 00:32:14 +05:30
} ;
2021-03-18 18:06:13 -07:00
2021-05-11 00:32:14 +05:30
return (
< div className = "throughputInputContainer throughputInputSpacing" >
< Stack horizontal >
< span className = "mandatoryStar" > * & nbsp ; < / span >
2021-05-12 17:12:03 -07:00
< Text aria-label = "Throughput header" variant = "small" style = { { lineHeight : "20px" , fontWeight : 600 } } >
2021-05-11 00:32:14 +05:30
{ getThroughputLabelText ( ) }
< / Text >
< InfoTooltip > { PricingUtils . getRuToolTipText ( ) } < / InfoTooltip >
< / Stack >
< Stack horizontal verticalAlign = "center" >
< input
className = "throughputInputRadioBtn"
aria - label = "Autoscale mode"
2021-09-17 02:55:28 +05:30
aria - required = { true }
2021-05-11 00:32:14 +05:30
checked = { isAutoscaleSelected }
type = "radio"
role = "radio"
tabIndex = { 0 }
onChange = { ( e ) = > handleOnChangeMode ( e , "Autoscale" ) }
/ >
< span className = "throughputInputRadioBtnLabel" > Autoscale < / span >
< input
className = "throughputInputRadioBtn"
aria - label = "Manual mode"
checked = { ! isAutoscaleSelected }
type = "radio"
2021-09-17 02:55:28 +05:30
aria - required = { true }
2021-05-11 00:32:14 +05:30
role = "radio"
tabIndex = { 0 }
onChange = { ( e ) = > handleOnChangeMode ( e , "Manual" ) }
/ >
< span className = "throughputInputRadioBtnLabel" > Manual < / span >
< / Stack >
{ isAutoscaleSelected && (
< Stack className = "throughputInputSpacing" >
< Text variant = "small" aria-label = "ruDescription" >
Estimate your required RU / s with { " " }
< Link target = "_blank" href = "https://cosmos.azure.com/capacitycalculator/" aria-label = "ruDescription" >
capacity calculator
< / Link >
.
< / Text >
2021-03-18 18:06:13 -07:00
2021-05-11 00:32:14 +05:30
< Stack horizontal >
< Text variant = "small" style = { { lineHeight : "20px" , fontWeight : 600 } } aria-label = "maxRUDescription" >
{ isDatabase ? "Database" : getCollectionName ( ) } Max RU / s
< / Text >
< InfoTooltip > { getAutoScaleTooltip ( ) } < / InfoTooltip >
< / Stack >
2021-03-18 18:06:13 -07:00
2021-05-11 00:32:14 +05:30
< TextField
type = "number"
styles = { {
fieldGroup : { width : 300 , height : 27 } ,
field : { fontSize : 12 } ,
} }
onChange = { ( event , newInput? : string ) = > onThroughputValueChange ( newInput ) }
step = { AutoPilotUtils . autoPilotIncrementStep }
min = { AutoPilotUtils . minAutoPilotThroughput }
value = { throughput . toString ( ) }
aria - label = "Max request units per second"
required = { true }
errorMessage = { throughputError }
/ >
2021-04-30 10:23:34 -07:00
2021-05-11 00:32:14 +05:30
< Text variant = "small" >
Your { isDatabase ? "database" : getCollectionName ( ) . toLocaleLowerCase ( ) } throughput will automatically scale
from { " " }
< b >
{ AutoPilotUtils . getMinRUsBasedOnUserInput ( throughput ) } RU / s ( 10 % of max RU / s ) - { throughput } RU / s
< / b > { " " }
based on usage .
< / Text >
< / Stack >
) }
{ ! isAutoscaleSelected && (
< Stack className = "throughputInputSpacing" >
< Text variant = "small" aria-label = "ruDescription" >
Estimate your required RU / s with & nbsp ;
< Link target = "_blank" href = "https://cosmos.azure.com/capacitycalculator/" aria-label = "capacityLink" >
capacity calculator
< / Link >
.
< / Text >
2021-03-18 18:06:13 -07:00
2021-05-11 00:32:14 +05:30
< TooltipHost
directionalHint = { DirectionalHint . topLeftEdge }
content = {
2021-05-24 13:42:54 -07:00
showFreeTierExceedThroughputTooltip && throughput > 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. `
2021-05-11 00:32:14 +05:30
: undefined
}
>
< TextField
type = "number"
styles = { {
fieldGroup : { width : 300 , height : 27 } ,
field : { fontSize : 12 } ,
} }
onChange = { ( event , newInput? : string ) = > 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 }
/ >
< / TooltipHost >
< / Stack >
) }
< CostEstimateText requestUnits = { throughput } isAutoscale = { isAutoscaleSelected } / >
{ throughput > SharedConstants . CollectionCreation . DefaultCollectionRUs100K && (
< Stack horizontal verticalAlign = "start" >
< Checkbox
checked = { isCostAcknowledged }
styles = { {
checkbox : { width : 12 , height : 12 } ,
label : { padding : 0 , margin : "4px 4px 0 0" } ,
} }
onChange = { ( ev : React.FormEvent < HTMLElement > , isChecked : boolean ) = > {
setIsCostAcknowledged ( isChecked ) ;
onCostAcknowledgeChange ( isChecked ) ;
} }
/ >
< Text variant = "small" style = { { lineHeight : "20px" } } >
{ getCostAcknowledgeText ( ) }
< / Text >
< / Stack >
) }
< / div >
2021-03-18 18:06:13 -07:00
) ;
} ;