From fcdd4cee4d2064dd9c95c289f2f94346d7251b9e Mon Sep 17 00:00:00 2001 From: Xin Hao Zhang Date: Tue, 4 Apr 2023 16:06:44 -0400 Subject: [PATCH] cluster-ui: fix cached data invalidation on timescale change In a prior change, we moved the invalidation of cached data depending on the timescale to the local storage saga for CC. This was so invaldiation would occur after updating the cache. The local storage saga created for the time scale action was not hooked up to fire after the action, thus the data would not have been invalidated. This commit properly subscribes the saga to the update time scale action in CC. In addition, the imported constant `$ internal` used in some files in the api module, was moved to use the import from `util/constants` over `recentExecutions` for better import hygiene. Epic: none Release note: None --- .../cluster-ui/src/api/stmtInsightsApi.ts | 2 +- .../cluster-ui/src/api/txnInsightsApi.ts | 2 +- .../src/indexDetailsPage/indexDetailsPage.tsx | 2 +- .../localStorage/localStorage.reducer.ts | 2 +- .../localStorage/localStorage.saga.spec.ts | 51 +++++++++++++++++++ .../store/localStorage/localStorage.saga.ts | 19 +++++-- .../cluster-ui/src/store/utils/selectors.ts | 2 +- .../cluster-ui/src/util/constants.ts | 2 + 8 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.saga.spec.ts diff --git a/pkg/ui/workspaces/cluster-ui/src/api/stmtInsightsApi.ts b/pkg/ui/workspaces/cluster-ui/src/api/stmtInsightsApi.ts index 8eec4d8765e5..7b5d303a06e8 100644 --- a/pkg/ui/workspaces/cluster-ui/src/api/stmtInsightsApi.ts +++ b/pkg/ui/workspaces/cluster-ui/src/api/stmtInsightsApi.ts @@ -26,7 +26,7 @@ import { StmtInsightEvent, } from "src/insights"; import moment from "moment"; -import { INTERNAL_APP_NAME_PREFIX } from "src/recentExecutions/recentStatementUtils"; +import { INTERNAL_APP_NAME_PREFIX } from "src/util/constants"; import { FixFingerprintHexValue } from "../util"; import { getContentionDetailsApi } from "./contentionApi"; diff --git a/pkg/ui/workspaces/cluster-ui/src/api/txnInsightsApi.ts b/pkg/ui/workspaces/cluster-ui/src/api/txnInsightsApi.ts index 61767edd6e2c..954ade64d19a 100644 --- a/pkg/ui/workspaces/cluster-ui/src/api/txnInsightsApi.ts +++ b/pkg/ui/workspaces/cluster-ui/src/api/txnInsightsApi.ts @@ -38,7 +38,7 @@ import { stmtInsightsByTxnExecutionQuery, StmtInsightsResponseRow, } from "./stmtInsightsApi"; -import { INTERNAL_APP_NAME_PREFIX } from "src/recentExecutions/recentStatementUtils"; +import { INTERNAL_APP_NAME_PREFIX } from "src/util/constants"; import { getContentionDetailsApi } from "./contentionApi"; export const TXN_QUERY_PREVIEW_MAX = 800; diff --git a/pkg/ui/workspaces/cluster-ui/src/indexDetailsPage/indexDetailsPage.tsx b/pkg/ui/workspaces/cluster-ui/src/indexDetailsPage/indexDetailsPage.tsx index 35e3b0b3774b..38ed29b30fec 100644 --- a/pkg/ui/workspaces/cluster-ui/src/indexDetailsPage/indexDetailsPage.tsx +++ b/pkg/ui/workspaces/cluster-ui/src/indexDetailsPage/indexDetailsPage.tsx @@ -74,7 +74,7 @@ import { import { commonStyles } from "../common"; import { Loading } from "src"; import LoadingError from "../sqlActivity/errorComponent"; -import { INTERNAL_APP_NAME_PREFIX } from "../recentExecutions/recentStatementUtils"; +import { INTERNAL_APP_NAME_PREFIX } from "src/util/constants"; import { filteredStatementsData } from "../sqlActivity/util"; const cx = classNames.bind(styles); diff --git a/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.reducer.ts b/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.reducer.ts index 372e89a1a3dc..2b670eec0fe5 100644 --- a/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.reducer.ts +++ b/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.reducer.ts @@ -74,7 +74,7 @@ type Payload = { value: any; }; -type TypedPayload = { +export type TypedPayload = { value: T; }; diff --git a/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.saga.spec.ts b/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.saga.spec.ts new file mode 100644 index 000000000000..3e49a84fe776 --- /dev/null +++ b/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.saga.spec.ts @@ -0,0 +1,51 @@ +// Copyright 2023 The Cockroach Authors. +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +import { expectSaga, testSaga } from "redux-saga-test-plan"; +import { actions } from "./localStorage.reducer"; +import { actions as stmtInsightActions } from "src/store/insights/statementInsights/statementInsights.reducer"; +import { actions as txnInsightActions } from "src/store/insights/transactionInsights/transactionInsights.reducer"; +import { actions as sqlStatsActions } from "src/store/sqlStats/sqlStats.reducer"; +import { actions as txnStatsActions } from "src/store/transactionStats"; +import { + localStorageSaga, + updateLocalStorageItemSaga, + updateTimeScale, +} from "./localStorage.saga"; +import { defaultTimeScaleSelected } from "../../timeScaleDropdown"; +import { takeEvery, takeLatest } from "redux-saga/effects"; + +const ts = defaultTimeScaleSelected; + +describe("local storage sagas", () => { + describe("localStorageSaga", () => { + it("should fork relevant sagas on actions", () => { + testSaga(localStorageSaga) + .next() + .all([ + takeEvery(actions.update, updateLocalStorageItemSaga), + takeLatest(actions.updateTimeScale, updateTimeScale), + ]) + .finish() + .isDone(); + }); + }); + + describe("updateTimeScale", () => { + it("invalidates data depending on timescale ", () => { + return expectSaga(updateTimeScale, actions.updateTimeScale({ value: ts })) + .put(sqlStatsActions.invalidated()) + .put(stmtInsightActions.invalidated()) + .put(txnInsightActions.invalidated()) + .put(txnStatsActions.invalidated()) + .run(); + }); + }); +}); diff --git a/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.saga.ts b/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.saga.ts index 8404ba99f7bf..618a5e4e082d 100644 --- a/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.saga.ts +++ b/pkg/ui/workspaces/cluster-ui/src/store/localStorage/localStorage.saga.ts @@ -10,11 +10,17 @@ import { AnyAction } from "redux"; import { all, call, takeEvery, takeLatest, put } from "redux-saga/effects"; -import { actions } from "./localStorage.reducer"; +import { + actions, + LocalStorageKeys, + TypedPayload, +} from "./localStorage.reducer"; import { actions as sqlStatsActions } from "src/store/sqlStats"; import { actions as stmtInsightActions } from "src/store/insights/statementInsights"; import { actions as txnInsightActions } from "src/store/insights/transactionInsights"; import { actions as txnStatsActions } from "src/store/transactionStats"; +import { PayloadAction } from "@reduxjs/toolkit"; +import { TimeScale } from "src/timeScaleDropdown"; export function* updateLocalStorageItemSaga(action: AnyAction) { const { key, value } = action.payload; @@ -25,18 +31,25 @@ export function* updateLocalStorageItemSaga(action: AnyAction) { ); } -export function* updateTimeScale(action: AnyAction) { +export function* updateTimeScale( + action: PayloadAction>, +) { yield all([ put(sqlStatsActions.invalidated()), put(stmtInsightActions.invalidated()), put(txnInsightActions.invalidated()), put(txnStatsActions.invalidated()), ]); + yield call( + { context: localStorage, fn: localStorage.setItem }, + LocalStorageKeys.GLOBAL_TIME_SCALE, + JSON.stringify(action.payload?.value), + ); } export function* localStorageSaga() { yield all([ takeEvery(actions.update, updateLocalStorageItemSaga), - takeLatest(actions.updateTimeScale, updateLocalStorageItemSaga), + takeLatest(actions.updateTimeScale, updateTimeScale), ]); } diff --git a/pkg/ui/workspaces/cluster-ui/src/store/utils/selectors.ts b/pkg/ui/workspaces/cluster-ui/src/store/utils/selectors.ts index e74072454513..add19012a7b7 100644 --- a/pkg/ui/workspaces/cluster-ui/src/store/utils/selectors.ts +++ b/pkg/ui/workspaces/cluster-ui/src/store/utils/selectors.ts @@ -9,7 +9,7 @@ // licenses/APL.txt. import { createSelector } from "reselect"; -import { LocalStorageKeys } from "../localStorage"; +import { LocalStorageKeys } from "src/store/localStorage/localStorage.reducer"; import { AppState } from "../reducers"; export const adminUISelector = createSelector( diff --git a/pkg/ui/workspaces/cluster-ui/src/util/constants.ts b/pkg/ui/workspaces/cluster-ui/src/util/constants.ts index 43d9e21b0706..a6ad8e301aae 100644 --- a/pkg/ui/workspaces/cluster-ui/src/util/constants.ts +++ b/pkg/ui/workspaces/cluster-ui/src/util/constants.ts @@ -47,3 +47,5 @@ export const serverToClientErrorMessageMap = new Map([ ]); export const NO_SAMPLES_FOUND = "no samples"; + +export const INTERNAL_APP_NAME_PREFIX = "$ internal";