Revert "[perf] Performance Improvements"
This reverts commit 20d0b632c7c70238b6fef1d6119a3f20a28fb70a.
Reason for revert: Seeing issues with dataframe, going to revert and investigate
Original change's description:
> [perf] Performance Improvements
>
> Refactor ExploreMultiSk to improve graph management and data loading efficiency.
>
> - Fix to correctly reset exploreElements, resolving memory leak and state issues with graph splitting.
> - Use allGraphConfigs for centralized configuration management.
> - Optimize graph rendering and data fetching coordination via pagination.
>
> Change-Id: I10c745f387af59d6f012b4b0c39447e83e7d92f8
> Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/1037197
> Reviewed-by: Farid (Mojtaba) Faridzad <faridzad@google.com>
> Commit-Queue: Tony Seaward <seawardt@google.com>
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Change-Id: I691a55488d764cdf5bbee000f51f8b5b367c5cd4
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/1120456
Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
Commit-Queue: Tony Seaward <seawardt@google.com>
diff --git a/.gitignore b/.gitignore
index 4500eb1..31394ab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,4 +123,3 @@
# Dotenv
.env
-tsconfig.json
diff --git a/perf/modules/alerts-page-sk/alerts-page-sk.ts b/perf/modules/alerts-page-sk/alerts-page-sk.ts
index 723d6c6..c9b13eb 100644
--- a/perf/modules/alerts-page-sk/alerts-page-sk.ts
+++ b/perf/modules/alerts-page-sk/alerts-page-sk.ts
@@ -244,7 +244,7 @@
}
private startEditing(cfg: Alert) {
- this.origCfg = structuredClone(this._cfg);
+ this.origCfg = JSON.parse(JSON.stringify(this._cfg)) as Alert;
this.cfg = cfg;
this.dialog!.showModal();
}
@@ -291,7 +291,7 @@
}
set cfg(val) {
- this._cfg = structuredClone(val);
+ this._cfg = JSON.parse(JSON.stringify(val)) as Alert;
if (this._cfg && !this._cfg.owner) {
this._cfg.owner = this.email;
}
diff --git a/perf/modules/dataframe/dataframe_context.ts b/perf/modules/dataframe/dataframe_context.ts
index 120b016..d3d1757 100644
--- a/perf/modules/dataframe/dataframe_context.ts
+++ b/perf/modules/dataframe/dataframe_context.ts
@@ -39,8 +39,6 @@
import { formatSpecialFunctions } from '../paramtools';
import { MISSING_DATA_SENTINEL } from '../const/const';
-const MAX_DATAPOINTS = 5000;
-
// Holds issue data for a single data point.
// x and y corresponds to the location of the data point on the chart
export interface IssueDetail {
@@ -278,21 +276,6 @@
.concat(traceTail[key] || []) as Trace;
});
- if (this._header.length > MAX_DATAPOINTS) {
- const excess = this._header.length - MAX_DATAPOINTS;
- if (isAfter) {
- this._header.splice(0, excess);
- Object.keys(this._traceset).forEach((key) => {
- (this._traceset[key] as number[]).splice(0, excess);
- });
- } else {
- this._header.splice(MAX_DATAPOINTS);
- Object.keys(this._traceset).forEach((key) => {
- (this._traceset[key] as number[]).splice(MAX_DATAPOINTS);
- });
- }
- }
-
if (traceMetadata !== null) {
if (this._traceMetadata === null) {
this._traceMetadata = [];
diff --git a/perf/modules/explore-multi-sk/explore-multi-sk.ts b/perf/modules/explore-multi-sk/explore-multi-sk.ts
index 03f1cbd..1c97219 100644
--- a/perf/modules/explore-multi-sk/explore-multi-sk.ts
+++ b/perf/modules/explore-multi-sk/explore-multi-sk.ts
@@ -113,14 +113,6 @@
}
export class ExploreMultiSk extends ElementSk {
- private allGraphConfigs: GraphConfig[] = [];
-
- private allFrameResponses: FrameResponse[] = [];
-
- private allFrameRequests: FrameRequest[] = [];
-
- private mainGraphSelectedRange: { begin: number; end: number } | null = null;
-
private graphConfigs: GraphConfig[] = [];
private exploreElements: ExploreSimpleSk[] = [];
@@ -290,7 +282,7 @@
}
await load();
- await this.renderCurrentPage();
+ await this.addGraphsToCurrentPage();
// If a key is specified on initial load, we must wait for the
// shortcut's graphs to load their data before we can split them.
if (this.state.splitByKeys.length > 0 && this.exploreElements.length > 0) {
@@ -369,7 +361,7 @@
check();
});
}
- this.renderCurrentPage(false);
+ this.addGraphsToCurrentPage(false);
const query = this.testPicker!.createQueryFromFieldData();
await newExplore.addFromQueryOrFormula(true, 'query', query, '');
} else {
@@ -397,7 +389,7 @@
return;
}
await mainGraph.requestComplete;
- this.renderCurrentPage(false);
+ this.addGraphsToCurrentPage(false);
const CHUNK_SIZE = 5;
const groupdToLoadInChunks = Math.min(this.state.pageSize, groups.length);
@@ -499,7 +491,7 @@
check();
});
}
- this.renderCurrentPage(true);
+ this.addGraphsToCurrentPage(true);
explore = newExplore;
} else {
return;
@@ -902,8 +894,8 @@
traces.forEach((trace) => {
queries.push(queryFromKey(trace));
});
- } else if (this.allGraphConfigs.length > 0) {
- queries.push(...this.allGraphConfigs[0].queries);
+ } else {
+ queries.push(...this.graphConfigs[0].queries);
}
const request: FrameRequest = {
queries: queries,
@@ -1011,24 +1003,16 @@
}
*/
- this.mainGraphSelectedRange = this.exploreElements[0].getSelectedRange();
+ const selectedRange = this.exploreElements[0].getSelectedRange();
// Create the main graph config containing all trace data.
const mainRequest: FrameRequest = this.createFrameRequest();
const mainResponse: FrameResponse = this.createFrameResponse();
- this.allFrameRequests = [mainRequest];
- this.allFrameResponses = [mainResponse];
+ const frameRequests: FrameRequest[] = [mainRequest];
+ const frameResponses: FrameResponse[] = [mainResponse];
this.clearGraphs();
-
- // Add the main graph back so it occupies index 0.
- this.addEmptyGraph();
- // Ensure the main graph config (index 0) has the queries for all data.
- if (this.allGraphConfigs.length > 0) {
- this.allGraphConfigs[0].queries = mainRequest.queries || [];
- }
-
// Create the graph configs for each group.
Array.from(groupedTraces.values()).forEach((traces, i) => {
this.addEmptyGraph();
@@ -1040,13 +1024,31 @@
// Main graph config is always at index 0.
this.graphConfigs[i + 1] = graphConfig;
- this.allFrameRequests.push(exploreRequest);
- this.allFrameResponses.push(exploreResponse);
+ frameRequests.push(exploreRequest);
+ frameResponses.push(exploreResponse);
});
// Now add the graphs that have been configured to the page.
- this.renderCurrentPage();
+ this.addGraphsToCurrentPage(true);
+ const isSplitChart: boolean = this.exploreElements.length > 1;
+ // Limit page size to the number of graphs available.
+ const limit = Math.min(
+ this.state.pageSize + this.state.pageOffset + 1,
+ this.exploreElements.length
+ );
+
+ // If graph is being split, skip the primary graph (index 0), as it contains all the data.
+ // This is to avoid displaying the primary graph in the pagination view.
+ const offset = isSplitChart ? this.state.pageOffset + 1 : 0;
+ for (let i = offset; i < limit; i++) {
+ this.exploreElements[i].UpdateWithFrameResponse(
+ frameResponses[i],
+ frameRequests[i],
+ false,
+ selectedRange
+ );
+ }
if (this.stateHasChanged) {
this.stateHasChanged();
}
@@ -1105,7 +1107,7 @@
const checkTracesets = () => {
const currentTracesets = this.getTracesets();
if (
- currentTracesets.length === this.currentPageExploreElements.length &&
+ currentTracesets.length === this.exploreElements.length &&
currentTracesets.some((ts) => ts.length > 0)
) {
resolve(currentTracesets);
@@ -1137,7 +1139,7 @@
this.testPicker!.populateFieldDataFromParamSet(paramSets, paramSet);
this.testPicker!.setReadOnly(false);
- this.currentPageExploreElements[0].useBrowserURL(false);
+ this.exploreElements[0].useBrowserURL(false);
this.testPicker!.scrollIntoView();
}
@@ -1169,28 +1171,26 @@
const numPages = Math.ceil(this.state.totalGraphs / this.state.pageSize);
const maxValidPageOffset = Math.max(0, (numPages - 1) * this.state.pageSize);
this.state.pageOffset = Math.min(this.state.pageOffset, maxValidPageOffset);
- this.renderCurrentPage();
+ this.addGraphsToCurrentPage(true);
}
this.updateShortcutMultiview();
} else {
- this.state.totalGraphs = this.exploreElements.length;
+ const numElements = this.exploreElements.length;
+ this.state.totalGraphs = numElements > 1 ? numElements - 1 : 1;
if (this.stateHasChanged) this.stateHasChanged();
- this.renderCurrentPage();
+ this.addGraphsToCurrentPage(true);
}
}
private resetGraphs() {
this.emptyCurrentPage();
- this.currentPageExploreElements = [];
- this.allGraphConfigs = [];
this.exploreElements = [];
- this.allFrameRequests = [];
- this.allFrameResponses = [];
+ this.graphConfigs = [];
}
private clearGraphs() {
- this.allGraphConfigs = [];
- this.exploreElements = [];
+ this.exploreElements.splice(1);
+ this.graphConfigs.splice(1);
}
private emptyCurrentPage(): void {
@@ -1199,36 +1199,30 @@
this.currentPageGraphConfigs = [];
}
- private renderCurrentPage(doNotQueryData: boolean = true): void {
- this.state.totalGraphs = this.allGraphConfigs.length > 1 ? this.allGraphConfigs.length - 1 : 1;
+ private addGraphsToCurrentPage(doNotQueryData: boolean = false): void {
+ this.state.totalGraphs = this.exploreElements.length > 1 ? this.exploreElements.length - 1 : 1;
this.emptyCurrentPage();
- let startIndex = this.allGraphConfigs.length > 1 ? this.state.pageOffset : 0;
+ let startIndex = this.exploreElements.length > 1 ? this.state.pageOffset : 0;
- if (this.allGraphConfigs.length > 1) {
+ if (this.exploreElements.length > 1) {
startIndex++;
}
let endIndex = startIndex + this.state.pageSize - 1;
- if (this.allGraphConfigs.length <= endIndex) {
- endIndex = this.allGraphConfigs.length - 1;
+ if (this.exploreElements.length <= endIndex) {
+ endIndex = this.exploreElements.length - 1;
+ }
+
+ for (let i = startIndex; i <= endIndex; i++) {
+ this.currentPageExploreElements.push(this.exploreElements[i]);
+ this.currentPageGraphConfigs.push(this.graphConfigs[i]);
}
const fragment = document.createDocumentFragment();
- for (let i = startIndex; i <= endIndex; i++) {
- const graphConfig = this.allGraphConfigs[i];
- if (!graphConfig) {
- continue;
- }
- const explore = this.createExploreSimpleSk();
- this.currentPageExploreElements.push(explore);
- this.addStateToExplore(explore, graphConfig, doNotQueryData, i);
- fragment.appendChild(explore);
- explore.UpdateWithFrameResponse(
- this.allFrameResponses[i],
- this.allFrameRequests[i],
- false,
- this.mainGraphSelectedRange
- );
- }
+ this.currentPageExploreElements.forEach((elem, i) => {
+ const graphConfig = this.currentPageGraphConfigs[i];
+ this.addStateToExplore(elem, graphConfig, doNotQueryData);
+ fragment.appendChild(elem);
+ });
this.graphDiv!.appendChild(fragment);
this.updateChartHeights();
@@ -1244,14 +1238,12 @@
}
private async syncRange(e: CustomEvent<PlotSelectionEventDetails>): Promise<void> {
- const graphs = this.currentPageExploreElements;
+ const graphs = this.exploreElements;
const offset = e.detail.offsetInSeconds;
const range = e.detail.value;
// It is possible when loading split graphs on start that the first element
// hasnt selected a range yet.
- const selectedRange = this.currentPageExploreElements
- .map((e) => e.getSelectedRange())
- .find((r) => !!r);
+ const selectedRange = this.exploreElements.map((e) => e.getSelectedRange()).find((r) => !!r);
// Sets dataLoading state across all graphs since the main graph is only one doing work.
graphs.forEach((graph, i) => {
@@ -1263,7 +1255,7 @@
// Extend range of primary graph first, so that the other graphs can use
// the updated range when they are updated.
- await this.currentPageExploreElements[0].extendRange(range, offset);
+ await this.exploreElements[0].extendRange(range, offset);
// Once extended, then update each split graph.
graphs.forEach((graph, i) => {
@@ -1288,7 +1280,7 @@
}
private async syncChartSelection(e: CustomEvent<PlotSelectionEventDetails>): Promise<void> {
- const graphs = this.currentPageExploreElements;
+ const graphs = this.exploreElements;
if (!e.detail.value) {
return;
}
@@ -1342,9 +1334,9 @@
private addStateToExplore(
explore: ExploreSimpleSk,
graphConfig: GraphConfig,
- doNotQueryData: boolean,
- index: number
+ doNotQueryData: boolean
) {
+ const index = this.exploreElements.indexOf(explore);
const newState: ExploreState = {
formulas: graphConfig.formulas || [],
queries: graphConfig.queries || [],
@@ -1375,15 +1367,13 @@
hide_paramset: true,
graph_index: index,
doNotQueryData: doNotQueryData,
- dots: this.state.dots,
- autoRefresh: false,
- show_google_plot: this.state.show_google_plot,
};
explore.state = newState;
}
- private createExploreSimpleSk(): ExploreSimpleSk {
+ private addEmptyGraph(unshift?: boolean): ExploreSimpleSk | null {
const explore: ExploreSimpleSk = new ExploreSimpleSk(this.useTestPicker);
+ const graphConfig = new GraphConfig();
explore.defaults = this.defaults;
explore.openQueryByDefault = false;
explore.navOpen = false;
@@ -1391,25 +1381,33 @@
if (this.userEmail) {
explore.user = this.userEmail;
}
+ if (unshift) {
+ this.exploreElements.unshift(explore);
+ this.graphConfigs.unshift(graphConfig);
+ } else {
+ this.exploreElements.push(explore);
+ this.graphConfigs.push(graphConfig);
+ }
explore.addEventListener('state_changed', () => {
const elemState = explore.state;
let stateChanged = false;
- if (this.allGraphConfigs[elemState.graph_index].formulas !== elemState.formulas) {
- this.allGraphConfigs[elemState.graph_index].formulas = elemState.formulas || [];
+ if (this.graphConfigs[elemState.graph_index].formulas !== elemState.formulas) {
+ graphConfig.formulas = elemState.formulas || [];
stateChanged = true;
}
- if (this.allGraphConfigs[elemState.graph_index].queries[0] !== elemState.queries[0]) {
- this.allGraphConfigs[elemState.graph_index].queries = elemState.queries || [];
+ if (this.graphConfigs[elemState.graph_index].queries[0] !== elemState.queries[0]) {
+ graphConfig.queries = elemState.queries || [];
stateChanged = true;
}
- if (this.allGraphConfigs[elemState.graph_index].keys !== elemState.keys) {
- this.allGraphConfigs[elemState.graph_index].keys = elemState.keys || '';
+ if (this.graphConfigs[elemState.graph_index].keys !== elemState.keys) {
+ graphConfig.keys = elemState.keys || '';
stateChanged = true;
}
if (stateChanged) {
+ this.graphConfigs[elemState.graph_index] = graphConfig;
this.updateShortcutMultiview();
}
});
@@ -1534,10 +1532,7 @@
* @returns
*/
private getHeader(): (ColumnHeader | null)[] | null {
- if (this.currentPageExploreElements.length === 0) {
- return null;
- }
- return this.currentPageExploreElements[0].getHeader();
+ return this.exploreElements[0].getHeader();
}
/**
@@ -1548,7 +1543,7 @@
private getCompleteTraceset(): { [key: string]: number[] } {
const fullTraceSet: { [key: string]: number[] } = {};
- this.currentPageExploreElements.forEach((elem) => {
+ this.exploreElements.forEach((elem) => {
const headerLength = elem.getHeader()?.length;
// Check that header lengths are the same, otherwise ignore.
if (headerLength === this.getHeader()?.length) {
@@ -1571,7 +1566,7 @@
*/
private getAllCommitLinks(): (CommitLinks | null)[] {
const commitLinks: (CommitLinks | null)[] = [];
- this.currentPageExploreElements.forEach((elem) => {
+ this.exploreElements.forEach((elem) => {
const elemLinks = elem.getCommitLinks();
if (elemLinks.length > 0) {
commitLinks.push(...elemLinks);
@@ -1583,7 +1578,7 @@
private getFullAnomalyMap(): AnomalyMap {
const anomalyMap: AnomalyMap = {};
- this.currentPageExploreElements.forEach((elem) => {
+ this.exploreElements.forEach((elem) => {
const anomalies = elem.getAnomalyMap();
Object.keys(anomalies!).forEach((traceId) => {
const existingEntry = anomalyMap[traceId];
@@ -1638,7 +1633,7 @@
*
*/
private updateShortcutMultiview() {
- updateShortcut(this.allGraphConfigs)
+ updateShortcut(this.graphConfigs)
.then((shortcut) => {
if (shortcut === '') {
this.state.shortcut = '';
@@ -1659,7 +1654,7 @@
this.state.pageOffset + e.detail.delta * this.state.pageSize
);
this.stateHasChanged!();
- this.renderCurrentPage();
+ this.splitGraphs();
}
private pageSizeChanged(e: MouseEvent) {
@@ -1667,20 +1662,7 @@
this.testPicker?.setReadOnly(true);
this.state.pageSize = +(e.target! as HTMLInputElement).value;
this.stateHasChanged!();
- this.renderCurrentPage();
- }
-
- private addEmptyGraph(unshift?: boolean): ExploreSimpleSk {
- const explore = this.createExploreSimpleSk();
- const graphConfig = new GraphConfig();
- if (unshift) {
- this.exploreElements.unshift(explore);
- this.allGraphConfigs.unshift(graphConfig);
- } else {
- this.exploreElements.push(explore);
- this.allGraphConfigs.push(graphConfig);
- }
- return explore;
+ this.splitGraphs();
}
private async loadAllCharts() {
@@ -1697,4 +1679,5 @@
}
}
}
+
define('explore-multi-sk', ExploreMultiSk);
diff --git a/perf/modules/explore-multi-sk/explore-multi-sk_test.ts b/perf/modules/explore-multi-sk/explore-multi-sk_test.ts
index 7b69c3a..b42e73c 100644
--- a/perf/modules/explore-multi-sk/explore-multi-sk_test.ts
+++ b/perf/modules/explore-multi-sk/explore-multi-sk_test.ts
@@ -200,7 +200,7 @@
const initialGraphCount = element['exploreElements'].length;
element['addEmptyGraph']();
assert.equal(element['exploreElements'].length, initialGraphCount + 1);
- assert.equal(element['allGraphConfigs'].length, initialGraphCount + 1);
+ assert.equal(element['graphConfigs'].length, initialGraphCount + 1);
});
it('removes a graph when a remove-explore event is dispatched', async () => {
@@ -216,7 +216,6 @@
element.dispatchEvent(event);
assert.equal(element['exploreElements'].length, initialGraphCount - 1);
- assert.equal(element['allGraphConfigs'].length, initialGraphCount - 1);
});
it('resets pagination when the last graph is removed', async () => {
@@ -232,7 +231,6 @@
element.dispatchEvent(event);
assert.equal(element['exploreElements'].length, 0);
- assert.equal(element['allGraphConfigs'].length, 0);
assert.equal(element.state.totalGraphs, 0);
// However, the page offset is correctly reset to 0 by a different
@@ -285,7 +283,7 @@
const newShortcutId = 'new-shortcut-id';
fetchMock.post('/_/shortcut/update', { id: newShortcutId });
- element['allGraphConfigs'] = [{ queries: ['config=new'], formulas: [], keys: '' }];
+ element['graphConfigs'] = [{ queries: ['config=new'], formulas: [], keys: '' }];
// stateHasChanged needs to be non-null for the update to be pushed.
element['stateHasChanged'] = () => {};
element['updateShortcutMultiview']();
@@ -349,34 +347,6 @@
beforeEach(async () => {
await setupElement();
});
-
- it('does not leak exploreElements on repeated splitGraphs', async () => {
- // Initial state: 0 graphs (connectedCallback might not add one if no shortcut/params)
- // Let's add one manually to start with a known state.
- element['addEmptyGraph']();
- assert.equal(element['exploreElements'].length, 1);
-
- // Mock grouping to always return 1 group to simulate steady state
- sinon.stub(element, 'groupTracesBySplitKey' as any).returns(new Map([['key', ['trace1']]]));
-
- // Mock methods called by splitGraphs
- sinon.stub(element, 'createFrameRequest' as any).returns({});
- sinon.stub(element, 'createFrameResponse' as any).returns({});
- sinon.stub(element, 'renderCurrentPage' as any);
- sinon.stub(element, 'checkDataLoaded' as any);
-
- // Force totalGraphs to > 1 so splitGraphs doesn't return early
- element.state.totalGraphs = 2;
-
- await element['splitGraphs'](false, true);
-
- // Should be reset to 2 (1 Master + 1 group)
- assert.equal(element['exploreElements'].length, 2, 'exploreElements should be reset');
-
- // Run again
- await element['splitGraphs'](false, true);
- assert.equal(element['exploreElements'].length, 2, 'exploreElements should remain stable');
- });
});
describe('Synchronization', () => {
@@ -463,7 +433,7 @@
const spy2 = sinon.spy(graph2, 'updateSelectedRangeWithPlotSummary');
// Manually set the internal state to use our mocks.
- element['currentPageExploreElements'] = [graph1 as any, graph2 as any];
+ element['exploreElements'] = [graph1 as any, graph2 as any];
const detail: PlotSelectionEventDetails = {
domain: 'commit',
@@ -572,12 +542,9 @@
horizontal_zoom: false,
graph_index: 0,
doNotQueryData: false,
- dots: true,
- autoRefresh: false,
- show_google_plot: false,
};
- element['addStateToExplore'](simpleSk, new GraphConfig(), false, 0);
+ element['addStateToExplore'](simpleSk, new GraphConfig(), false);
assert.equal(simpleSk.state.begin, 1000);
assert.equal(simpleSk.state.end, 2000);
@@ -644,7 +611,6 @@
{ queries: ['config=test1'], formulas: [], keys: '' },
{ queries: ['config=test2'], formulas: [], keys: '' },
];
- element['allGraphConfigs'] = element['graphConfigs'];
element.state.splitByKeys = ['config'];
await element['loadAllCharts']();
@@ -808,16 +774,14 @@
// After the initial chunked load, the `exploreElements` array is populated.
// Let's simulate the state after `_onPlotButtonClicked` has run.
- // The stub for `splitGraphs` prevents `renderCurrentPage` from being called
+ // The stub for `splitGraphs` prevents `addGraphsToCurrentPage` from being called
// in a way that's useful for this test's setup, so we call it manually.
element['exploreElements'] = [
mainGraph,
...Array(totalSplitGraphs).fill(new ExploreSimpleSk()),
];
element['graphConfigs'] = Array(totalSplitGraphs + 1).fill(new GraphConfig());
- element['allGraphConfigs'] = element['graphConfigs'];
- element.state.pageSize = 2;
- element['renderCurrentPage'](true); // This will respect the small pageSize.
+ element['addGraphsToCurrentPage'](true); // This will respect the small pageSize.
assert.equal(
element['currentPageExploreElements'].length,
@@ -849,7 +813,7 @@
assert.equal(element.state.pageOffset, 0, 'Page offset should be reset');
assert.isTrue(splitGraphsSpy.called, 'splitGraphs should be called to reload');
- element['renderCurrentPage']();
+ element['addGraphsToCurrentPage']();
assert.equal(
element['currentPageExploreElements'].length,
totalSplitGraphs,
@@ -903,8 +867,8 @@
state.shortcut = shortcutId;
state.splitByKeys = ['someKey']; // Enable splitting to trigger aggregation.
- // Mock renderCurrentPage to avoid DOM issues in this test
- sinon.stub(element, 'renderCurrentPage' as any).returns(undefined);
+ // Mock addGraphsToCurrentPage to avoid DOM issues in this test
+ sinon.stub(element, 'addGraphsToCurrentPage' as any).returns(undefined);
// Mock splitGraphs as well since it's called after graph loading
sinon.stub(element, 'splitGraphs' as any).resolves();
// Mock checkDataLoaded to prevent side effects
diff --git a/perf/modules/explore-simple-sk/explore-simple-sk.ts b/perf/modules/explore-simple-sk/explore-simple-sk.ts
index 57eb46e..a952fe5 100644
--- a/perf/modules/explore-simple-sk/explore-simple-sk.ts
+++ b/perf/modules/explore-simple-sk/explore-simple-sk.ts
@@ -183,10 +183,6 @@
// max number of charts to show on a page
const chartsPerPage = 11;
-const REFRESH_TIMEOUT = 30 * 1000;
-
-type RequestFrameCallback = (frameResponse: FrameResponse) => Promise<void>;
-
export interface ZoomWithDelta {
zoom: CommitRange;
delta: CommitNumber;
@@ -315,12 +311,6 @@
showZero: boolean = false;
- dots: boolean = true;
-
- autoRefresh: boolean = false;
-
- show_google_plot = false;
-
numCommits: number = 250;
requestType: RequestType = 1; // 0 to use begin/end, 1 to use numCommits.
@@ -475,8 +465,6 @@
// is no pending request.
private _requestId = '';
- private _refreshId = -1;
-
// All the data converted into a CVS blob to download.
private _csvBlobURL: string = '';
@@ -957,10 +945,7 @@
};
private showZero = () => {
- this.setState({
- ...this._state,
- showZero: !this._state.showZero,
- });
+ this._state.showZero = !this._state.showZero;
this.googleChartPlot.value!.showZero = this._state.showZero;
this.render();
};
@@ -1117,15 +1102,12 @@
const position = chart.getPositionByIndex(index);
const commit = this.getCommitDetails(commitPos);
const prevCommit = this.getCommitDetails(this.getPreviousCommit(index.tableRow, traceName));
- this.setState({
- ...this._state,
- selected: {
- name: traceName,
- commit: commitPos,
- tableCol: index.tableCol,
- tableRow: index.tableRow,
- },
- });
+ this.state.selected = {
+ name: traceName,
+ commit: commitPos,
+ tableCol: index.tableCol,
+ tableRow: index.tableRow,
+ };
this.enableTooltip(
{
@@ -1544,15 +1526,9 @@
private switchZoom(target: MdSwitch | null) {
this.zoomDirectionSwitch = !this.zoomDirectionSwitch;
if (target!.selected) {
- this.setState({
- ...this._state,
- horizontal_zoom: true,
- });
+ this.state.horizontal_zoom = true;
} else {
- this.setState({
- ...this._state,
- horizontal_zoom: false,
- });
+ this.state.horizontal_zoom = false;
}
this.render();
const detail = {
@@ -1708,12 +1684,10 @@
})
.then(jsonOrThrow)
.then((json: ShiftResponse) => {
- this.setState({
- ...this._state,
- begin: json.begin,
- end: json.end,
- requestType: 0,
- });
+ this._state.begin = json.begin;
+ this._state.end = json.end;
+ this._state.requestType = 0;
+ this._stateHasChanged();
this.rangeChangeImpl();
})
.catch(errorMessage);
@@ -2459,10 +2433,8 @@
if (this.logEntry) {
this.logEntry!.textContent = '';
}
- this.setState({
- ...this._state,
- selected: defaultPointSelected(),
- });
+ this._state.selected = defaultPointSelected();
+ this._stateHasChanged();
}
/**
@@ -2759,15 +2731,17 @@
this.commitTime!.textContent = '';
}
const switchToTab = body.formulas!.length > 0 || body.queries!.length > 0 || body.keys !== '';
- this.requestFrame(body, (json) => {
- if (json === null || json === undefined) {
+ this.requestFrame(body)
+ .then((json) => {
+ if (json === null || json === undefined) {
+ errorMessage('Failed to find any matching traces.');
+ return;
+ }
+ return this.UpdateWithFrameResponse(json, body, switchToTab);
+ })
+ .catch(() => {
errorMessage('Failed to find any matching traces.');
- return Promise.resolve();
- }
- return this.UpdateWithFrameResponse(json, body, switchToTab);
- }).catch(() => {
- errorMessage('Failed to find any matching traces.');
- });
+ });
}
/**
@@ -2840,40 +2814,6 @@
* otherwise replace them all with the new ones.
* @param {Boolean} tab - If true then switch to the Params tab.
*/
- private toggleDotsHandler() {
- this.setState({
- ...this._state,
- dots: !this._state.dots,
- });
- }
-
- private autoRefreshHandler(target: MdSwitch | null) {
- this.setState({
- ...this._state,
- autoRefresh: target!.selected,
- });
- this.autoRefreshChanged();
- }
-
- private autoRefreshChanged() {
- if (!this._state.autoRefresh) {
- if (this._refreshId !== -1) {
- clearInterval(this._refreshId);
- }
- } else {
- this._refreshId = window.setInterval(() => this.autoRefresh(), REFRESH_TIMEOUT);
- }
- }
-
- private autoRefresh() {
- const body = this.requestFrameBodyFullFromState();
- const switchToTab = body.formulas!.length > 0 || body.queries!.length > 0 || body.keys !== '';
- this.requestFrame(body, (json) => {
- return this.UpdateWithFrameResponse(json, body, switchToTab);
- });
- this.setState(this._state);
- }
-
private async addTraces(
json: FrameResponse,
tab: boolean,
@@ -3036,10 +2976,10 @@
*
* @param plotType - The type of traces being added.
*/
- async add(replace: boolean, plotType: addPlotType): Promise<void> {
+ add(replace: boolean, plotType: addPlotType): void {
const q = this.query!.current_query;
const f = this.formula!.value;
- await this.addFromQueryOrFormula(replace, plotType, q, f);
+ this.addFromQueryOrFormula(replace, plotType, q, f);
}
/** Create a FrameRequest for just a single query or formula. */
@@ -3153,7 +3093,7 @@
try {
if (createFromScratch) {
const body = this.requestFrameBodyFullFromState();
- return this.requestFrame(body, (json) => {
+ return this.requestFrame(body).then((json) => {
return this.UpdateWithFrameResponse(
json,
body,
@@ -3165,7 +3105,7 @@
} else {
const requestNewData = this.requestFrameBodyForTrace(plotType, q, f);
const requestAllData = this.requestFrameBodyFullFromState();
- return this.requestFrame(requestNewData, (json) => {
+ return this.requestFrame(requestNewData).then((json) => {
// Merge new data with the existing state.
const frameResponse = json as FrameResponse;
const newDf = frameResponse.dataframe!;
@@ -3392,12 +3332,10 @@
})
.then(jsonOrThrow)
.then((json) => {
- this.setState({
- ...this._state,
- keys: json.id,
- queries: [],
- });
+ this._state.keys = json.id;
+ this._state.queries = [];
this.clearSelectedState();
+ this._stateHasChanged();
this.render();
})
.catch(errorMessage);
@@ -3538,6 +3476,16 @@
}
}
+ /** Common catch function for _requestFrame and _checkFrameRequestStatus. */
+ private catch(msg: any) {
+ this._requestId = '';
+ if (msg) {
+ errorMessage(msg);
+ }
+ this.percent!.textContent = '';
+ this.spinning = false;
+ }
+
/** @prop spinning - True if we are waiting to retrieve data from
* the server.
*/
@@ -3566,58 +3514,43 @@
* tz: "America/New_York"
* };
*
- * The 'cb' callback function will be called with the decoded JSON body
+ * @returns A Promise that resolves with the decoded JSON body
* of the response once it's available.
*/
- private requestFrame(body: FrameRequest, cb: RequestFrameCallback): Promise<void> {
+ private async requestFrame(body: FrameRequest): Promise<FrameResponse> {
if (this._requestId !== '') {
const err = new Error('There is a pending query already running.');
errorMessage(err.message);
- return Promise.reject(err);
+ throw err;
}
- this._requestId = 'About to make request';
+ // _requestId is used as a lock, any non-empty string works for that.
+ this._requestId = 'Request in progress';
this.spinning = true;
- return new Promise<void>((resolve, reject) => {
- this.sendFrameRequest(body, async (json) => {
- // make this inner callback async
- try {
- await cb(json); // await the execution of the main callback
- resolve();
- } catch (e: any) {
- // Catch errors from cb
- errorMessage(e.message || e.toString());
- reject(e);
- }
- })
- .catch((msg: any) => {
- // Catch errors from sendFrameRequest itself
- if (msg) {
- errorMessage(msg.message || msg.toString());
- }
- if (this.percent) {
- // Check if percent is not null
- this.percent.textContent = '';
- }
- reject(msg);
- })
- .finally(() => {
- this.spinning = false;
- this._requestId = '';
- });
- });
+ this.dataLoading = true;
+
+ try {
+ const jsonResponse = await this.sendFrameRequest(body);
+ return jsonResponse;
+ } catch (err: any) {
+ // This catches errors from sendFrameRequest (e.g., status !== 'Finished')
+ if (this.percent) {
+ this.percent.textContent = '';
+ }
+ // Re-throw the error for the caller to handle
+ throw err;
+ } finally {
+ // Ensuring the state is always cleaned up.
+ this.spinning = false;
+ this.dataLoading = false;
+ this._requestId = '';
+ }
}
/**
* Starts the frame request and returns the resulting data.
*/
- private async sendFrameRequest(body: FrameRequest, cb: RequestFrameCallback) {
- // Fill in a default time range if either of the values are -1.
- if (this._state.begin === -1 || this._state.end === -1) {
- const now = Math.floor(Date.now() / 1000);
- this._state.begin = now - DEFAULT_RANGE_S;
- this._state.end = now;
- }
+ private async sendFrameRequest(body: FrameRequest): Promise<FrameResponse> {
body.tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
const finishedProg = await startRequest(
@@ -3640,7 +3573,7 @@
errorMessage(msg);
}
- await cb(finishedProg.results as FrameResponse);
+ return finishedProg.results as FrameResponse;
}
get state(): State {
@@ -3648,10 +3581,6 @@
}
set state(state: State) {
- this.setState(state);
- }
-
- setState(state: State): void {
const prevBegin = this._state?.begin;
const prevEnd = this._state?.end;
const prevRequestType = this._state?.requestType;
@@ -3659,13 +3588,15 @@
const prevFormulas = [...(this._state?.formulas || [])];
const prevKeys = this._state?.keys;
- if (state.begin === -1 && state.end === -1) {
- const now = Math.floor(Date.now() / 1000);
- state.end = now;
- state.begin = now - this.getDefaultRange();
- }
state = this.rationalizeTimeRange(state);
this._state = state;
+
+ // Synchronize the xAxisSwitch with the state's domain
+ const isDateDomain = this._state.domain === 'date';
+ if (this.xAxisSwitch !== isDateDomain) {
+ this.xAxisSwitch = isDateDomain;
+ }
+
if (this.range) {
this.range!.state = {
begin: this._state.begin,
@@ -3715,8 +3646,7 @@
this.updateTestPickerUrl();
}
- this.autoRefreshChanged();
- if (!state.doNotQueryData && this.getHeader() !== null) {
+ if (!state.doNotQueryData) {
this.rangeChangeImpl();
}
}
diff --git a/perf/modules/plot-google-chart-sk/plot-google-chart-sk.ts b/perf/modules/plot-google-chart-sk/plot-google-chart-sk.ts
index 07d991b..299f636 100644
--- a/perf/modules/plot-google-chart-sk/plot-google-chart-sk.ts
+++ b/perf/modules/plot-google-chart-sk/plot-google-chart-sk.ts
@@ -712,19 +712,6 @@
});
}
- disconnectedCallback(): void {
- super.disconnectedCallback();
- this.clear();
- window.removeEventListener('mousemove', this.onWindowMouseMove);
- window.removeEventListener('mouseup', this.onWindowMouseUp);
- }
-
- public clear(): void {
- if (this.chart) {
- this.chart.clearChart();
- }
- }
-
private onSidePanelToggle() {
this.plotElement.value?.redraw();
}
diff --git a/perf/modules/plot-google-chart-sk/plot-google-chart-sk_test.ts b/perf/modules/plot-google-chart-sk/plot-google-chart-sk_test.ts
index fd3a6a9..799dbf1 100644
--- a/perf/modules/plot-google-chart-sk/plot-google-chart-sk_test.ts
+++ b/perf/modules/plot-google-chart-sk/plot-google-chart-sk_test.ts
@@ -51,11 +51,6 @@
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
expect(element).to.not.be.null;
});
-
- it('some result', () => {
- // eslint-disable-next-line @typescript-eslint/no-unused-expressions
- expect(element).to.not.be.null;
- });
});
describe('determineYAxisTitle', () => {
diff --git a/perf/modules/plot-summary-sk/BUILD.bazel b/perf/modules/plot-summary-sk/BUILD.bazel
index 4fdd95f..560b0ae 100644
--- a/perf/modules/plot-summary-sk/BUILD.bazel
+++ b/perf/modules/plot-summary-sk/BUILD.bazel
@@ -102,18 +102,14 @@
"//:node_modules/lit",
"//perf/modules/dataframe:index_ts_lib",
],
- ts_srcs = [
- "h_resizable_box_sk.ts",
- ],
+ ts_srcs = ["h_resizable_box_sk.ts"],
visibility = ["//visibility:public"],
)
sk_element(
name = "plot-summary-sk.css_ts_lib",
ts_deps = ["//:node_modules/lit"],
- ts_srcs = [
- "plot-summary-sk.css.ts",
- ],
+ ts_srcs = ["plot-summary-sk.css.ts"],
visibility = ["//visibility:public"],
)
diff --git a/perf/modules/plot-summary-sk/plot-summary-sk.ts b/perf/modules/plot-summary-sk/plot-summary-sk.ts
index 845693c..b02d29d 100644
--- a/perf/modules/plot-summary-sk/plot-summary-sk.ts
+++ b/perf/modules/plot-summary-sk/plot-summary-sk.ts
@@ -113,18 +113,6 @@
const options = SummaryChartOptions(getComputedStyle(this), this.domain);
options.colors = [];
- // Downsample the data if there are too many points.
- const numRows = dt.getNumberOfRows();
- const maxPoints = 1000;
- if (numRows > maxPoints) {
- const sampleRate = Math.ceil(numRows / maxPoints);
- const rowsToKeep = [];
- for (let i = 0; i < numRows; i += sampleRate) {
- rowsToKeep.push(i);
- }
- view.setRows(rowsToKeep);
- }
-
// The first two columns are the commit position and the date.
const cols = [this.domain === 'commit' ? 0 : 1];
for (let i = 2; i < dt.getNumberOfColumns(); i++) {
diff --git a/perf/modules/report-page-sk/BUILD.bazel b/perf/modules/report-page-sk/BUILD.bazel
index 814c90a..0df10b7 100644
--- a/perf/modules/report-page-sk/BUILD.bazel
+++ b/perf/modules/report-page-sk/BUILD.bazel
@@ -23,13 +23,13 @@
"//:node_modules/lit",
"//elements-sk/modules:define_ts_lib",
"//elements-sk/modules:upgradeproperty_ts_lib",
- "//infra-sk/modules:jsonorthrow_ts_lib",
"//infra-sk/modules/ElementSk:index_ts_lib",
"//perf/modules/cid:cid_ts_lib",
"//perf/modules/errorMessage:index_ts_lib",
"//perf/modules/json:index_ts_lib",
- "//perf/modules/telemetry:telemetry_ts_lib",
"//perf/modules/trace-details-formatter:traceformatter_ts_lib",
+ "//perf/modules/telemetry:telemetry_ts_lib",
+ "//infra-sk/modules:jsonorthrow_ts_lib",
],
ts_srcs = [
"report-page-sk.ts",
diff --git a/perf/modules/report-page-sk/report-page-sk.ts b/perf/modules/report-page-sk/report-page-sk.ts
index b2e2be4..cb4b8e4 100644
--- a/perf/modules/report-page-sk/report-page-sk.ts
+++ b/perf/modules/report-page-sk/report-page-sk.ts
@@ -381,7 +381,6 @@
const query = this.getQueryFromAnomaly(anomaly);
const state = new State();
- state.plotSummary = true;
const timerange = this.anomalyTracker.getAnomaly(anomaly.id)!.timerange;
explore.state = {
...state,
diff --git a/perf/modules/report-page-sk/report-page-sk_puppeteer_test.ts b/perf/modules/report-page-sk/report-page-sk_puppeteer_test.ts
index dbdb876..ad914f5 100644
--- a/perf/modules/report-page-sk/report-page-sk_puppeteer_test.ts
+++ b/perf/modules/report-page-sk/report-page-sk_puppeteer_test.ts
@@ -52,86 +52,6 @@
const apiMocks = [
{
- name: 'Frame Start',
- matches: (url: string) => url.includes('/_/frame/start'),
- response: {
- status: 200,
- contentType: 'application/json',
- body: JSON.stringify({
- status: 'Finished',
- results: {
- dataframe: {
- traceset: {
- ',config=test,': [1, 2, 3, 4],
- },
- header: [
- {
- offset: 100,
- timestamp: 1676307170,
- hash: 'h1',
- author: 'a',
- message: 'm',
- url: 'u',
- },
- {
- offset: 101,
- timestamp: 1676307171,
- hash: 'h2',
- author: 'a',
- message: 'm',
- url: 'u',
- },
- {
- offset: 102,
- timestamp: 1676307172,
- hash: 'h3',
- author: 'a',
- message: 'm',
- url: 'u',
- },
- {
- offset: 103,
- timestamp: 1676307173,
- hash: 'h4',
- author: 'a',
- message: 'm',
- url: 'u',
- },
- ],
- paramset: {
- config: ['test'],
- },
- skip: 0,
- traceMetadata: [],
- },
- anomalymap: {},
- skps: [],
- msg: '',
- display_mode: 'display_plot',
- },
- messages: [],
- }),
- },
- },
- {
- name: 'User Issues',
- matches: (url: string) => url.includes('/_/user_issues/'),
- response: {
- status: 200,
- contentType: 'application/json',
- body: JSON.stringify({ UserIssues: [] }),
- },
- },
- {
- name: 'Telemetry',
- matches: (url: string) => url.includes('/_/fe_telemetry'),
- response: {
- status: 200,
- contentType: 'text/plain',
- body: '',
- },
- },
- {
name: 'CID Details',
matches: (url: string) => url.endsWith('/_/cid/'),
response: {
diff --git a/perf/modules/user-issue-sk/BUILD.bazel b/perf/modules/user-issue-sk/BUILD.bazel
index b7ca4cb..cbfb081 100644
--- a/perf/modules/user-issue-sk/BUILD.bazel
+++ b/perf/modules/user-issue-sk/BUILD.bazel
@@ -12,9 +12,9 @@
"//:node_modules/lit",
"//:node_modules/lit-html", # keep
"//infra-sk/modules/json:index_ts_lib",
- "//infra-sk/modules:jsonorthrow_ts_lib",
- "//perf/modules/errorMessage:index_ts_lib",
"//perf/modules/json:index_ts_lib",
+ "//perf/modules/errorMessage:index_ts_lib",
+ "//infra-sk/modules:jsonorthrow_ts_lib",
],
ts_srcs = [
"user-issue-sk.ts",