Merge pull request #14392 from influxdata/fix/rate-limit-banners
fix(ui/cloud): display alerts for read or write ratespull/14400/head
commit
e0325296d3
|
@ -50,6 +50,8 @@ export enum ActionTypes {
|
|||
SetDashboardLimitStatus = 'SET_DASHBOARD_LIMIT_STATUS',
|
||||
SetBucketLimitStatus = 'SET_BUCKET_LIMIT_STATUS',
|
||||
SetTaskLimitStatus = 'SET_TASK_LIMIT_STATUS',
|
||||
SetReadRateLimitStatus = 'SET_READ_RATE_LIMIT_STATUS',
|
||||
SetWriteRateLimitStatus = 'SET_WRITE_RATE_LIMIT_STATUS',
|
||||
}
|
||||
|
||||
export type Actions =
|
||||
|
@ -58,6 +60,8 @@ export type Actions =
|
|||
| SetDashboardLimitStatus
|
||||
| SetBucketLimitStatus
|
||||
| SetTaskLimitStatus
|
||||
| SetReadRateLimitStatus
|
||||
| SetWriteRateLimitStatus
|
||||
|
||||
export interface SetLimits {
|
||||
type: ActionTypes.SetLimits
|
||||
|
@ -112,6 +116,33 @@ export const setTaskLimitStatus = (
|
|||
payload: {limitStatus},
|
||||
}
|
||||
}
|
||||
export interface SetReadRateLimitStatus {
|
||||
type: ActionTypes.SetReadRateLimitStatus
|
||||
payload: {limitStatus: LimitStatus}
|
||||
}
|
||||
|
||||
export const setReadRateLimitStatus = (
|
||||
limitStatus: LimitStatus
|
||||
): SetReadRateLimitStatus => {
|
||||
return {
|
||||
type: ActionTypes.SetReadRateLimitStatus,
|
||||
payload: {limitStatus},
|
||||
}
|
||||
}
|
||||
|
||||
export interface SetWriteRateLimitStatus {
|
||||
type: ActionTypes.SetWriteRateLimitStatus
|
||||
payload: {limitStatus: LimitStatus}
|
||||
}
|
||||
|
||||
export const setWriteRateLimitStatus = (
|
||||
limitStatus: LimitStatus
|
||||
): SetWriteRateLimitStatus => {
|
||||
return {
|
||||
type: ActionTypes.SetWriteRateLimitStatus,
|
||||
payload: {limitStatus},
|
||||
}
|
||||
}
|
||||
|
||||
export interface SetLimitsStatus {
|
||||
type: ActionTypes.SetLimitsStatus
|
||||
|
@ -138,10 +169,17 @@ export const getReadWriteLimits = () => async (
|
|||
|
||||
const limits = await getReadWriteLimitsAJAX(org.id)
|
||||
|
||||
const isReadLimited = limits.read.status === LimitStatus.EXCEEDED
|
||||
|
||||
if (isReadLimited) {
|
||||
if (limits.read.status === LimitStatus.EXCEEDED) {
|
||||
dispatch(notify(readLimitReached()))
|
||||
dispatch(setReadRateLimitStatus(LimitStatus.EXCEEDED))
|
||||
} else {
|
||||
dispatch(setReadRateLimitStatus(LimitStatus.OK))
|
||||
}
|
||||
|
||||
if (limits.write.status === LimitStatus.EXCEEDED) {
|
||||
dispatch(setWriteRateLimitStatus(LimitStatus.EXCEEDED))
|
||||
} else {
|
||||
dispatch(setWriteRateLimitStatus(LimitStatus.OK))
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,10 @@ export interface LimitsState {
|
|||
dashboards: Limit
|
||||
tasks: Limit
|
||||
buckets: Limit
|
||||
rate: {
|
||||
readKBs: Limit
|
||||
writeKBs: Limit
|
||||
}
|
||||
status: RemoteDataState
|
||||
}
|
||||
|
||||
|
@ -26,6 +30,10 @@ export const defaultState: LimitsState = {
|
|||
dashboards: defaultLimit,
|
||||
tasks: defaultLimit,
|
||||
buckets: defaultLimit,
|
||||
rate: {
|
||||
readKBs: defaultLimit,
|
||||
writeKBs: defaultLimit,
|
||||
},
|
||||
status: RemoteDataState.NotStarted,
|
||||
}
|
||||
|
||||
|
@ -47,10 +55,14 @@ export const limitsReducer = (
|
|||
const {maxBuckets} = limits.bucket
|
||||
const {maxDashboards} = limits.dashboard
|
||||
const {maxTasks} = limits.task
|
||||
const {readKBs, writeKBs} = limits.rate
|
||||
|
||||
draftState.buckets.maxAllowed = maxBuckets
|
||||
draftState.dashboards.maxAllowed = maxDashboards
|
||||
draftState.tasks.maxAllowed = maxTasks
|
||||
draftState.rate.readKBs.maxAllowed = readKBs
|
||||
draftState.rate.writeKBs.maxAllowed = writeKBs
|
||||
|
||||
return
|
||||
}
|
||||
case ActionTypes.SetDashboardLimitStatus: {
|
||||
|
@ -65,6 +77,14 @@ export const limitsReducer = (
|
|||
draftState.tasks.limitStatus = action.payload.limitStatus
|
||||
return
|
||||
}
|
||||
case ActionTypes.SetReadRateLimitStatus: {
|
||||
draftState.rate.readKBs.limitStatus = action.payload.limitStatus
|
||||
return
|
||||
}
|
||||
case ActionTypes.SetWriteRateLimitStatus: {
|
||||
draftState.rate.writeKBs.limitStatus = action.payload.limitStatus
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -30,3 +30,30 @@ export const extractTaskLimits = (limits: LimitsState): LimitStatus => {
|
|||
export const extractTaskMax = (limits: LimitsState): number => {
|
||||
return get(limits, 'tasks.maxAllowed') || Infinity
|
||||
}
|
||||
|
||||
export const extractRateLimitStatus = (limits: LimitsState): LimitStatus => {
|
||||
const statuses = [
|
||||
get(limits, 'rate.writeKBs.limitStatus'),
|
||||
get(limits, 'rate.readKBs.limitStatus'),
|
||||
]
|
||||
|
||||
if (statuses.includes(LimitStatus.EXCEEDED)) {
|
||||
return LimitStatus.EXCEEDED
|
||||
}
|
||||
|
||||
return LimitStatus.OK
|
||||
}
|
||||
|
||||
export const extractRateLimitResourceName = (limits: LimitsState): string => {
|
||||
const rateLimitedResources = []
|
||||
|
||||
if (get(limits, 'rate.writeKBs.limitStatus') === LimitStatus.EXCEEDED) {
|
||||
rateLimitedResources.push('writes')
|
||||
}
|
||||
|
||||
if (get(limits, 'rate.readKBs.limitStatus') === LimitStatus.EXCEEDED) {
|
||||
rateLimitedResources.push('reads')
|
||||
}
|
||||
|
||||
return rateLimitedResources.join(' and ')
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ import DashboardComponent from 'src/dashboards/components/Dashboard'
|
|||
import ManualRefresh from 'src/shared/components/ManualRefresh'
|
||||
import {HoverTimeProvider} from 'src/dashboards/utils/hoverTime'
|
||||
import VariablesControlBar from 'src/dashboards/components/variablesControlBar/VariablesControlBar'
|
||||
import LimitChecker from 'src/cloud/components/LimitChecker'
|
||||
import AssetLimitAlert from 'src/cloud/components/AssetLimitAlert'
|
||||
|
||||
// Actions
|
||||
import * as dashboardActions from 'src/dashboards/actions'
|
||||
|
@ -26,6 +28,10 @@ import {
|
|||
// Utils
|
||||
import {GlobalAutoRefresher} from 'src/utils/AutoRefresher'
|
||||
import {createView} from 'src/shared/utils/view'
|
||||
import {
|
||||
extractRateLimitResourceName,
|
||||
extractRateLimitStatus,
|
||||
} from 'src/cloud/utils/limits'
|
||||
|
||||
// Constants
|
||||
import {
|
||||
|
@ -57,9 +63,11 @@ import * as AppActions from 'src/types/actions/app'
|
|||
import * as ColorsModels from 'src/types/colors'
|
||||
import {toggleShowVariablesControls} from 'src/userSettings/actions'
|
||||
import {Organization} from '@influxdata/influx'
|
||||
import LimitChecker from 'src/cloud/components/LimitChecker'
|
||||
import {LimitStatus} from 'src/cloud/actions/limits'
|
||||
|
||||
interface StateProps {
|
||||
resourceName: string
|
||||
limitStatus: LimitStatus
|
||||
org: Organization
|
||||
links: Links
|
||||
zoomedTimeRange: TimeRange
|
||||
|
@ -164,6 +172,8 @@ class DashboardPage extends Component<Props, State> {
|
|||
zoomedTimeRange,
|
||||
dashboard,
|
||||
autoRefresh,
|
||||
limitStatus,
|
||||
resourceName,
|
||||
manualRefresh,
|
||||
onManualRefresh,
|
||||
inPresentationMode,
|
||||
|
@ -176,47 +186,52 @@ class DashboardPage extends Component<Props, State> {
|
|||
return (
|
||||
<Page titleTag={this.pageTitle}>
|
||||
<LimitChecker>
|
||||
<HoverTimeProvider>
|
||||
<DashboardHeader
|
||||
org={org}
|
||||
dashboard={dashboard}
|
||||
timeRange={timeRange}
|
||||
autoRefresh={autoRefresh}
|
||||
isHidden={inPresentationMode}
|
||||
onAddCell={this.handleAddCell}
|
||||
onAddNote={this.showNoteOverlay}
|
||||
onManualRefresh={onManualRefresh}
|
||||
zoomedTimeRange={zoomedTimeRange}
|
||||
onRenameDashboard={this.handleRenameDashboard}
|
||||
activeDashboard={dashboard ? dashboard.name : ''}
|
||||
handleChooseAutoRefresh={this.handleChooseAutoRefresh}
|
||||
onSetAutoRefreshStatus={this.handleSetAutoRefreshStatus}
|
||||
handleChooseTimeRange={this.handleChooseTimeRange}
|
||||
handleClickPresentationButton={handleClickPresentationButton}
|
||||
toggleVariablesControlBar={onToggleShowVariablesControls}
|
||||
isShowingVariablesControlBar={showVariablesControls}
|
||||
/>
|
||||
{showVariablesControls && !!dashboard && (
|
||||
<VariablesControlBar dashboardID={dashboard.id} />
|
||||
)}
|
||||
{!!dashboard && (
|
||||
<DashboardComponent
|
||||
inView={this.inView}
|
||||
<AssetLimitAlert
|
||||
resourceName={resourceName}
|
||||
limitStatus={limitStatus}
|
||||
>
|
||||
<HoverTimeProvider>
|
||||
<DashboardHeader
|
||||
org={org}
|
||||
dashboard={dashboard}
|
||||
timeRange={timeRange}
|
||||
manualRefresh={manualRefresh}
|
||||
setScrollTop={this.setScrollTop}
|
||||
onCloneCell={this.handleCloneCell}
|
||||
inPresentationMode={inPresentationMode}
|
||||
onPositionChange={this.handlePositionChange}
|
||||
onDeleteCell={this.handleDeleteDashboardCell}
|
||||
onEditView={this.handleEditView}
|
||||
autoRefresh={autoRefresh}
|
||||
isHidden={inPresentationMode}
|
||||
onAddCell={this.handleAddCell}
|
||||
onEditNote={this.showNoteOverlay}
|
||||
onAddNote={this.showNoteOverlay}
|
||||
onManualRefresh={onManualRefresh}
|
||||
zoomedTimeRange={zoomedTimeRange}
|
||||
onRenameDashboard={this.handleRenameDashboard}
|
||||
activeDashboard={dashboard ? dashboard.name : ''}
|
||||
handleChooseAutoRefresh={this.handleChooseAutoRefresh}
|
||||
onSetAutoRefreshStatus={this.handleSetAutoRefreshStatus}
|
||||
handleChooseTimeRange={this.handleChooseTimeRange}
|
||||
handleClickPresentationButton={handleClickPresentationButton}
|
||||
toggleVariablesControlBar={onToggleShowVariablesControls}
|
||||
isShowingVariablesControlBar={showVariablesControls}
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
</HoverTimeProvider>
|
||||
{showVariablesControls && !!dashboard && (
|
||||
<VariablesControlBar dashboardID={dashboard.id} />
|
||||
)}
|
||||
{!!dashboard && (
|
||||
<DashboardComponent
|
||||
inView={this.inView}
|
||||
dashboard={dashboard}
|
||||
timeRange={timeRange}
|
||||
manualRefresh={manualRefresh}
|
||||
setScrollTop={this.setScrollTop}
|
||||
onCloneCell={this.handleCloneCell}
|
||||
inPresentationMode={inPresentationMode}
|
||||
onPositionChange={this.handlePositionChange}
|
||||
onDeleteCell={this.handleDeleteDashboardCell}
|
||||
onEditView={this.handleEditView}
|
||||
onAddCell={this.handleAddCell}
|
||||
onEditNote={this.showNoteOverlay}
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
</HoverTimeProvider>
|
||||
</AssetLimitAlert>
|
||||
</LimitChecker>
|
||||
</Page>
|
||||
)
|
||||
|
@ -360,6 +375,7 @@ const mstp = (state: AppState, {params: {dashboardID}}): StateProps => {
|
|||
views: {views},
|
||||
userSettings: {showVariablesControls},
|
||||
orgs: {org},
|
||||
cloud: {limits},
|
||||
} = state
|
||||
|
||||
const timeRange =
|
||||
|
@ -369,6 +385,9 @@ const mstp = (state: AppState, {params: {dashboardID}}): StateProps => {
|
|||
|
||||
const dashboard = dashboards.list.find(d => d.id === dashboardID)
|
||||
|
||||
const resourceName = extractRateLimitResourceName(limits)
|
||||
const limitStatus = extractRateLimitStatus(limits)
|
||||
|
||||
return {
|
||||
org,
|
||||
links,
|
||||
|
@ -377,6 +396,8 @@ const mstp = (state: AppState, {params: {dashboardID}}): StateProps => {
|
|||
timeRange,
|
||||
dashboard,
|
||||
autoRefresh,
|
||||
limitStatus,
|
||||
resourceName,
|
||||
inPresentationMode,
|
||||
showVariablesControls,
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import {connect} from 'react-redux'
|
|||
|
||||
// Components
|
||||
import TimeMachine from 'src/timeMachine/components/TimeMachine'
|
||||
import LimitChecker from 'src/cloud/components/LimitChecker'
|
||||
import AssetLimitAlert from 'src/cloud/components/AssetLimitAlert'
|
||||
|
||||
// Actions
|
||||
import {setActiveTimeMachine} from 'src/timeMachine/actions'
|
||||
|
@ -12,14 +14,27 @@ import {setActiveTimeMachine} from 'src/timeMachine/actions'
|
|||
import {DE_TIME_MACHINE_ID} from 'src/timeMachine/constants'
|
||||
import {HoverTimeProvider} from 'src/dashboards/utils/hoverTime'
|
||||
import {queryBuilderFetcher} from 'src/timeMachine/apis/QueryBuilderFetcher'
|
||||
import LimitChecker from 'src/cloud/components/LimitChecker'
|
||||
import {
|
||||
extractRateLimitResourceName,
|
||||
extractRateLimitStatus,
|
||||
} from 'src/cloud/utils/limits'
|
||||
|
||||
// Types
|
||||
import {AppState} from 'src/types'
|
||||
import {LimitStatus} from 'src/cloud/actions/limits'
|
||||
|
||||
interface StateProps {
|
||||
resourceName: string
|
||||
limitStatus: LimitStatus
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
onSetActiveTimeMachine: typeof setActiveTimeMachine
|
||||
}
|
||||
|
||||
class DataExplorer extends PureComponent<DispatchProps, {}> {
|
||||
constructor(props: DispatchProps) {
|
||||
type Props = DispatchProps & StateProps
|
||||
class DataExplorer extends PureComponent<Props, {}> {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
||||
props.onSetActiveTimeMachine(DE_TIME_MACHINE_ID)
|
||||
|
@ -27,23 +42,41 @@ class DataExplorer extends PureComponent<DispatchProps, {}> {
|
|||
}
|
||||
|
||||
public render() {
|
||||
const {resourceName, limitStatus} = this.props
|
||||
|
||||
return (
|
||||
<div className="data-explorer">
|
||||
<LimitChecker>
|
||||
<HoverTimeProvider>
|
||||
<TimeMachine />
|
||||
</HoverTimeProvider>
|
||||
<AssetLimitAlert
|
||||
resourceName={resourceName}
|
||||
limitStatus={limitStatus}
|
||||
>
|
||||
<HoverTimeProvider>
|
||||
<TimeMachine />
|
||||
</HoverTimeProvider>
|
||||
</AssetLimitAlert>
|
||||
</LimitChecker>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mstp = (state: AppState): StateProps => {
|
||||
const {
|
||||
cloud: {limits},
|
||||
} = state
|
||||
|
||||
return {
|
||||
resourceName: extractRateLimitResourceName(limits),
|
||||
limitStatus: extractRateLimitStatus(limits),
|
||||
}
|
||||
}
|
||||
|
||||
const mdtp: DispatchProps = {
|
||||
onSetActiveTimeMachine: setActiveTimeMachine,
|
||||
}
|
||||
|
||||
export default connect<{}, DispatchProps, {}>(
|
||||
null,
|
||||
export default connect<StateProps, DispatchProps, {}>(
|
||||
mstp,
|
||||
mdtp
|
||||
)(DataExplorer)
|
||||
|
|
Loading…
Reference in New Issue