diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.fixture.ts b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.fixture.ts index e4b28e4dcd62..8efcc957c6ad 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.fixture.ts +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.fixture.ts @@ -146,7 +146,7 @@ export const getStatementDetailsPropsFixture = (): StatementDetailsProps => ({ timeScale: { windowSize: moment.duration(5, "day"), sampleSize: moment.duration(5, "minutes"), - windowEnd: moment.utc("2021.12.12"), + fixedWindowEnd: moment.utc("2021.12.12"), key: "Custom", }, statement: { diff --git a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.fixture.ts b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.fixture.ts index 5d02e7b1aa54..df177219660f 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.fixture.ts +++ b/pkg/ui/workspaces/cluster-ui/src/statementsPage/statementsPage.fixture.ts @@ -785,7 +785,7 @@ const statementsPagePropsFixture: StatementsPageProps = { timeScale: { windowSize: moment.duration(5, "day"), sampleSize: moment.duration(5, "minutes"), - windowEnd: moment.utc("2021.12.12"), + fixedWindowEnd: moment.utc("2021.12.12"), key: "Custom" }, apps: ["$ internal", "movr", "$ cockroach demo"], diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.tsx b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.tsx index c9fc46fa6a7f..fd77526d75f6 100644 --- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleDropdown.tsx @@ -124,8 +124,8 @@ export const TimeScaleDropdown: React.FC = ({ setTimeScale, adjustTimeScaleOnChange, }): React.ReactElement => { - const end = currentScale.windowEnd - ? moment.utc(currentScale.windowEnd) + const end = currentScale.fixedWindowEnd + ? moment.utc(currentScale.fixedWindowEnd) : moment().utc(); const currentWindow: TimeWindow = { start: moment.utc(end).subtract(currentScale.windowSize), @@ -136,7 +136,7 @@ export const TimeScaleDropdown: React.FC = ({ let timeScale: TimeScale = { ...options[rangeOption.label], key: rangeOption.label, - windowEnd: null, + fixedWindowEnd: false, }; if (adjustTimeScaleOnChange) { const timeWindow: TimeWindow = { @@ -156,37 +156,47 @@ export const TimeScaleDropdown: React.FC = ({ const seconds = windowSize.asSeconds(); let selected = {}; let key = currentScale.key; - let windowEnd = moment.utc(currentWindow.end); + let fixedWindowEnd = moment.utc(currentWindow.end); switch (direction) { case ArrowDirection.RIGHT: - if (windowEnd) { - windowEnd = windowEnd.add(seconds, "seconds"); + if (fixedWindowEnd) { + fixedWindowEnd = fixedWindowEnd.add(seconds, "seconds"); } break; case ArrowDirection.LEFT: - windowEnd = windowEnd.subtract(seconds, "seconds"); + fixedWindowEnd = fixedWindowEnd.subtract(seconds, "seconds"); break; case ArrowDirection.CENTER: // CENTER is used to set the time window to the current time. - windowEnd = moment.utc(); + fixedWindowEnd = moment.utc(); break; default: console.error("Unknown direction: ", direction); } + // If the timescale extends into the future then fallback to a default // timescale. Otherwise set the key to "Custom" so it appears correctly. + // If fixedWindowEnd is null, or fixedWindowEnd + windowValid > now. if ( - !windowEnd || - windowEnd > moment.utc().subtract(currentScale.windowValid) + !fixedWindowEnd || + fixedWindowEnd > moment.utc().subtract(currentScale.windowValid) ) { const foundTimeScale = Object.entries(options).find( // eslint-disable-next-line @typescript-eslint/no-unused-vars ([_, value]) => value.windowSize.asSeconds() === windowSize.asSeconds(), ); if (foundTimeScale) { + /** + * This code can be hit by: + * - Select a default option, then click the left arrow, then click the right arrow. + * This (or the parent if block) is *not* hit by: + * - Select a default time, click left, select a custom time of the same range, then click right. The arrow is + * not disabled, but the clause doesn't seem to be true. + */ selected = { key: foundTimeScale[0], ...foundTimeScale[1] }; } else { + // This code might not be possible to hit, due to the right arrow being disabled key = "Custom"; } } else { @@ -195,7 +205,7 @@ export const TimeScaleDropdown: React.FC = ({ let timeScale: TimeScale = { ...currentScale, - windowEnd, + fixedWindowEnd: fixedWindowEnd, windowSize, key, ...selected, @@ -225,7 +235,7 @@ export const TimeScaleDropdown: React.FC = ({ let timeScale: TimeScale = { ...findClosestTimeScale(options, seconds), windowSize: moment.duration(end.diff(start)), - windowEnd: end, + fixedWindowEnd: end, key: "Custom", }; if (adjustTimeScaleOnChange) { diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleTypes.ts b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleTypes.ts index 5412c36be9aa..9c763cf72eb7 100644 --- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleTypes.ts +++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timeScaleTypes.ts @@ -21,9 +21,7 @@ export interface TimeWindow { } /** - * TimeScale describes the requested dimensions of TimeWindows; it - * prescribes a length for the window, along with a period of time that a - * newly created TimeWindow will remain valid. + * TimeScale describes the requested dimensions, from which one can derive concrete TimeWindows using toDateRange. */ export interface TimeScale { // The key used to index in to the availableTimeScales collection. @@ -32,27 +30,27 @@ export interface TimeScale { windowSize: moment.Duration; // The length of time the global time window is valid. The current time window // is invalid if now > (currentWindow.end + windowValid). Default is ten - // seconds. If windowEnd is set this is ignored. + // seconds. If fixedWindowEnd is set this is ignored. windowValid?: moment.Duration; // The expected duration of individual samples for queries at this time scale. sampleSize: moment.Duration; - // The end time of the window, or null if it should be a dynamically moving "now". - windowEnd: moment.Moment | null; + // The fixed end time of the window, or null if it should be a dynamically moving "now". + fixedWindowEnd: moment.Moment | false; } -export class TimeWindowState { +export class TimeScaleState { // Currently selected scale. scale: TimeScale; constructor() { this.scale = { ...defaultTimeScaleOptions["Past 10 Minutes"], - windowEnd: null, + fixedWindowEnd: false, key: "Past 10 Minutes", }; } } -export type DefaultTimeScaleOption = Omit; +export type DefaultTimeScaleOption = Omit; export interface DefaultTimesScaleOptions { [key: string]: DefaultTimeScaleOption; diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timescale.spec.tsx b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timescale.spec.tsx index ff9aef9d5464..75c7efc94856 100644 --- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timescale.spec.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timescale.spec.tsx @@ -19,7 +19,7 @@ import { TimeScaleDropdown, } from "./timeScaleDropdown"; import { defaultTimeScaleOptions, findClosestTimeScale } from "./utils"; -import * as timewindow from "./timeScaleTypes"; +import * as timescale from "./timeScaleTypes"; import moment from "moment"; import { MemoryRouter } from "react-router"; import TimeFrameControls from "./timeFrameControls"; @@ -51,7 +51,7 @@ describe("", function() { let currentWindow: TimeWindow; const setCurrentWindowFromTimeScale = (timeScale: TimeScale): void => { - const end = timeScale.windowEnd || moment.utc(); + const end = timeScale.fixedWindowEnd || moment.utc(); currentWindow = { start: moment(end).subtract(timeScale.windowSize), end, @@ -69,10 +69,10 @@ describe("", function() { beforeEach(() => { clock = sinon.useFakeTimers(new Date(2020, 5, 1, 9, 28, 30)); - const timewindowState = new timewindow.TimeWindowState(); - setCurrentWindowFromTimeScale(timewindowState.scale); + const timeScaleState = new timescale.TimeScaleState(); + setCurrentWindowFromTimeScale(timeScaleState.scale); state = { - currentScale: timewindowState.scale, + currentScale: timeScaleState.scale, setTimeScale: () => {}, }; }); @@ -93,7 +93,7 @@ describe("", function() { const expected: TimeScale = { key: "Past 10 Minutes", ...defaultTimeScaleOptions["Past 10 Minutes"], - windowEnd: null, + fixedWindowEnd: false, }; assert.deepEqual(wrapper.props().currentScale, expected); }); @@ -143,7 +143,7 @@ describe("", function() { }; const currentScale = { ...state.currentScale, - windowEnd: window.end, + fixedWindowEnd: window.end, windowSize: moment.duration( window.end.diff(window.start, "seconds"), "seconds", @@ -191,7 +191,7 @@ describe("", function() { }; const currentTimeScale = { ...state.currentScale, - windowEnd: window.end, + fixedWindowEnd: window.end, }; const arrows = generateDisabledArrows(window); const wrapper = makeTimeScaleDropdown({ diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts index c3b780f70e4c..ff1d42964ed1 100644 --- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts +++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts @@ -80,11 +80,13 @@ export const defaultTimeScaleOptions: DefaultTimesScaleOptions = { export const defaultTimeScaleSelected: TimeScale = { ...defaultTimeScaleOptions["Past 1 Hour"], key: "Past 1 Hour", - windowEnd: null, + fixedWindowEnd: false, }; export const toDateRange = (ts: TimeScale): [moment.Moment, moment.Moment] => { - const end = ts.windowEnd ? moment.utc(ts.windowEnd) : moment().utc(); + const end = ts.fixedWindowEnd + ? moment.utc(ts.fixedWindowEnd) + : moment().utc(); const start = moment.utc(end).subtract(ts.windowSize); return [start, end]; }; @@ -110,7 +112,9 @@ export const findClosestTimeScale = ( // that exactly matches one of the standard available time scales e.g. selecting June 1 at // 0:00 to June 2 at 0:00 when the date is July 1 at 0:00 should return a custom timescale // instead of past day. + // If the start is specified, and the window size matches. if (startSeconds && firstTimeScaleOptionSeconds === seconds) { + // Find what the start would have been, if the end was now. const startWindow = moment() .subtract(firstTimeScaleOptionSeconds, "seconds") .unix(); diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.fixture.ts b/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.fixture.ts index 11bdfb825344..e1c99ad8a06b 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.fixture.ts +++ b/pkg/ui/workspaces/cluster-ui/src/transactionDetails/transactionDetails.fixture.ts @@ -628,6 +628,6 @@ export const transactionDetails = { export const timeScale: TimeScale = { windowSize: moment.duration(1, "year"), sampleSize: moment.duration(1, "day"), - windowEnd: moment.utc("2021.12.31"), + fixedWindowEnd: moment.utc("2021.12.31"), key: "Custom", }; diff --git a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactions.fixture.ts b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactions.fixture.ts index 25ca126b0ef5..691ef58e8562 100644 --- a/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactions.fixture.ts +++ b/pkg/ui/workspaces/cluster-ui/src/transactionsPage/transactions.fixture.ts @@ -47,7 +47,7 @@ export const columns: string[] = ["all"]; export const timeScale: TimeScale = { windowSize: moment.duration(5, "day"), sampleSize: moment.duration(5, "minutes"), - windowEnd: moment.utc("2021.08.12"), + fixedWindowEnd: moment.utc("2021.08.12"), key: "Custom", }; export const timestamp = new protos.google.protobuf.Timestamp({ diff --git a/pkg/ui/workspaces/db-console/ccl/src/views/clusterviz/containers/map/index.tsx b/pkg/ui/workspaces/db-console/ccl/src/views/clusterviz/containers/map/index.tsx index a804d6d00fd7..c1b053610a19 100644 --- a/pkg/ui/workspaces/db-console/ccl/src/views/clusterviz/containers/map/index.tsx +++ b/pkg/ui/workspaces/db-console/ccl/src/views/clusterviz/containers/map/index.tsx @@ -14,7 +14,7 @@ import cn from "classnames"; import { Breadcrumbs } from "src/views/clusterviz/containers/map/breadcrumbs"; import NeedEnterpriseLicense from "src/views/clusterviz/containers/map/needEnterpriseLicense"; import NodeCanvasContainer from "src/views/clusterviz/containers/map/nodeCanvasContainer"; -import TimeScaleDropdown from "src/views/cluster/containers/timescale"; +import TimeScaleDropdown from "src/views/cluster/containers/timeScaleDropdownWithSearchParams"; import swapByLicense from "src/views/shared/containers/licenseSwap"; import { parseLocalityRoute } from "src/util/localities"; import { Loading } from "@cockroachlabs/cluster-ui"; diff --git a/pkg/ui/workspaces/db-console/src/redux/state.ts b/pkg/ui/workspaces/db-console/src/redux/state.ts index a82c5f9a6f10..c800f208ffd8 100644 --- a/pkg/ui/workspaces/db-console/src/redux/state.ts +++ b/pkg/ui/workspaces/db-console/src/redux/state.ts @@ -31,7 +31,7 @@ import { hoverReducer, HoverState } from "./hover"; import { localSettingsReducer, LocalSettingsState } from "./localsettings"; import { metricsReducer, MetricsState } from "./metrics"; import { queryManagerReducer, QueryManagerState } from "./queryManager/reducer"; -import { timeWindowReducer, TimeWindowState } from "./timewindow"; +import { timeScaleReducer, TimeScaleState } from "./timeScale"; import { uiDataReducer, UIDataState } from "./uiData"; import { loginReducer, LoginAPIState } from "./login"; import rootSaga from "./sagas"; @@ -43,7 +43,7 @@ export interface AdminUIState { metrics: MetricsState; queryManager: QueryManagerState; router: RouterState; - timewindow: TimeWindowState; + timeScale: TimeScaleState; uiData: UIDataState; login: LoginAPIState; } @@ -65,7 +65,7 @@ export function createAdminUIStore(historyInst: History) { metrics: metricsReducer, queryManager: queryManagerReducer, router: routerReducer, - timewindow: timeWindowReducer, + timeScale: timeScaleReducer, uiData: uiDataReducer, login: loginReducer, }), diff --git a/pkg/ui/workspaces/db-console/src/redux/statements/statementsActions.ts b/pkg/ui/workspaces/db-console/src/redux/statements/statementsActions.ts index 7ba41c63873a..ee90c0206ea0 100644 --- a/pkg/ui/workspaces/db-console/src/redux/statements/statementsActions.ts +++ b/pkg/ui/workspaces/db-console/src/redux/statements/statementsActions.ts @@ -12,7 +12,7 @@ import { Action } from "redux"; import { PayloadAction } from "src/interfaces/action"; import { google } from "@cockroachlabs/crdb-protobuf-client"; import IDuration = google.protobuf.IDuration; -import { TimeScale } from "src/redux/timewindow"; +import { TimeScale } from "src/redux/timeScale"; export const CREATE_STATEMENT_DIAGNOSTICS_REPORT = "cockroachui/statements/CREATE_STATEMENT_DIAGNOSTICS_REPORT"; diff --git a/pkg/ui/workspaces/db-console/src/redux/timewindow.spec.ts b/pkg/ui/workspaces/db-console/src/redux/timeScale.spec.ts similarity index 60% rename from pkg/ui/workspaces/db-console/src/redux/timewindow.spec.ts rename to pkg/ui/workspaces/db-console/src/redux/timeScale.spec.ts index 905fbcbd8a7c..299ca4470da8 100644 --- a/pkg/ui/workspaces/db-console/src/redux/timewindow.spec.ts +++ b/pkg/ui/workspaces/db-console/src/redux/timeScale.spec.ts @@ -10,36 +10,36 @@ import { assert } from "chai"; import { defaultTimeScaleOptions } from "@cockroachlabs/cluster-ui"; -import * as timewindow from "./timewindow"; +import * as timeScale from "./timeScale"; import moment from "moment"; -describe("time window reducer", function() { +describe("time scale reducer", function() { describe("actions", function() { - it("should create the correct action to set the current time window", function() { + it("should create the correct SET_METRICS_MOVING_WINDOW action to set the current time window", function() { const start = moment(); const end = start.add(10, "s"); const expectedSetting = { - type: timewindow.SET_WINDOW, + type: timeScale.SET_METRICS_MOVING_WINDOW, payload: { start, end, }, }; assert.deepEqual( - timewindow.setTimeWindow({ start, end }), + timeScale.setMetricsMovingWindow({ start, end }), expectedSetting, ); }); - it("should create the correct action to set time window settings", function() { - const payload: timewindow.TimeScale = { + it("should create the correct SET_SCALE action to set time window settings", function() { + const payload: timeScale.TimeScale = { windowSize: moment.duration(10, "s"), windowValid: moment.duration(10, "s"), sampleSize: moment.duration(10, "s"), - windowEnd: null, + fixedWindowEnd: false, }; - assert.deepEqual(timewindow.setTimeScale(payload), { - type: timewindow.SET_SCALE, + assert.deepEqual(timeScale.setTimeScale(payload), { + type: timeScale.SET_SCALE, payload, }); }); @@ -48,57 +48,57 @@ describe("time window reducer", function() { describe("reducer", () => { it("should have the correct default value.", () => { assert.deepEqual( - timewindow.timeWindowReducer(undefined, { type: "unknown" }), - new timewindow.TimeWindowState(), + timeScale.timeScaleReducer(undefined, { type: "unknown" }), + new timeScale.TimeScaleState(), ); - assert.deepEqual(new timewindow.TimeWindowState().scale, { + assert.deepEqual(new timeScale.TimeScaleState().scale, { ...defaultTimeScaleOptions["Past 10 Minutes"], key: "Past 10 Minutes", - windowEnd: null, + fixedWindowEnd: false, }); }); - describe("setTimeWindow", () => { + describe("setMetricsMovingWindow", () => { const start = moment(); const end = start.add(10, "s"); it("should correctly overwrite previous value", () => { - const expected = new timewindow.TimeWindowState(); + const expected = new timeScale.TimeScaleState(); expected.metricsTime.currentWindow = { start, end, }; - expected.metricsTime.scaleChanged = false; + expected.metricsTime.shouldUpdateMetricsWindowFromScale = false; assert.deepEqual( - timewindow.timeWindowReducer( + timeScale.timeScaleReducer( undefined, - timewindow.setTimeWindow({ start, end }), + timeScale.setMetricsMovingWindow({ start, end }), ), expected, ); }); }); - describe("setTimeWindowSettings", () => { + describe("setTimeScale", () => { const newSize = moment.duration(1, "h"); const newValid = moment.duration(1, "m"); const newSample = moment.duration(1, "m"); it("should correctly overwrite previous value", () => { - const expected = new timewindow.TimeWindowState(); + const expected = new timeScale.TimeScaleState(); expected.scale = { windowSize: newSize, windowValid: newValid, sampleSize: newSample, - windowEnd: null, + fixedWindowEnd: false, }; - expected.metricsTime.scaleChanged = true; + expected.metricsTime.shouldUpdateMetricsWindowFromScale = true; assert.deepEqual( - timewindow.timeWindowReducer( + timeScale.timeScaleReducer( undefined, - timewindow.setTimeScale({ + timeScale.setTimeScale({ windowSize: newSize, windowValid: newValid, sampleSize: newSample, - windowEnd: null, + fixedWindowEnd: false, }), ), expected, diff --git a/pkg/ui/workspaces/db-console/src/redux/timewindow.ts b/pkg/ui/workspaces/db-console/src/redux/timeScale.ts similarity index 70% rename from pkg/ui/workspaces/db-console/src/redux/timewindow.ts rename to pkg/ui/workspaces/db-console/src/redux/timeScale.ts index 73822ce88079..1971053d86b5 100644 --- a/pkg/ui/workspaces/db-console/src/redux/timewindow.ts +++ b/pkg/ui/workspaces/db-console/src/redux/timeScale.ts @@ -19,9 +19,11 @@ import _ from "lodash"; import { defaultTimeScaleOptions } from "@cockroachlabs/cluster-ui"; import moment from "moment"; -export const SET_WINDOW = "cockroachui/timewindow/SET_WINDOW"; -export const SET_RANGE = "cockroachui/timewindow/SET_RANGE"; export const SET_SCALE = "cockroachui/timewindow/SET_SCALE"; +export const SET_METRICS_MOVING_WINDOW = + "cockroachui/timewindow/SET_METRICS_MOVING_WINDOW"; +export const SET_METRICS_FIXED_WINDOW = + "cockroachui/timewindow/SET_METRICS_FIXED_WINDOW"; /** * TimeWindow represents an absolute window of time, defined with a start and @@ -43,74 +45,83 @@ export interface TimeScale { // The size of a global time window. Default is ten minutes. windowSize: moment.Duration; // The length of time the global time window is valid. The current time window - // is invalid if now > (currentWindow.end + windowValid). Default is ten - // seconds. If windowEnd is set this is ignored. + // is invalid if now > (metricsTime.currentWindow.end + windowValid). Default is ten + // seconds. If fixedWindowEnd is set this is ignored. windowValid?: moment.Duration; // The expected duration of individual samples for queries at this time scale. sampleSize: moment.Duration; - // The end time of the window, or null if it should be a dynamically moving "now". - windowEnd: moment.Moment | null; + // The fixed end time of the window, or null if it should be a dynamically moving "now". + fixedWindowEnd: moment.Moment | false; } -export class TimeWindowState { +export class TimeScaleState { // Currently selected scale. scale: TimeScale; - // Timekeeping for the DB Console metrics page. + /** + * Timekeeping for the db console metrics page. Due to tech debt, this duplicates part of state currently in scale. + * e.g., + * currentWindow.start can be derived from metricsTime.currentWindow.end - scale.windowSize, + * and metricsTime.isFixedWindow can be derived from scale. + * However, the key difference is that metricsTime.currentWindow.end is different from scale.fixedWindowEnd. + * Specifically, when the end is "now", scale.fixedWindowEnd is null and metricsTime.currentWindow.end is a specific + * time that is continually updated in order to do polling. + */ metricsTime: { - // Currently established time window. + // Start and end times to be used for metrics page graphs. currentWindow: TimeWindow; - // True if scale has changed since currentWindow was generated. - scaleChanged: boolean; - useTimeRange: boolean; + // True if scale has changed since currentWindow was generated, and it should be re-generated from scale. + shouldUpdateMetricsWindowFromScale: boolean; + // True if currentWindow should be unchanging. False if it should be updated with new "now" times. + isFixedWindow: boolean; }; constructor() { this.scale = { ...defaultTimeScaleOptions["Past 10 Minutes"], key: "Past 10 Minutes", - windowEnd: null, + fixedWindowEnd: false, }; this.metricsTime = { // This is explicitly initialized as undefined to match the prior implementation while satisfying Typescript currentWindow: undefined, - useTimeRange: false, + isFixedWindow: false, // This is used to update the metrics time window after the scale is changed, and prevent cycles when directly // updating the metrics time window. - scaleChanged: false, + shouldUpdateMetricsWindowFromScale: false, }; } } -export function timeWindowReducer( - state = new TimeWindowState(), +export function timeScaleReducer( + state = new TimeScaleState(), action: Action, -): TimeWindowState { +): TimeScaleState { switch (action.type) { - case SET_WINDOW: { + case SET_SCALE: { + const { payload: scale } = action as PayloadAction; + state = _.cloneDeep(state); + if (scale.key === "Custom") { + state.metricsTime.isFixedWindow = true; + } else { + state.metricsTime.isFixedWindow = false; + } + state.scale = scale; + state.metricsTime.shouldUpdateMetricsWindowFromScale = true; + return state; + } + case SET_METRICS_MOVING_WINDOW: { const { payload: tw } = action as PayloadAction; state = _.cloneDeep(state); state.metricsTime.currentWindow = tw; - state.metricsTime.scaleChanged = false; + state.metricsTime.shouldUpdateMetricsWindowFromScale = false; return state; } - case SET_RANGE: { + case SET_METRICS_FIXED_WINDOW: { const { payload: data } = action as PayloadAction; state = _.cloneDeep(state); state.metricsTime.currentWindow = data; - state.metricsTime.useTimeRange = true; - state.metricsTime.scaleChanged = false; - return state; - } - case SET_SCALE: { - const { payload: scale } = action as PayloadAction; - state = _.cloneDeep(state); - if (scale.key === "Custom") { - state.metricsTime.useTimeRange = true; - } else { - state.metricsTime.useTimeRange = false; - } - state.scale = scale; - state.metricsTime.scaleChanged = true; + state.metricsTime.isFixedWindow = true; + state.metricsTime.shouldUpdateMetricsWindowFromScale = false; return state; } default: @@ -118,24 +129,28 @@ export function timeWindowReducer( } } -export function setTimeWindow(tw: TimeWindow): PayloadAction { +export function setTimeScale(ts: TimeScale): PayloadAction { return { - type: SET_WINDOW, - payload: tw, + type: SET_SCALE, + payload: ts, }; } -export function setTimeRange(tw: TimeWindow): PayloadAction { +export function setMetricsMovingWindow( + tw: TimeWindow, +): PayloadAction { return { - type: SET_RANGE, + type: SET_METRICS_MOVING_WINDOW, payload: tw, }; } -export function setTimeScale(ts: TimeScale): PayloadAction { +export function setMetricsFixedWindow( + tw: TimeWindow, +): PayloadAction { return { - type: SET_SCALE, - payload: ts, + type: SET_METRICS_FIXED_WINDOW, + payload: tw, }; } diff --git a/pkg/ui/workspaces/db-console/src/views/app/containers/layout/index.tsx b/pkg/ui/workspaces/db-console/src/views/app/containers/layout/index.tsx index 7cf797c863f6..d86430af15e5 100644 --- a/pkg/ui/workspaces/db-console/src/views/app/containers/layout/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/app/containers/layout/index.tsx @@ -15,7 +15,7 @@ import { connect } from "react-redux"; import NavigationBar from "src/views/app/components/layoutSidebar"; import ErrorBoundary from "src/views/app/components/errorMessage/errorBoundary"; -import TimeWindowManager from "src/views/app/containers/timewindow"; +import TimeWindowManager from "src/views/app/containers/metricsTimeManager"; import AlertBanner from "src/views/app/containers/alertBanner"; import RequireLogin from "src/views/login/requireLogin"; import { diff --git a/pkg/ui/workspaces/db-console/src/views/app/containers/timewindow/index.tsx b/pkg/ui/workspaces/db-console/src/views/app/containers/metricsTimeManager/index.tsx similarity index 52% rename from pkg/ui/workspaces/db-console/src/views/app/containers/timewindow/index.tsx rename to pkg/ui/workspaces/db-console/src/views/app/containers/metricsTimeManager/index.tsx index 37a1a312c7cf..bd7b957eb1a7 100644 --- a/pkg/ui/workspaces/db-console/src/views/app/containers/timewindow/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/app/containers/metricsTimeManager/index.tsx @@ -13,45 +13,45 @@ import { connect } from "react-redux"; import moment from "moment"; import { AdminUIState } from "src/redux/state"; -import * as timewindow from "src/redux/timewindow"; +import * as timewindow from "src/redux/timeScale"; import _ from "lodash"; -interface TimeWindowManagerProps { - // The current timewindow redux state. - timeWindow: timewindow.TimeWindowState; +interface MetricsTimeManagerProps { + // The current timescale redux state. + timeScale: timewindow.TimeScaleState; // Callback function used to set a new time window. - setTimeWindow: typeof timewindow.setTimeWindow; + setMetricsMovingWindow: typeof timewindow.setMetricsMovingWindow; // Optional override method to obtain the current time. Used for tests. now?: () => moment.Moment; } -interface TimeWindowManagerState { +interface MetricsTimeManagerState { // Identifier from an outstanding call to setTimeout. timeout: number; } /** - * TimeWindowManager takes responsibility for advancing the current global + * MetricsTimeManager takes responsibility for advancing the current global * time window used by metric graphs. It renders nothing, but will dispatch an * updated time window into the redux store whenever the previous time window is * expired. */ -class TimeWindowManager extends React.Component< - TimeWindowManagerProps, - TimeWindowManagerState +class MetricsTimeManager extends React.Component< + MetricsTimeManagerProps, + MetricsTimeManagerState > { - constructor(props?: TimeWindowManagerProps, context?: any) { + constructor(props?: MetricsTimeManagerProps, context?: any) { super(props, context); this.state = { timeout: null }; } /** - * checkWindow determines when the current time window will expire. If it is + * checkWindow determines when the metrics current time window will expire. If it is * already expired, a new time window is dispatched immediately. Otherwise, * setTimeout is used to asynchronously set a new time window when the current * one expires. */ - checkWindow(props: TimeWindowManagerProps) { + checkWindow(props: MetricsTimeManagerProps) { // Clear any existing timeout. if (this.state.timeout) { clearTimeout(this.state.timeout); @@ -61,22 +61,22 @@ class TimeWindowManager extends React.Component< // If there is no current window, or if scale have changed since this // window was generated, set one immediately. if ( - !props.timeWindow.metricsTime.currentWindow || - props.timeWindow.metricsTime.scaleChanged + !props.timeScale.metricsTime.currentWindow || + props.timeScale.metricsTime.shouldUpdateMetricsWindowFromScale ) { this.setWindow(props); return; } - // Exact time ranges can't expire. - if (props.timeWindow.scale.windowEnd) { + // Fixed time ranges can't expire. + if (props.timeScale.scale.fixedWindowEnd) { // this.setWindow(props); return; } const now = props.now ? props.now() : moment(); - const currentEnd = props.timeWindow.metricsTime.currentWindow.end; - const expires = currentEnd.clone().add(props.timeWindow.scale.windowValid); + const currentEnd = props.timeScale.metricsTime.currentWindow.end; + const expires = currentEnd.clone().add(props.timeScale.scale.windowValid); if (now.isAfter(expires)) { // Current time window is expired, reset it. this.setWindow(props); @@ -94,22 +94,28 @@ class TimeWindowManager extends React.Component< /** * setWindow dispatches a new time window, extending backwards from the - * current time. + * current time, by deriving the time from timeScale. */ - setWindow(props: TimeWindowManagerProps) { - if (!props.timeWindow.scale.windowEnd) { - if (!props.timeWindow.metricsTime.useTimeRange) { + setWindow(props: MetricsTimeManagerProps) { + // The following two `if` blocks check two things that in theory should always be in sync. + // They check if the time selection is a dynamic, moving, now. + if (!props.timeScale.scale.fixedWindowEnd) { + if (!props.timeScale.metricsTime.isFixedWindow) { const now = props.now ? props.now() : moment(); - props.setTimeWindow({ - start: now.clone().subtract(props.timeWindow.scale.windowSize), + // Update the metrics page window with the new "now" time for polling. + props.setMetricsMovingWindow({ + start: now.clone().subtract(props.timeScale.scale.windowSize), end: now, }); } } else { - const windowEnd = props.timeWindow.scale.windowEnd; - props.setTimeWindow({ - start: windowEnd.clone().subtract(props.timeWindow.scale.windowSize), - end: windowEnd, + const fixedWindowEnd = props.timeScale.scale.fixedWindowEnd; + // Update the metrics page window with the fixed, custom end time. + props.setMetricsMovingWindow({ + start: fixedWindowEnd + .clone() + .subtract(props.timeScale.scale.windowSize), + end: fixedWindowEnd, }); } } @@ -118,8 +124,8 @@ class TimeWindowManager extends React.Component< this.checkWindow(this.props); } - componentDidUpdate(prevProps: TimeWindowManagerProps) { - if (!_.isEqual(prevProps.timeWindow, this.props.timeWindow)) { + componentDidUpdate(prevProps: MetricsTimeManagerProps) { + if (!_.isEqual(prevProps.timeScale, this.props.timeScale)) { this.checkWindow(this.props); } } @@ -130,16 +136,16 @@ class TimeWindowManager extends React.Component< } } -const timeWindowManagerConnected = connect( +const metricsTimeManagerConnected = connect( (state: AdminUIState) => { return { - timeWindow: state.timewindow, + timeScale: state.timeScale, }; }, { - setTimeWindow: timewindow.setTimeWindow, + setMetricsMovingWindow: timewindow.setMetricsMovingWindow, }, -)(TimeWindowManager); +)(MetricsTimeManager); -export default timeWindowManagerConnected; -export { TimeWindowManager as TimeWindowManagerUnconnected }; +export default metricsTimeManagerConnected; +export { MetricsTimeManager as MetricsTimeManagerUnconnected }; diff --git a/pkg/ui/workspaces/db-console/src/views/app/containers/timewindow/timewindow.spec.tsx b/pkg/ui/workspaces/db-console/src/views/app/containers/metricsTimeManager/metricsTimeManager.spec.tsx similarity index 90% rename from pkg/ui/workspaces/db-console/src/views/app/containers/timewindow/timewindow.spec.tsx rename to pkg/ui/workspaces/db-console/src/views/app/containers/metricsTimeManager/metricsTimeManager.spec.tsx index 3e6ddc23486f..8e111d0ee05c 100644 --- a/pkg/ui/workspaces/db-console/src/views/app/containers/timewindow/timewindow.spec.tsx +++ b/pkg/ui/workspaces/db-console/src/views/app/containers/metricsTimeManager/metricsTimeManager.spec.tsx @@ -16,24 +16,24 @@ import moment from "moment"; import _ from "lodash"; import "src/enzymeInit"; -import { TimeWindowManagerUnconnected as TimeWindowManager } from "./"; -import * as timewindow from "src/redux/timewindow"; +import { MetricsTimeManagerUnconnected as MetricsTimeManager } from "./"; +import * as timewindow from "src/redux/timeScale"; -describe("", function() { +describe("", function() { let spy: sinon.SinonSpy; - let state: timewindow.TimeWindowState; + let state: timewindow.TimeScaleState; const now = () => moment("11-12-1955 10:04PM -0800", "MM-DD-YYYY hh:mma Z"); beforeEach(function() { spy = sinon.spy(); - state = new timewindow.TimeWindowState(); + state = new timewindow.TimeScaleState(); }); const getManager = () => shallow( - , ); @@ -69,7 +69,7 @@ describe("", function() { start: now().subtract(state.scale.windowSize), end: now(), }; - state.metricsTime.scaleChanged = true; + state.metricsTime.shouldUpdateMetricsWindowFromScale = true; getManager(); assert.isTrue(spy.calledOnce); diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx index 98fa2c2f48b1..1b305d47efcb 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/index.tsx @@ -446,7 +446,7 @@ export class LineGraph extends React.Component { // setNewTimeRange uses code from the TimeScaleDropdown component // to set new start/end ranges in the query params and force a // reload of the rest of the dashboard at new ranges via the props - // `setTimeRange` and `setTimeScale`. + // `setMetricsFixedWindow` and `setTimeScale`. // TODO(davidh): centralize management of query params for time range // TODO(davidh): figure out why the timescale doesn't get more granular // automatically when a narrower time frame is selected. @@ -461,7 +461,7 @@ export class LineGraph extends React.Component { let newTimeScale: TimeScale = { ...findClosestTimeScale(defaultTimeScaleOptions, end - start, start), key: "Custom", - windowEnd: moment.unix(end), + fixedWindowEnd: moment.unix(end), }; if (this.props.adjustTimeScaleOnChange) { newTimeScale = this.props.adjustTimeScaleOnChange( @@ -469,7 +469,7 @@ export class LineGraph extends React.Component { newTimeWindow, ); } - this.props.setTimeRange(newTimeWindow); + this.props.setMetricsFixedWindow(newTimeWindow); this.props.setTimeScale(newTimeScale); const { pathname, search } = this.props.history.location; const urlParams = new URLSearchParams(search); diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/linegraph.spec.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/linegraph.spec.tsx index 85f5df0f5e60..4f5fbcbfde87 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/linegraph.spec.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/components/linegraph/linegraph.spec.tsx @@ -17,7 +17,7 @@ import uPlot from "uplot"; import _ from "lodash"; import { fillGaps, LineGraph, LineGraphProps } from "./index"; -import * as timewindow from "src/redux/timewindow"; +import * as timewindow from "src/redux/timeScale"; import * as protos from "src/js/protos"; import { Axis } from "src/views/shared/components/metricQuery"; import { @@ -49,7 +49,7 @@ describe("", function() { end: new Long(2346), sampleDuration: new Long(1), }, - setTimeRange: timewindow.setTimeRange, + setMetricsFixedWindow: timewindow.setMetricsFixedWindow, setTimeScale: timewindow.setTimeScale, history: { length: 0, diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/index.tsx b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/index.tsx index c8f36f338042..6443a83f8f94 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/cluster/containers/nodeGraphs/index.tsx @@ -22,7 +22,7 @@ import { PageConfig, PageConfigItem, } from "src/views/shared/components/pageconfig"; -import TimeScaleDropdown from "src/views/cluster/containers/timescale"; +import TimeScaleDropdown from "src/views/cluster/containers/timeScaleDropdownWithSearchParams"; import ClusterSummaryBar from "./summaryBar"; import { AdminUIState } from "src/redux/state"; @@ -64,12 +64,12 @@ import overloadDashboard from "./dashboards/overload"; import { getMatchParamByName } from "src/util/query"; import { PayloadAction } from "src/interfaces/action"; import { - setTimeRange, + setMetricsFixedWindow, setTimeScale, TimeWindow, TimeScale, adjustTimeScale, -} from "src/redux/timewindow"; +} from "src/redux/timeScale"; import { InlineAlert } from "src/components"; import { Anchor } from "@cockroachlabs/cluster-ui"; import { reduceStorageOfTimeSeriesDataOperationalFlags } from "src/util/docs"; @@ -119,7 +119,7 @@ type MapDispatchToProps = { refreshNodeSettings: typeof refreshSettings; hoverOn: typeof hoverOn; hoverOff: typeof hoverOff; - setTimeRange: (tw: TimeWindow) => PayloadAction; + setMetricsFixedWindow: (tw: TimeWindow) => PayloadAction; setTimeScale: (ts: TimeScale) => PayloadAction; }; @@ -305,7 +305,7 @@ export class NodeGraphs extends React.Component< { const setDatesByQueryParams = (dates: Partial) => { const now = moment.utc(); + // `currentWindow` is derived from `scale`, and does not have to do with the `currentWindow` for the metrics page. const currentWindow: TimeWindow = { start: moment(now).subtract(props.currentScale.windowSize), end: now, }; + /** + * Prioritize an end defined in the query params. + * Else, use window end. + * Else, a seemingly unreachable option says otherwise use now, but that should never happen since it is set in + * the line above (and is the same value anyway, always now). + */ const end = dates.end?.utc() || currentWindow.end?.utc() || now; + /** + * Prioritize start as defined in the query params. + * Else, use now minus the window size. + * Else, a final seemingly unreachable option (since start is always set above) is to do ten minutes before now. + */ const start = dates.start?.utc() || currentWindow.start?.utc() || @@ -47,8 +59,9 @@ const TimeScaleDropdownWithSearchParams = ( const timeScale: TimeScale = { ...findClosestTimeScale(defaultTimeScaleOptions, seconds), windowSize: moment.duration(end.diff(start)), - windowEnd: null, + fixedWindowEnd: false, }; + if ( moment.duration(now.diff(end)).asMinutes() > timeScale.sampleSize.asMinutes() @@ -94,7 +107,7 @@ const TimeScaleDropdownWithSearchParams = ( props.setTimeScale(timeScale); setQueryParamsByDates( timeScale.windowSize, - timeScale.windowEnd || moment.utc(), + timeScale.fixedWindowEnd || moment.utc(), ); }; @@ -102,7 +115,7 @@ const TimeScaleDropdownWithSearchParams = ( }; const scaleSelector = createSelector( - (state: AdminUIState) => state?.timewindow, + (state: AdminUIState) => state?.timeScale, tw => tw?.scale, ); diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/containers/timescale/timescale.styl b/pkg/ui/workspaces/db-console/src/views/cluster/containers/timeScaleDropdownWithSearchParams/timeScaleDropdownWithSearchParams.styl similarity index 100% rename from pkg/ui/workspaces/db-console/src/views/cluster/containers/timescale/timescale.styl rename to pkg/ui/workspaces/db-console/src/views/cluster/containers/timeScaleDropdownWithSearchParams/timeScaleDropdownWithSearchParams.styl diff --git a/pkg/ui/workspaces/db-console/src/views/cluster/util/graphs.ts b/pkg/ui/workspaces/db-console/src/views/cluster/util/graphs.ts index 0dc944551a92..f0f8d7933f17 100644 --- a/pkg/ui/workspaces/db-console/src/views/cluster/util/graphs.ts +++ b/pkg/ui/workspaces/db-console/src/views/cluster/util/graphs.ts @@ -541,7 +541,7 @@ export function configureUPlotLineChart( metrics: React.ReactElement[], axis: React.ReactElement, data: TSResponse, - setTimeRange: (startMillis: number, endMillis: number) => void, + setMetricsFixedWindow: (startMillis: number, endMillis: number) => void, getLatestXAxisDomain: () => AxisDomain, getLatestYAxisDomain: () => AxisDomain, ): uPlot.Options { @@ -677,7 +677,7 @@ export function configureUPlotLineChart( // From what I understand, `self.select` contains the pixel edges // of the user's selection. Then I use the `posToIdx` to tell me // what the xAxis range is of the pixels. - setTimeRange( + setMetricsFixedWindow( self.data[0][self.posToIdx(self.select.left)], self.data[0][self.posToIdx(self.select.left + self.select.width)], ); diff --git a/pkg/ui/workspaces/db-console/src/views/devtools/containers/raftMessages/index.tsx b/pkg/ui/workspaces/db-console/src/views/devtools/containers/raftMessages/index.tsx index 54d2676c2cb8..c0a087e17902 100644 --- a/pkg/ui/workspaces/db-console/src/views/devtools/containers/raftMessages/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/devtools/containers/raftMessages/index.tsx @@ -28,7 +28,7 @@ import { GraphDashboardProps, storeIDsForNode, } from "src/views/cluster/containers/nodeGraphs/dashboards/dashboardUtils"; -import TimeScaleDropdown from "src/views/cluster/containers/timescale"; +import TimeScaleDropdown from "src/views/cluster/containers/timeScaleDropdownWithSearchParams"; import Dropdown, { DropdownOption } from "src/views/shared/components/dropdown"; import { PageConfig, diff --git a/pkg/ui/workspaces/db-console/src/views/reports/containers/customChart/index.tsx b/pkg/ui/workspaces/db-console/src/views/reports/containers/customChart/index.tsx index 4a0742d80e34..9a83895262ef 100644 --- a/pkg/ui/workspaces/db-console/src/views/reports/containers/customChart/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/reports/containers/customChart/index.tsx @@ -19,7 +19,7 @@ import { refreshMetricMetadata, refreshNodes } from "src/redux/apiReducers"; import { nodesSummarySelector, NodesSummary } from "src/redux/nodes"; import { AdminUIState } from "src/redux/state"; import { LineGraph } from "src/views/cluster/components/linegraph"; -import TimeScaleDropdown from "src/views/cluster/containers/timescale"; +import TimeScaleDropdown from "src/views/cluster/containers/timeScaleDropdownWithSearchParams"; import { DropdownOption } from "src/views/shared/components/dropdown"; import { MetricsDataProvider } from "src/views/shared/containers/metricDataProvider"; import { @@ -44,9 +44,9 @@ import { PayloadAction } from "src/interfaces/action"; import { TimeWindow, TimeScale, - setTimeRange, + setMetricsFixedWindow, setTimeScale, -} from "src/redux/timewindow"; +} from "src/redux/timeScale"; export interface CustomChartProps { refreshNodes: typeof refreshNodes; @@ -54,7 +54,7 @@ export interface CustomChartProps { nodesSummary: NodesSummary; refreshMetricMetadata: typeof refreshMetricMetadata; metricsMetadata: MetricsMetadata; - setTimeRange: (tw: TimeWindow) => PayloadAction; + setMetricsFixedWindow: (tw: TimeWindow) => PayloadAction; setTimeScale: (ts: TimeScale) => PayloadAction; } @@ -205,7 +205,7 @@ export class CustomChart extends React.Component< @@ -328,7 +328,7 @@ const mapStateToProps = (state: AdminUIState) => ({ const mapDispatchToProps = { refreshNodes, refreshMetricMetadata, - setTimeRange, + setMetricsFixedWindow: setMetricsFixedWindow, setTimeScale, }; diff --git a/pkg/ui/workspaces/db-console/src/views/shared/components/metricQuery/index.tsx b/pkg/ui/workspaces/db-console/src/views/shared/components/metricQuery/index.tsx index cb7bb9cb119b..77d22c1389b8 100644 --- a/pkg/ui/workspaces/db-console/src/views/shared/components/metricQuery/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/shared/components/metricQuery/index.tsx @@ -37,7 +37,7 @@ import TimeSeriesQueryAggregator = protos.cockroach.ts.tspb.TimeSeriesQueryAggre import TimeSeriesQueryDerivative = protos.cockroach.ts.tspb.TimeSeriesQueryDerivative; import Long from "long"; import { History } from "history"; -import { TimeWindow, TimeScale } from "src/redux/timewindow"; +import { TimeWindow, TimeScale } from "src/redux/timeScale"; import { PayloadAction } from "src/interfaces/action"; /** @@ -160,7 +160,7 @@ export interface MetricsDataComponentProps { // convenient syntax for a common use case where all metrics on a graph are // are from the same source set. sources?: string[]; - setTimeRange?: (tw: TimeWindow) => PayloadAction; + setMetricsFixedWindow?: (tw: TimeWindow) => PayloadAction; setTimeScale?: (ts: TimeScale) => PayloadAction; history?: History; adjustTimeScaleOnChange?: ( diff --git a/pkg/ui/workspaces/db-console/src/views/shared/containers/metricDataProvider/index.tsx b/pkg/ui/workspaces/db-console/src/views/shared/containers/metricDataProvider/index.tsx index 4eac2393d59a..69b1778a6813 100644 --- a/pkg/ui/workspaces/db-console/src/views/shared/containers/metricDataProvider/index.tsx +++ b/pkg/ui/workspaces/db-console/src/views/shared/containers/metricDataProvider/index.tsx @@ -97,7 +97,7 @@ interface MetricsDataProviderConnectProps { timeInfo: QueryTimeInfo; requestMetrics: typeof requestMetricsAction; refreshNodeSettings: typeof refreshSettings; - setTimeRange?: (tw: TimeWindow) => PayloadAction; + setMetricsFixedWindow?: (tw: TimeWindow) => PayloadAction; setTimeScale?: (ts: TimeScale) => PayloadAction; history?: History; } @@ -234,7 +234,7 @@ class MetricsDataProvider extends React.Component< const dataProps: MetricsDataComponentProps = { data: this.getData(), timeInfo: this.props.timeInfo, - setTimeRange: this.props.setTimeRange, + setMetricsFixedWindow: this.props.setMetricsFixedWindow, setTimeScale: this.props.setTimeScale, history: this.props.history, adjustTimeScaleOnChange, @@ -249,7 +249,7 @@ class MetricsDataProvider extends React.Component< // timeInfoSelector converts the current global time window into a set of Long // timestamps, which can be sent with requests to the server. const timeInfoSelector = createSelector( - (state: AdminUIState) => state.timewindow, + (state: AdminUIState) => state.timeScale, tw => { if (!_.isObject(tw.scale)) { return null;