Merge pull request #14392 from influxdata/fix/rate-limit-banners

fix(ui/cloud): display alerts for read or write rates
pull/14400/head
Delmer 2019-07-19 18:14:28 -04:00 committed by GitHub
commit e0325296d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 188 additions and 49 deletions

View File

@ -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) {}
}

View File

@ -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
}
}
})

View File

@ -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 ')
}

View File

@ -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,
}

View File

@ -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)