Line data Source code
1 : import * as dashql from '@ankoh/dashql-core';
2 : import type { ProfileExecutionState } from '../../query/profile_execution_state.js';
3 :
4 1 : const view = dashql.buffers.profile;
5 :
6 : /// Updates the status markers of a mounted plan in place, without rendering the plan again
7 1 : export class ProfileExecutionController {
8 1 : private operatorElements: Array<SVGGElement | null> = [];
9 1 : private pipelinePaths: Array<SVGPathElement | null> = [];
10 1 : private statuses = new Uint8Array();
11 :
12 1 : public reset(operatorCount: number, pipelineCount: number) {
13 1 : this.operatorElements.length = operatorCount;
14 1 : this.pipelinePaths.length = pipelineCount;
15 1 : this.statuses = new Uint8Array(operatorCount);
16 1 : for (const element of this.operatorElements) {
17 1 : if (element != null) element.dataset.status = `${view.ProfileExecutionStatus.UNKNOWN}`;
18 : }
19 1 : for (const path of this.pipelinePaths) {
20 1 : if (path != null) delete path.dataset.status;
21 : }
22 1 : }
23 :
24 1 : public registerOperator(id: number, element: SVGGElement | null) {
25 1 : this.operatorElements[id] = element;
26 1 : if (element != null) element.dataset.status = `${this.statuses[id] ?? view.ProfileExecutionStatus.UNKNOWN}`;
27 1 : }
28 :
29 1 : public registerPipeline(id: number, path: SVGPathElement | null) {
30 1 : this.pipelinePaths[id] = path;
31 1 : }
32 :
33 1 : private setOperatorStatus(id: number, status: dashql.buffers.profile.ProfileExecutionStatus) {
34 1 : if (id >= this.statuses.length || this.statuses[id] === status) return;
35 1 : this.statuses[id] = status;
36 1 : const element = this.operatorElements[id];
37 1 : if (element != null) element.dataset.status = `${status}`;
38 1 : }
39 :
40 1 : private setPipelineStatus(id: number, status: dashql.buffers.profile.ProfileExecutionStatus) {
41 1 : const path = this.pipelinePaths[id];
42 1 : if (path != null) path.dataset.status = `${status}`;
43 1 : }
44 :
45 : /// Apply the output events of the core query profile
46 1 : public applyEvents(events: dashql.buffers.profile.QueryProfileEvents) {
47 1 : const event = new view.ProfileUpdateEvent();
48 1 : for (let i = 0; i < events.eventsLength(); ++i) {
49 1 : events.events(i, event);
50 1 : switch (event.entity()) {
51 1 : case view.ProfileEntity.OPERATOR:
52 1 : this.setOperatorStatus(event.index(), event.status());
53 1 : break;
54 1 : case view.ProfileEntity.PIPELINE:
55 1 : this.setPipelineStatus(event.index(), event.status());
56 : break;
57 1 : }
58 1 : }
59 1 : }
60 :
61 : /// Show the statuses of an execution state, after a reset. An operator without status of its own shows the
62 : /// status of its fragment (`operatorFragments`, by operator): Trino reports operators of finished stages only.
63 1 : public applyState(state: ProfileExecutionState, operatorFragments: readonly number[] = []) {
64 1 : for (let id = 0; id < this.statuses.length; ++id) {
65 1 : const own = state.operators[id]?.status ?? view.ProfileExecutionStatus.UNKNOWN;
66 1 : const fragment = operatorFragments[id];
67 1 : const inherited = fragment == null ? undefined : state.fragments[fragment]?.status;
68 1 : const status = own !== view.ProfileExecutionStatus.UNKNOWN ? own : inherited;
69 1 : if (status != null) this.setOperatorStatus(id, status);
70 1 : }
71 1 : state.pipelines.forEach((pipeline, id) => {
72 1 : if (pipeline != null) this.setPipelineStatus(id, pipeline.status);
73 1 : });
74 1 : }
75 1 : }
|