mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-19 17:01:13 +00:00
Fix throughputcap check (#1156)
This commit is contained in:
@@ -27,6 +27,7 @@ export interface DatabaseAccountExtendedProperties {
|
|||||||
ipRules?: IpRule[];
|
ipRules?: IpRule[];
|
||||||
privateEndpointConnections?: unknown[];
|
privateEndpointConnections?: unknown[];
|
||||||
capacity?: { totalThroughputLimit: number };
|
capacity?: { totalThroughputLimit: number };
|
||||||
|
locations?: DatabaseAccountResponseLocation[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DatabaseAccountResponseLocation {
|
export interface DatabaseAccountResponseLocation {
|
||||||
|
|||||||
@@ -213,20 +213,9 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
this.totalThroughputUsed = 0;
|
if (userContext.databaseAccount?.properties.capacity?.totalThroughputLimit) {
|
||||||
(useDatabases.getState().databases || []).forEach((database) => {
|
this.calculateTotalThroughputUsed();
|
||||||
if (database.offer()) {
|
|
||||||
const dbThroughput = database.offer().autoscaleMaxThroughput || database.offer().manualThroughput;
|
|
||||||
this.totalThroughputUsed += dbThroughput;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(database.collections() || []).forEach((collection) => {
|
|
||||||
if (collection.offer()) {
|
|
||||||
const colThroughput = collection.offer().autoscaleMaxThroughput || collection.offer().manualThroughput;
|
|
||||||
this.totalThroughputUsed += colThroughput;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
@@ -504,6 +493,26 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
private onMongoIndexingPolicyDiscardableChange = (isMongoIndexingPolicyDiscardable: boolean): void =>
|
private onMongoIndexingPolicyDiscardableChange = (isMongoIndexingPolicyDiscardable: boolean): void =>
|
||||||
this.setState({ isMongoIndexingPolicyDiscardable });
|
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 => {
|
public getAnalyticalStorageTtl = (): number => {
|
||||||
if (this.isAnalyticalStorageEnabled) {
|
if (this.isAnalyticalStorageEnabled) {
|
||||||
if (this.state.analyticalStorageTtlSelection === TtlType.On) {
|
if (this.state.analyticalStorageTtlSelection === TtlType.On) {
|
||||||
@@ -669,9 +678,11 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
private onMaxAutoPilotThroughputChange = (newThroughput: number): void => {
|
private onMaxAutoPilotThroughputChange = (newThroughput: number): void => {
|
||||||
let throughputError = "";
|
let throughputError = "";
|
||||||
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
|
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 ${
|
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.`;
|
} RU/s. Change total throughput limit in cost management.`;
|
||||||
}
|
}
|
||||||
this.setState({ autoPilotThroughput: newThroughput, throughputError });
|
this.setState({ autoPilotThroughput: newThroughput, throughputError });
|
||||||
@@ -680,9 +691,11 @@ export class SettingsComponent extends React.Component<SettingsComponentProps, S
|
|||||||
private onThroughputChange = (newThroughput: number): void => {
|
private onThroughputChange = (newThroughput: number): void => {
|
||||||
let throughputError = "";
|
let throughputError = "";
|
||||||
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
|
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) {
|
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 ${
|
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.`;
|
} RU/s. Change total throughput limit in cost management.`;
|
||||||
}
|
}
|
||||||
this.setState({ throughput: newThroughput, throughputError });
|
this.setState({ throughput: newThroughput, throughputError });
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
setThroughputValue(throughput);
|
setThroughputValue(throughput);
|
||||||
|
|
||||||
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
|
const throughputCap = userContext.databaseAccount?.properties.capacity?.totalThroughputLimit;
|
||||||
|
const numberOfRegions = userContext.databaseAccount?.properties.locations?.length || 1;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// throughput cap check for the initial state
|
// throughput cap check for the initial state
|
||||||
@@ -57,12 +58,13 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
totalThroughput *= numberOfRegions;
|
||||||
setTotalThroughputUsed(totalThroughput);
|
setTotalThroughputUsed(totalThroughput);
|
||||||
|
|
||||||
if (throughputCap && throughputCap - totalThroughput < throughput) {
|
if (throughputCap && throughputCap - totalThroughput < throughput) {
|
||||||
setThroughputError(
|
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 ${
|
`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.`
|
} RU/s. Change total throughput limit in cost management.`
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -74,7 +76,7 @@ export const ThroughputInput: FunctionComponent<ThroughputInputProps> = ({
|
|||||||
if (throughputCap && throughputCap - totalThroughputUsed < newThroughput) {
|
if (throughputCap && throughputCap - totalThroughputUsed < newThroughput) {
|
||||||
setThroughputError(
|
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 ${
|
`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.`
|
} RU/s. Change total throughput limit in cost management.`
|
||||||
);
|
);
|
||||||
setIsThroughputCapExceeded(true);
|
setIsThroughputCapExceeded(true);
|
||||||
|
|||||||
@@ -1176,7 +1176,9 @@ export default class Explorer {
|
|||||||
<CassandraAddCollectionPane explorer={this} cassandraApiClient={new CassandraAPIDataClient()} />
|
<CassandraAddCollectionPane explorer={this} cassandraApiClient={new CassandraAPIDataClient()} />
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await useDatabases.getState().loadDatabaseOffers();
|
userContext.databaseAccount?.properties.capacity?.totalThroughputLimit
|
||||||
|
? await useDatabases.getState().loadAllOffers()
|
||||||
|
: await useDatabases.getState().loadDatabaseOffers();
|
||||||
useSidePanel
|
useSidePanel
|
||||||
.getState()
|
.getState()
|
||||||
.openSidePanel("New " + getCollectionName(), <AddCollectionPanel explorer={this} databaseId={databaseId} />);
|
.openSidePanel("New " + getCollectionName(), <AddCollectionPanel explorer={this} databaseId={databaseId} />);
|
||||||
|
|||||||
@@ -308,8 +308,12 @@ function createNewDatabase(container: Explorer): CommandButtonComponentProps {
|
|||||||
return {
|
return {
|
||||||
iconSrc: AddDatabaseIcon,
|
iconSrc: AddDatabaseIcon,
|
||||||
iconAlt: label,
|
iconAlt: label,
|
||||||
onCommandClick: () =>
|
onCommandClick: async () => {
|
||||||
useSidePanel.getState().openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={container} />),
|
if (userContext.databaseAccount?.properties.capacity?.totalThroughputLimit) {
|
||||||
|
await useDatabases.getState().loadAllOffers();
|
||||||
|
}
|
||||||
|
useSidePanel.getState().openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={container} />);
|
||||||
|
},
|
||||||
commandButtonLabel: label,
|
commandButtonLabel: label,
|
||||||
ariaLabel: label,
|
ariaLabel: label,
|
||||||
hasPopup: true,
|
hasPopup: true,
|
||||||
|
|||||||
@@ -307,10 +307,14 @@ export class SplashScreen extends React.Component<SplashScreenProps> {
|
|||||||
iconSrc: AddDatabaseIcon,
|
iconSrc: AddDatabaseIcon,
|
||||||
title: "New " + getDatabaseName(),
|
title: "New " + getDatabaseName(),
|
||||||
description: undefined,
|
description: undefined,
|
||||||
onClick: () =>
|
onClick: async () => {
|
||||||
|
if (userContext.databaseAccount?.properties.capacity?.totalThroughputLimit) {
|
||||||
|
await useDatabases.getState().loadAllOffers();
|
||||||
|
}
|
||||||
useSidePanel
|
useSidePanel
|
||||||
.getState()
|
.getState()
|
||||||
.openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={this.container} />),
|
.openSidePanel("New " + getDatabaseName(), <AddDatabasePanel explorer={this.container} />);
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -576,7 +576,9 @@ export default class Collection implements ViewModels.Collection {
|
|||||||
|
|
||||||
public onSettingsClick = async (): Promise<void> => {
|
public onSettingsClick = async (): Promise<void> => {
|
||||||
useSelectedNode.getState().setSelectedNode(this);
|
useSelectedNode.getState().setSelectedNode(this);
|
||||||
await this.loadOffer();
|
userContext.databaseAccount?.properties.capacity?.totalThroughputLimit
|
||||||
|
? await useDatabases.getState().loadAllOffers()
|
||||||
|
: await this.loadOffer();
|
||||||
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Settings);
|
this.selectedSubnodeKind(ViewModels.CollectionTabKind.Settings);
|
||||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||||
description: "Settings node",
|
description: "Settings node",
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export default class Database implements ViewModels.Database {
|
|||||||
this.isOfferRead = false;
|
this.isOfferRead = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public onSettingsClick = (): void => {
|
public onSettingsClick = async (): Promise<void> => {
|
||||||
useSelectedNode.getState().setSelectedNode(this);
|
useSelectedNode.getState().setSelectedNode(this);
|
||||||
this.selectedSubnodeKind(ViewModels.CollectionTabKind.DatabaseSettings);
|
this.selectedSubnodeKind(ViewModels.CollectionTabKind.DatabaseSettings);
|
||||||
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
TelemetryProcessor.trace(Action.SelectItem, ActionModifiers.Mark, {
|
||||||
@@ -66,6 +66,10 @@ export default class Database implements ViewModels.Database {
|
|||||||
dataExplorerArea: Constants.Areas.ResourceTree,
|
dataExplorerArea: Constants.Areas.ResourceTree,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (userContext.databaseAccount?.properties.capacity?.totalThroughputLimit) {
|
||||||
|
await useDatabases.getState().loadAllOffers();
|
||||||
|
}
|
||||||
|
|
||||||
const pendingNotificationsPromise: Promise<DataModels.Notification> = this.getPendingThroughputSplitNotification();
|
const pendingNotificationsPromise: Promise<DataModels.Notification> = this.getPendingThroughputSplitNotification();
|
||||||
const tabKind = ViewModels.CollectionTabKind.DatabaseSettingsV2;
|
const tabKind = ViewModels.CollectionTabKind.DatabaseSettingsV2;
|
||||||
const matchingTabs = useTabs.getState().getTabs(tabKind, (tab) => tab.node?.id() === this.id());
|
const matchingTabs = useTabs.getState().getTabs(tabKind, (tab) => tab.node?.id() === this.id());
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ interface DatabasesState {
|
|||||||
findCollection: (databaseId: string, collectionId: string) => ViewModels.Collection;
|
findCollection: (databaseId: string, collectionId: string) => ViewModels.Collection;
|
||||||
isLastCollection: () => boolean;
|
isLastCollection: () => boolean;
|
||||||
loadDatabaseOffers: () => Promise<void>;
|
loadDatabaseOffers: () => Promise<void>;
|
||||||
|
loadAllOffers: () => Promise<void>;
|
||||||
isFirstResourceCreated: () => boolean;
|
isFirstResourceCreated: () => boolean;
|
||||||
findSelectedDatabase: () => ViewModels.Database;
|
findSelectedDatabase: () => ViewModels.Database;
|
||||||
validateDatabaseId: (id: string) => boolean;
|
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: () => {
|
isFirstResourceCreated: () => {
|
||||||
const databases = get().databases;
|
const databases = get().databases;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user