From a432d7fcde0ed6357c2e489dea124db3aa3dda01 Mon Sep 17 00:00:00 2001 From: Marylia Gutierrez Date: Thu, 3 Mar 2022 19:05:57 -0500 Subject: [PATCH] ui: add selected period as part of cached key Previously, the fingerprint id and the app names were used as a key for a statement details cache. This commits adds the start and end time (when existing) to the key, so the details are correctly assigned to the selected period. This commit also rounds the selected value period to the hour, since that is what is used on the persisted statistics, with the start value keeping the hour and the end value adding one hour, for example: start: 17:45:23 -> 17:00:00 end: 20:14:32 -> 21:00:00 Partially addresses #72129 Release note: None Release Justification: Low risk, high benefit change --- .../statementDetails.selectors.ts | 39 +-- .../src/statementDetails/statementDetails.tsx | 4 +- .../statementDetails.sagas.spec.ts | 6 +- .../statementDetails.sagas.ts | 4 +- .../src/timeScaleDropdown/timescale.spec.tsx | 32 ++- .../cluster-ui/src/timeScaleDropdown/utils.ts | 12 + .../cluster-ui/src/util/appStats/appStats.ts | 37 +++ .../cluster-ui/src/util/dataFromServer.ts | 3 + pkg/ui/workspaces/db-console/package.json | 1 + .../db-console/src/redux/apiReducers.ts | 30 +-- .../src/views/statements/statementDetails.tsx | 18 +- .../src/views/statements/statements.spec.tsx | 231 ++++++++++++------ 12 files changed, 277 insertions(+), 140 deletions(-) diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.selectors.ts b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.selectors.ts index 307d2e98f5fb..5175fff0b3ea 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.selectors.ts +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.selectors.ts @@ -8,6 +8,7 @@ // by the Apache License, Version 2.0, included in the file // licenses/APL.txt. +import Long from "long"; import { createSelector } from "@reduxjs/toolkit"; import { RouteComponentProps } from "react-router-dom"; import { AppState } from "../store"; @@ -16,47 +17,33 @@ import { statementAttr, getMatchParamByName, queryByName, + generateStmtDetailsToID, } from "../util"; import { cockroach } from "@cockroachlabs/crdb-protobuf-client"; +import { TimeScale, toRoundedDateRange } from "../timeScaleDropdown"; +import { selectTimeScale } from "../statementsPage/statementsPage.selectors"; type StatementDetailsResponseMessage = cockroach.server.serverpb.StatementDetailsResponse; -export const generateStmtDetailsToID = ( - fingerprintID: string, - appNames: string, -): string => { - if ( - appNames && - (appNames.includes("$ internal") || appNames.includes("unset")) - ) { - const apps = appNames.split(","); - for (let i = 0; i < apps.length; i++) { - if (apps[i].includes("$ internal")) { - apps[i] = "$ internal"; - } - if (apps[i].includes("unset")) { - apps[i] = ""; - } - } - appNames = apps.toString(); - } - if (appNames) { - return fingerprintID + appNames; - } - return fingerprintID; -}; - export const selectStatementDetails = createSelector( (_state: AppState, props: RouteComponentProps): string => getMatchParamByName(props.match, statementAttr), (_state: AppState, props: RouteComponentProps): string => queryByName(props.location, appNamesAttr), + (state: AppState): TimeScale => selectTimeScale(state), (state: AppState) => state.adminUI.sqlDetailsStats, ( fingerprintID, appNames, + timeScale, statementDetailsStats, ): StatementDetailsResponseMessage => { - const key = generateStmtDetailsToID(fingerprintID, appNames); + const [start, end] = toRoundedDateRange(timeScale); + const key = generateStmtDetailsToID( + fingerprintID, + appNames, + Long.fromNumber(start.unix()), + Long.fromNumber(end.unix()), + ); if (Object.keys(statementDetailsStats).includes(key)) { return statementDetailsStats[key].data; } diff --git a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx index 83967cec098c..ba04259b7d57 100644 --- a/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/statementDetails/statementDetails.tsx @@ -56,7 +56,7 @@ import { commonStyles } from "src/common"; import { NodeSummaryStats } from "../nodes"; import { UIConfigState } from "../store"; import moment from "moment"; -import { TimeScale, toDateRange } from "../timeScaleDropdown"; +import { TimeScale, toRoundedDateRange } from "../timeScaleDropdown"; import { StatementDetailsRequest } from "src/api/statementsApi"; import SQLActivityError from "../sqlActivity/errorComponent"; import { @@ -162,7 +162,7 @@ const summaryCardStylesCx = classNames.bind(summaryCardStyles); function statementDetailsRequestFromProps( props: StatementDetailsProps, ): cockroach.server.serverpb.StatementDetailsRequest { - const [start, end] = toDateRange(props.timeScale); + const [start, end] = toRoundedDateRange(props.timeScale); const statementFingerprintID = getMatchParamByName( props.match, statementAttr, diff --git a/pkg/ui/workspaces/cluster-ui/src/store/statementDetails/statementDetails.sagas.spec.ts b/pkg/ui/workspaces/cluster-ui/src/store/statementDetails/statementDetails.sagas.spec.ts index 5465ee5f55e8..e055f0a152ab 100644 --- a/pkg/ui/workspaces/cluster-ui/src/store/statementDetails/statementDetails.sagas.spec.ts +++ b/pkg/ui/workspaces/cluster-ui/src/store/statementDetails/statementDetails.sagas.spec.ts @@ -39,7 +39,7 @@ describe("SQLDetailsStats sagas", () => { type: "request", }; const key = - "SELECT * FROM crdb_internal.node_build_info$ cockroach sql,newname"; + "SELECT * FROM crdb_internal.node_build_info/$ cockroach sql,newname/0/0"; const SQLDetailsStatsResponse = new cockroach.server.serverpb.StatementDetailsResponse( { statement: { @@ -659,7 +659,7 @@ describe("SQLDetailsStats sagas", () => { ) .withReducer(reducer) .hasFinalState>({ - "SELECT * FROM crdb_internal.node_build_info$ cockroach sql,newname": { + "SELECT * FROM crdb_internal.node_build_info/$ cockroach sql,newname/0/0": { data: SQLDetailsStatsResponse, lastError: null, valid: true, @@ -680,7 +680,7 @@ describe("SQLDetailsStats sagas", () => { ) .withReducer(reducer) .hasFinalState>({ - "SELECT * FROM crdb_internal.node_build_info$ cockroach sql,newname": { + "SELECT * FROM crdb_internal.node_build_info/$ cockroach sql,newname/0/0": { data: null, lastError: error, valid: false, diff --git a/pkg/ui/workspaces/cluster-ui/src/store/statementDetails/statementDetails.sagas.ts b/pkg/ui/workspaces/cluster-ui/src/store/statementDetails/statementDetails.sagas.ts index cc0c95560a0a..e5d8bb6612f6 100644 --- a/pkg/ui/workspaces/cluster-ui/src/store/statementDetails/statementDetails.sagas.ts +++ b/pkg/ui/workspaces/cluster-ui/src/store/statementDetails/statementDetails.sagas.ts @@ -18,7 +18,7 @@ import { } from "src/api/statementsApi"; import { actions as sqlDetailsStatsActions } from "./statementDetails.reducer"; import { CACHE_INVALIDATION_PERIOD } from "src/store/utils"; -import { generateStmtDetailsToID } from "../../statementDetails/statementDetails.selectors"; +import { generateStmtDetailsToID } from "../../util"; export function* refreshSQLDetailsStatsSaga( action: PayloadAction, @@ -33,6 +33,8 @@ export function* requestSQLDetailsStatsSaga( ? generateStmtDetailsToID( action.payload.fingerprint_id, action.payload.app_names.toString(), + action.payload.start, + action.payload.end, ) : ""; try { 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 a6f517de3590..56be93c30667 100644 --- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timescale.spec.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/timescale.spec.tsx @@ -18,7 +18,11 @@ import { TimeScaleDropdownProps, TimeScaleDropdown, } from "./timeScaleDropdown"; -import { defaultTimeScaleOptions, findClosestTimeScale } from "./utils"; +import { + defaultTimeScaleOptions, + findClosestTimeScale, + toRoundedDateRange, +} from "./utils"; import * as timescale from "./timeScaleTypes"; import moment from "moment"; import { MemoryRouter } from "react-router"; @@ -204,6 +208,32 @@ describe("", function() { }); describe("timescale utils", (): void => { + describe("toRoundedDateRange", () => { + it("round values", () => { + const ts: TimeScale = { + windowSize: moment.duration(5, "day"), + sampleSize: moment.duration(5, "minutes"), + fixedWindowEnd: moment.utc("2022.01.10 13:42"), + key: "Custom", + }; + const [start, end] = toRoundedDateRange(ts); + assert.equal(start.format("YYYY.MM.DD HH:mm:ss"), "2022.01.05 13:00:00"); + assert.equal(end.format("YYYY.MM.DD HH:mm:ss"), "2022.01.10 14:00:00"); + }); + + it("already rounded values", () => { + const ts: TimeScale = { + windowSize: moment.duration(5, "day"), + sampleSize: moment.duration(5, "minutes"), + fixedWindowEnd: moment.utc("2022.01.10 13:00"), + key: "Custom", + }; + const [start, end] = toRoundedDateRange(ts); + assert.equal(start.format("YYYY.MM.DD HH:mm:ss"), "2022.01.05 13:00:00"); + assert.equal(end.format("YYYY.MM.DD HH:mm:ss"), "2022.01.10 14:00:00"); + }); + }); + describe("findClosestTimeScale", () => { it("should find the correct time scale", () => { // `seconds` != window size of any of the default options, `startSeconds` not specified. diff --git a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts index a3255b266db1..5b44eebc4df9 100644 --- a/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts +++ b/pkg/ui/workspaces/cluster-ui/src/timeScaleDropdown/utils.ts @@ -87,6 +87,18 @@ export const toDateRange = (ts: TimeScale): [moment.Moment, moment.Moment] => { return [start, end]; }; +export const toRoundedDateRange = ( + ts: TimeScale, +): [moment.Moment, moment.Moment] => { + const [start, end] = toDateRange(ts); + const startRounded = start.set({ minute: 0, second: 0, millisecond: 0 }); + const endRounded = end + .set({ minute: 0, second: 0, millisecond: 0 }) + .add(1, "hours"); + + return [startRounded, endRounded]; +}; + export const findClosestTimeScale = ( options: TimeScaleOptions, seconds: number, diff --git a/pkg/ui/workspaces/cluster-ui/src/util/appStats/appStats.ts b/pkg/ui/workspaces/cluster-ui/src/util/appStats/appStats.ts index 57a3565dff64..70fa99c20ceb 100644 --- a/pkg/ui/workspaces/cluster-ui/src/util/appStats/appStats.ts +++ b/pkg/ui/workspaces/cluster-ui/src/util/appStats/appStats.ts @@ -17,6 +17,7 @@ import { uniqueLong, unique, } from "src/util"; +import Long from "long"; export type StatementStatistics = protos.cockroach.sql.IStatementStatistics; export type ExecStats = protos.cockroach.sql.IExecStats; @@ -283,3 +284,39 @@ export function transactionScopedStatementKey( ): string { return statementKey(stmt) + stmt.transaction_fingerprint_id.toString(); } + +export const generateStmtDetailsToID = ( + fingerprintID: string, + appNames: string, + start: Long, + end: Long, +): string => { + if ( + appNames && + (appNames.includes("$ internal") || appNames.includes("unset")) + ) { + const apps = appNames.split(","); + for (let i = 0; i < apps.length; i++) { + if (apps[i].includes("$ internal")) { + apps[i] = "$ internal"; + } + if (apps[i].includes("unset")) { + apps[i] = ""; + } + } + appNames = unique(apps) + .sort() + .toString(); + } + let generatedID = fingerprintID; + if (appNames) { + generatedID += `/${appNames}`; + } + if (start) { + generatedID += `/${start}`; + } + if (end) { + generatedID += `/${end}`; + } + return generatedID; +}; diff --git a/pkg/ui/workspaces/cluster-ui/src/util/dataFromServer.ts b/pkg/ui/workspaces/cluster-ui/src/util/dataFromServer.ts index e73b26fd5f17..17fca0c36b0c 100644 --- a/pkg/ui/workspaces/cluster-ui/src/util/dataFromServer.ts +++ b/pkg/ui/workspaces/cluster-ui/src/util/dataFromServer.ts @@ -15,6 +15,9 @@ export interface DataFromServer { Tag: string; Version: string; NodeID: string; + OIDCAutoLogin: boolean; + OIDCLoginEnabled: boolean; + OIDCButtonText: string; } // Tell TypeScript about `window.dataFromServer`, which is set in a script diff --git a/pkg/ui/workspaces/db-console/package.json b/pkg/ui/workspaces/db-console/package.json index 600d687f10b7..41888b7f17c8 100644 --- a/pkg/ui/workspaces/db-console/package.json +++ b/pkg/ui/workspaces/db-console/package.json @@ -80,6 +80,7 @@ "@types/fetch-mock": "^5.8.1", "@types/highlight.js": "^10.1.0", "@types/lodash": "^4.14.149", + "@types/long": "^4.0.1", "@types/mocha": "^2.2.40", "@types/node": "^13.7.0", "@types/nvd3": "^1.8.36", diff --git a/pkg/ui/workspaces/db-console/src/redux/apiReducers.ts b/pkg/ui/workspaces/db-console/src/redux/apiReducers.ts index b93ee5001784..5b968f287277 100644 --- a/pkg/ui/workspaces/db-console/src/redux/apiReducers.ts +++ b/pkg/ui/workspaces/db-console/src/redux/apiReducers.ts @@ -11,6 +11,8 @@ import _ from "lodash"; import { combineReducers } from "redux"; import moment from "moment"; +import { util } from "@cockroachlabs/cluster-ui"; +const { generateStmtDetailsToID } = util; import { CachedDataReducer, @@ -314,40 +316,16 @@ const queriesReducerObj = new CachedDataReducer( export const invalidateStatements = queriesReducerObj.invalidateData; export const refreshStatements = queriesReducerObj.refresh; -// TODO (maryliag) add period selected to generate ID export const statementDetailsRequestToID = ( req: api.StatementDetailsRequestMessage, ): string => generateStmtDetailsToID( req.fingerprint_id.toString(), req.app_names.toString(), + req.start, + req.end, ); -export const generateStmtDetailsToID = ( - fingerprintID: string, - appNames: string, -): string => { - if ( - appNames && - (appNames.includes("$ internal") || appNames.includes("unset")) - ) { - const apps = appNames.split(","); - for (let i = 0; i < apps.length; i++) { - if (apps[i].includes("$ internal")) { - apps[i] = "$ internal"; - } - if (apps[i].includes("unset")) { - apps[i] = ""; - } - } - appNames = apps.toString(); - } - if (appNames) { - return fingerprintID + appNames; - } - return fingerprintID; -}; - const queryReducerObj = new KeyedCachedDataReducer( api.getStatementDetails, "statementDetails", diff --git a/pkg/ui/workspaces/db-console/src/views/statements/statementDetails.tsx b/pkg/ui/workspaces/db-console/src/views/statements/statementDetails.tsx index 6b82bda0f948..28da4cfcd4e9 100644 --- a/pkg/ui/workspaces/db-console/src/views/statements/statementDetails.tsx +++ b/pkg/ui/workspaces/db-console/src/views/statements/statementDetails.tsx @@ -10,6 +10,7 @@ import { connect } from "react-redux"; import { withRouter } from "react-router-dom"; import { createSelector } from "reselect"; +import Long from "long"; import { refreshLiveness, @@ -17,7 +18,6 @@ import { refreshStatementDiagnosticsRequests, refreshStatementDetails, refreshUserSQLRoles, - generateStmtDetailsToID, } from "src/redux/apiReducers"; import { RouteComponentProps } from "react-router"; import { @@ -30,6 +30,9 @@ import { StatementDetails, StatementDetailsDispatchProps, StatementDetailsStateProps, + TimeScale, + toRoundedDateRange, + util, } from "@cockroachlabs/cluster-ui"; import { cancelStatementDiagnosticsReportAction, @@ -50,18 +53,29 @@ import { getMatchParamByName, queryByName } from "src/util/query"; import { appNamesAttr, statementAttr } from "src/util/constants"; type IStatementDiagnosticsReport = protos.cockroach.server.serverpb.IStatementDiagnosticsReport; +const { generateStmtDetailsToID } = util; + export const selectStatementDetails = createSelector( (_state: AdminUIState, props: RouteComponentProps): string => getMatchParamByName(props.match, statementAttr), (_state: AdminUIState, props: RouteComponentProps): string => queryByName(props.location, appNamesAttr), + (state: AdminUIState): TimeScale => + statementsTimeScaleLocalSetting.selector(state), (state: AdminUIState) => state.cachedData.statementDetails, ( fingerprintID, appNames, + timeScale, statementDetailsStats, ): StatementDetailsResponseMessage => { - const key = generateStmtDetailsToID(fingerprintID, appNames); + const [start, end] = toRoundedDateRange(timeScale); + const key = generateStmtDetailsToID( + fingerprintID, + appNames, + Long.fromNumber(start.unix()), + Long.fromNumber(end.unix()), + ); if (Object.keys(statementDetailsStats).includes(key)) { return statementDetailsStats[key].data; } diff --git a/pkg/ui/workspaces/db-console/src/views/statements/statements.spec.tsx b/pkg/ui/workspaces/db-console/src/views/statements/statements.spec.tsx index d6ebea81b9ab..ff3562749b77 100644 --- a/pkg/ui/workspaces/db-console/src/views/statements/statements.spec.tsx +++ b/pkg/ui/workspaces/db-console/src/views/statements/statements.spec.tsx @@ -28,7 +28,8 @@ import { selectStatementDetails } from "./statementDetails"; import ISensitiveInfo = protos.cockroach.sql.ISensitiveInfo; import { AdminUIState, createAdminUIStore } from "src/redux/state"; import { util } from "@cockroachlabs/cluster-ui"; -import { generateStmtDetailsToID } from "src/redux/apiReducers"; + +const { generateStmtDetailsToID } = util; type CollectedStatementStatistics = util.CollectedStatementStatistics; type ExecStats = util.ExecStats; @@ -36,6 +37,11 @@ type StatementStatistics = util.StatementStatistics; const INTERNAL_STATEMENT_PREFIX = "$ internal"; +const timePeriod = { + start: new Long(1646250884), + end: new Long(1646337284), +}; + describe("selectStatements", () => { it("returns null if the statements data is invalid", () => { const state = makeInvalidState(); @@ -50,7 +56,11 @@ describe("selectStatements", () => { const stmtA = makeFingerprint(1); const stmtB = makeFingerprint(2, "foobar"); const stmtC = makeFingerprint(3, "another"); - const state = makeStateWithStatements([stmtA, stmtB, stmtC]); + const state = makeStateWithStatements( + [stmtA, stmtB, stmtC], + timePeriod.start, + timePeriod.end, + ); const props = makeEmptyRouteProps(); const result = selectStatements(state, props); @@ -70,7 +80,11 @@ describe("selectStatements", () => { const stmtB = makeFingerprint(2, INTERNAL_STATEMENT_PREFIX); const stmtC = makeFingerprint(3, INTERNAL_STATEMENT_PREFIX); const stmtD = makeFingerprint(3, "another"); - const state = makeStateWithStatements([stmtA, stmtB, stmtC, stmtD]); + const state = makeStateWithStatements( + [stmtA, stmtB, stmtC, stmtD], + timePeriod.start, + timePeriod.end, + ); const props = makeEmptyRouteProps(); const result = selectStatements(state, props); @@ -85,7 +99,11 @@ describe("selectStatements", () => { const sumCount = stmtA.stats.count .add(stmtB.stats.count.add(stmtC.stats.count)) .toNumber(); - const state = makeStateWithStatements([stmtA, stmtB, stmtC]); + const state = makeStateWithStatements( + [stmtA, stmtB, stmtC], + timePeriod.start, + timePeriod.end, + ); const props = makeEmptyRouteProps(); const result = selectStatements(state, props); @@ -96,11 +114,15 @@ describe("selectStatements", () => { }); it("coalesces statements with differing node ids", () => { - const state = makeStateWithStatements([ - makeFingerprint(1, "", 1), - makeFingerprint(1, "", 2), - makeFingerprint(1, "", 3), - ]); + const state = makeStateWithStatements( + [ + makeFingerprint(1, "", 1), + makeFingerprint(1, "", 2), + makeFingerprint(1, "", 3), + ], + timePeriod.start, + timePeriod.end, + ); const props = makeEmptyRouteProps(); const result = selectStatements(state, props); @@ -109,12 +131,16 @@ describe("selectStatements", () => { }); it("coalesces statements with differing distSQL and failed values", () => { - const state = makeStateWithStatements([ - makeFingerprint(1, "", 1, false, false), - makeFingerprint(1, "", 1, false, true), - makeFingerprint(1, "", 1, true, false), - makeFingerprint(1, "", 1, true, true), - ]); + const state = makeStateWithStatements( + [ + makeFingerprint(1, "", 1, false, false), + makeFingerprint(1, "", 1, false, true), + makeFingerprint(1, "", 1, true, false), + makeFingerprint(1, "", 1, true, true), + ], + timePeriod.start, + timePeriod.end, + ); const props = makeEmptyRouteProps(); const result = selectStatements(state, props); @@ -123,11 +149,15 @@ describe("selectStatements", () => { }); it("filters out statements when app param is set", () => { - const state = makeStateWithStatements([ - makeFingerprint(1, "foo"), - makeFingerprint(2, "bar"), - makeFingerprint(3, "baz"), - ]); + const state = makeStateWithStatements( + [ + makeFingerprint(1, "foo"), + makeFingerprint(2, "bar"), + makeFingerprint(3, "baz"), + ], + timePeriod.start, + timePeriod.end, + ); const props = makeRoutePropsWithApp("foo"); const result = selectStatements(state, props); @@ -136,11 +166,15 @@ describe("selectStatements", () => { }); it('filters out statements with app set when app param is "(unset)"', () => { - const state = makeStateWithStatements([ - makeFingerprint(1, ""), - makeFingerprint(2, "bar"), - makeFingerprint(3, "baz"), - ]); + const state = makeStateWithStatements( + [ + makeFingerprint(1, ""), + makeFingerprint(2, "bar"), + makeFingerprint(3, "baz"), + ], + timePeriod.start, + timePeriod.end, + ); const props = makeRoutePropsWithApp("(unset)"); const result = selectStatements(state, props); @@ -149,11 +183,15 @@ describe("selectStatements", () => { }); it('filters out statements with app set when app param is "$ internal"', () => { - const state = makeStateWithStatements([ - makeFingerprint(1, "$ internal_stmnt_app"), - makeFingerprint(2, "bar"), - makeFingerprint(3, "baz"), - ]); + const state = makeStateWithStatements( + [ + makeFingerprint(1, "$ internal_stmnt_app"), + makeFingerprint(2, "bar"), + makeFingerprint(3, "baz"), + ], + timePeriod.start, + timePeriod.end, + ); const props = makeRoutePropsWithApp("$ internal"); const result = selectStatements(state, props); assert.equal(result.length, 1); @@ -170,12 +208,16 @@ describe("selectApps", () => { }); it("returns all the apps that appear in the statements", () => { - const state = makeStateWithStatements([ - makeFingerprint(1), - makeFingerprint(1, "foobar"), - makeFingerprint(2, "foobar"), - makeFingerprint(3, "cockroach sql"), - ]); + const state = makeStateWithStatements( + [ + makeFingerprint(1), + makeFingerprint(1, "foobar"), + makeFingerprint(2, "foobar"), + makeFingerprint(3, "cockroach sql"), + ], + timePeriod.start, + timePeriod.end, + ); const result = selectApps(state); @@ -193,11 +235,11 @@ describe("selectTotalFingerprints", () => { }); it("returns the number of statement fingerprints", () => { - const state = makeStateWithStatements([ - makeFingerprint(1), - makeFingerprint(2), - makeFingerprint(3), - ]); + const state = makeStateWithStatements( + [makeFingerprint(1), makeFingerprint(2), makeFingerprint(3)], + timePeriod.start, + timePeriod.end, + ); const result = selectTotalFingerprints(state); @@ -205,11 +247,15 @@ describe("selectTotalFingerprints", () => { }); it("coalesces statements from different apps", () => { - const state = makeStateWithStatements([ - makeFingerprint(1), - makeFingerprint(1, "foobar"), - makeFingerprint(1, "another"), - ]); + const state = makeStateWithStatements( + [ + makeFingerprint(1), + makeFingerprint(1, "foobar"), + makeFingerprint(1, "another"), + ], + timePeriod.start, + timePeriod.end, + ); const result = selectTotalFingerprints(state); @@ -217,11 +263,15 @@ describe("selectTotalFingerprints", () => { }); it("coalesces statements with differing node ids", () => { - const state = makeStateWithStatements([ - makeFingerprint(1, "", 1), - makeFingerprint(1, "", 2), - makeFingerprint(1, "", 3), - ]); + const state = makeStateWithStatements( + [ + makeFingerprint(1, "", 1), + makeFingerprint(1, "", 2), + makeFingerprint(1, "", 3), + ], + timePeriod.start, + timePeriod.end, + ); const result = selectTotalFingerprints(state); @@ -229,12 +279,16 @@ describe("selectTotalFingerprints", () => { }); it("coalesces statements with differing distSQL and failed keys", () => { - const state = makeStateWithStatements([ - makeFingerprint(1, "", 1, false, false), - makeFingerprint(1, "", 1, false, true), - makeFingerprint(1, "", 1, true, false), - makeFingerprint(1, "", 1, true, true), - ]); + const state = makeStateWithStatements( + [ + makeFingerprint(1, "", 1, false, false), + makeFingerprint(1, "", 1, false, true), + makeFingerprint(1, "", 1, true, false), + makeFingerprint(1, "", 1, true, true), + ], + timePeriod.start, + timePeriod.end, + ); const result = selectTotalFingerprints(state); @@ -253,7 +307,11 @@ describe("selectLastReset", () => { it("returns the formatted timestamp if valid", () => { const timestamp = 92951700; - const state = makeStateWithLastReset(timestamp); + const state = makeStateWithLastReset( + timestamp, + timePeriod.start, + timePeriod.end, + ); const result = selectLastReset(state); @@ -275,7 +333,11 @@ describe("selectStatement", () => { const stmtA = makeFingerprint(1); const stmtB = makeFingerprint(2, "foobar"); const stmtC = makeFingerprint(3, "another"); - const state = makeStateWithStatements([stmtA, stmtB, stmtC]); + const state = makeStateWithStatements( + [stmtA, stmtB, stmtC], + timePeriod.start, + timePeriod.end, + ); const stmtAFingerprintID = stmtA.id.toString(); const props = makeRoutePropsWithStatement(stmtAFingerprintID); @@ -292,11 +354,11 @@ describe("selectStatement", () => { it("filters out statements when app param is set", () => { const stmtA = makeFingerprint(1, "foo"); - const state = makeStateWithStatements([ - stmtA, - makeFingerprint(2, "bar"), - makeFingerprint(3, "baz"), - ]); + const state = makeStateWithStatements( + [stmtA, makeFingerprint(2, "bar"), makeFingerprint(3, "baz")], + timePeriod.start, + timePeriod.end, + ); const stmtAFingerprintID = stmtA.id.toString(); const props = makeRoutePropsWithStatementAndApp(stmtAFingerprintID, "foo"); @@ -313,11 +375,11 @@ describe("selectStatement", () => { it('filters out statements with app set when app param is "(unset)"', () => { const stmtA = makeFingerprint(1, ""); - const state = makeStateWithStatements([ - stmtA, - makeFingerprint(2, "bar"), - makeFingerprint(3, "baz"), - ]); + const state = makeStateWithStatements( + [stmtA, makeFingerprint(2, "bar"), makeFingerprint(3, "baz")], + timePeriod.start, + timePeriod.end, + ); const stmtAFingerprintID = stmtA.id.toString(); const props = makeRoutePropsWithStatementAndApp( stmtAFingerprintID, @@ -337,11 +399,11 @@ describe("selectStatement", () => { it('filters out statements with app set when app param is "$ internal"', () => { const stmtA = makeFingerprint(1, "$ internal_stmnt_app"); - const state = makeStateWithStatements([ - stmtA, - makeFingerprint(2, "bar"), - makeFingerprint(3, "baz"), - ]); + const state = makeStateWithStatements( + [stmtA, makeFingerprint(2, "bar"), makeFingerprint(3, "baz")], + timePeriod.start, + timePeriod.end, + ); const stmtAFingerprintID = stmtA.id.toString(); const props = makeRoutePropsWithStatementAndApp( stmtAFingerprintID, @@ -459,6 +521,8 @@ function makeInvalidState(): AdminUIState { function makeStateWithStatementsAndLastReset( statements: CollectedStatementStatistics[], lastReset: number, + start: Long, + end: Long, ) { const store = createAdminUIStore(H.createMemoryHistory()); const state = merge(store.getState(), { @@ -485,7 +549,12 @@ function makeStateWithStatementsAndLastReset( for (const stmt of statements) { state.cachedData.statementDetails[ - generateStmtDetailsToID(stmt.id.toString(), stmt.key.key_data.app) + generateStmtDetailsToID( + stmt.id.toString(), + stmt.key.key_data.app, + start, + end, + ) ] = { data: protos.cockroach.server.serverpb.StatementDetailsResponse.fromObject( { @@ -508,12 +577,16 @@ function makeStateWithStatementsAndLastReset( return state; } -function makeStateWithStatements(statements: CollectedStatementStatistics[]) { - return makeStateWithStatementsAndLastReset(statements, 0); +function makeStateWithStatements( + statements: CollectedStatementStatistics[], + start: Long, + end: Long, +) { + return makeStateWithStatementsAndLastReset(statements, 0, start, end); } -function makeStateWithLastReset(lastReset: number) { - return makeStateWithStatementsAndLastReset([], lastReset); +function makeStateWithLastReset(lastReset: number, start: Long, end: Long) { + return makeStateWithStatementsAndLastReset([], lastReset, start, end); } function makeRoutePropsWithParams(params: { [key: string]: string }) {