Fix throughputcap check (#1156)

This commit is contained in:
victor-meng
2021-11-05 10:23:21 -07:00
committed by GitHub
parent 5597a1e8b6
commit a3d88af175
9 changed files with 72 additions and 26 deletions

View File

@@ -27,6 +27,7 @@ export interface DatabaseAccountExtendedProperties {
ipRules?: IpRule[];
privateEndpointConnections?: unknown[];
capacity?: { totalThroughputLimit: number };
locations?: DatabaseAccountResponseLocation[];
}
export interface DatabaseAccountResponseLocation {

View File

@@ -213,20 +213,9 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
},
};
this.totalThroughputUsed = 0;
(useDatabases.getState().databases || []).forEach((database) => {
if (database.offer()) {
const dbThroughput = database.offer().autoscaleMaxThroughput || database.offer().manualThroughput;
this.totalThroughputUsed += dbThroughput;
if (userContext.databaseAccount?.properties.capacity?.totalThroughputLimit) {
this.calculateTotalThroughputUsed();
}
(database.collections() || []).forEach((collection) => {
if (collection.offer()) {
const colThroughput = collection.offer().autoscaleMaxThroughput || collection.offer().manualThroughput;
this.totalThroughputUsed += colThroughput;
}
});
});
}
componentDidMount(): void {
@@ -504,6 +493,26 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
private onMongoIndexingPolicyDiscardableChange = (isMongoIndexingPolicyDiscardable: boolean): void =>
this.setState({ isMongoIndexingPolicyDiscardable });
private calculateTotalThroughputUsed = (): void => {
this.totalThroughputUsed = 0;
(useDatabases.getState().databases || []).forEach(async (database) => {
if (database.offer()) {
const dbThroughput = database.offer().autoscaleMaxThroughput || database.offer().manualThroughput;
this.totalThroughputUsed += dbThroughput;
}
(database.collections() || []).forEach(async (collection) => {
if (collection.offer()) {
const colThroughput = collection.offer().autoscaleMaxThroughput || collection.offer().manualThroughput;
this.totalThroughputUsed += colThroughput;
}
});
});
const numberOfRegions = userContext.databaseAccount?.properties.locations?.length || 1;
this.totalThroughputUsed *= numberOfRegions;
};
public getAnalyticalStorageTtl = (): number => {
if (this.isAnalyticalStorageEnabled) {
if (this.state.analyticalStorageTtlSelection === TtlType.On) {
@@ -669,9 +678,11 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
private onMaxAutoPilotThroughputChange = (newThroughput: number): void => {
let throughputError = "";
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
if (throughputCap && throughputCap - this.totalThroughputUsed < newThroughput - this.offer.autoscaleMaxThroughput) {
const numberOfRegions = userContext.databaseAccount?.properties.locations?.length || 1;
const throughputDelta = (newThroughput - this.offer.autoscaleMaxThroughput) * numberOfRegions;
if (throughputCap && throughputCap - this.totalThroughputUsed < throughputDelta) {
throughputError = `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 ${
this.totalThroughputUsed + newThroughput
this.totalThroughputUsed + throughputDelta
} RU/s. Change total throughput limit in cost management.`;
}
this.setState({ autoPilotThroughput: newThroughput, throughputError });
@@ -680,9 +691,11 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
private onThroughputChange = (newThroughput: number): void => {
let throughputError = "";
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
const numberOfRegions = userContext.databaseAccount?.properties.locations?.length || 1;
const throughputDelta = (newThroughput - this.offer.manualThroughput) * numberOfRegions;
if (throughputCap && throughputCap - this.totalThroughputUsed < newThroughput - this.offer.manualThroughput) {
throughputError = `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 ${
this.totalThroughputUsed + newThroughput
this.totalThroughputUsed + throughputDelta
} RU/s. Change total throughput limit in cost management.`;
}
this.setState({ throughput: newThroughput, throughputError });

View File

@@ -40,6 +40,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
setThroughputValue(throughput);
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
const numberOfRegions = userContext.databaseAccount?.properties.locations?.length || 1;
useEffect(() => {
// throughput cap check for the initial state
@@ -57,12 +58,13 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
}
});
});
totalThroughput *= numberOfRegions;
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
totalThroughput + throughput * numberOfRegions
} RU/s. Change total throughput limit in cost management.`
);
@@ -74,7 +76,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
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
totalThroughputUsed + newThroughput * numberOfRegions
} RU/s. Change total throughput limit in cost management.`
);
setIsThroughputCapExceeded(true);

View File

@@ -1176,7 +1176,9 @@ export default class Explorer {
<CassandraAddCollectionPane explorer={this} cassandraApiClient={new CassandraAPIDataClient()} />
);
} else {
await useDatabases.getState().loadDatabaseOffers();
userContext.databaseAccount?.properties.capacity?.totalThroughputLimit
? await useDatabases.getState().loadAllOffers()
: await useDatabases.getState().loadDatabaseOffers();
useSidePanel
.getState()
.openSidePanel("New " + getCollectionName(), <AddCollectionPanel explorer={this} databaseId={databaseId} />);

View File

@@ -308,8 +308,12 @@ function createNewDatabase(container: Explorer): CommandButtonComponentProps {
return {
iconSrc: AddDatabaseIcon,
iconAlt: label,
onCommandClick: () =>
useSidePanel.getState().openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={container} />),
onCommandClick: async () => {
if (userContext.databaseAccount?.properties.capacity?.totalThroughputLimit) {
await useDatabases.getState().loadAllOffers();
}
useSidePanel.getState().openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={container} />);
},
commandButtonLabel: label,
ariaLabel: label,
hasPopup: true,

View File

@@ -307,10 +307,14 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
iconSrc: AddDatabaseIcon,
title: "New " + getDatabaseName(),
description: undefined,
onClick: () =>
onClick: async () => {
if (userContext.databaseAccount?.properties.capacity?.totalThroughputLimit) {
await useDatabases.getState().loadAllOffers();
}
useSidePanel
.getState()
.openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={this.container} />),
.openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={this.container} />);
},
});
}

View File

@@ -576,7 +576,9 @@ export default class Collection implements ViewModels.Collection {
public onSettingsClick = async (): Promise<void> => {
useSelectedNode.getState().setSelectedNode(this);
await this.loadOffer();
userContext.databaseAccount?.properties.capacity?.totalThroughputLimit
? await useDatabases.getState().loadAllOffers()
: await this.loadOffer();
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Settings);
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
description: "Settings node",

View File

@@ -57,7 +57,7 @@ export default class Database implements ViewModels.Database {
this.isOfferRead = false;
}
public onSettingsClick = (): void => {
public onSettingsClick = async (): Promise<void> => {
useSelectedNode.getState().setSelectedNode(this);
this.selectedSubnodeKind(ViewModels.CollectionTabKind.DatabaseSettings);
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
@@ -66,6 +66,10 @@ export default class Database implements ViewModels.Database {
dataExplorerArea: Constants.Areas.ResourceTree,
});
if (userContext.databaseAccount?.properties.capacity?.totalThroughputLimit) {
await useDatabases.getState().loadAllOffers();
}
const pendingNotificationsPromise: Promise<DataModels.Notification> = this.getPendingThroughputSplitNotification();
const tabKind = ViewModels.CollectionTabKind.DatabaseSettingsV2;
const matchingTabs = useTabs.getState().getTabs(tabKind, (tab) => tab.node?.id() === this.id());

View File

@@ -18,6 +18,7 @@ interface DatabasesState {
findCollection: (databaseId: string, collectionId: string) => ViewModels.Collection;
isLastCollection: () => boolean;
loadDatabaseOffers: () => Promise<void>;
loadAllOffers: () => Promise<void>;
isFirstResourceCreated: () => boolean;
findSelectedDatabase: () => ViewModels.Database;
validateDatabaseId: (id: string) => boolean;
@@ -97,6 +98,19 @@ export const useDatabases: UseStore<DatabasesState> = create((set, get) => ({
})
);
},
loadAllOffers: async () => {
await Promise.all(
get().databases?.map(async (database: ViewModels.Database) => {
await database.loadOffer();
await database.loadCollections();
await Promise.all(
(database.collections() || []).map(async (collection: ViewModels.Collection) => {
await collection.loadOffer();
})
);
})
);
},
isFirstResourceCreated: () => {
const databases = get().databases;