From 444ae4e48bdb82fb71d3c64d1a299c5b739597c5 Mon Sep 17 00:00:00 2001
From: zzyangh <799463087@qq.com>
Date: Thu, 16 Jan 2025 16:33:21 +0800
Subject: [PATCH] [feature](SqlAnalyze/ManagementConf): Add SQL execution plan
cost chart
---
.../__snapshots__/index.test.tsx.snap | 6617 ++++++++++++++++-
.../SqlAnalyze/ManagementConf/index.test.tsx | 35 +-
.../page/SqlAnalyze/ManagementConf/index.tsx | 15 +
.../src/page/SqlAnalyze/SqlManage/index.tsx | 38 +-
.../SqlAnalyze/hooks/useSqlExecPlanCost.ts | 51 +
5 files changed, 6721 insertions(+), 35 deletions(-)
create mode 100644 packages/sqle/src/page/SqlAnalyze/hooks/useSqlExecPlanCost.ts
diff --git a/packages/sqle/src/page/SqlAnalyze/ManagementConf/__snapshots__/index.test.tsx.snap b/packages/sqle/src/page/SqlAnalyze/ManagementConf/__snapshots__/index.test.tsx.snap
index a043266ca..c1a031f9f 100644
--- a/packages/sqle/src/page/SqlAnalyze/ManagementConf/__snapshots__/index.test.tsx.snap
+++ b/packages/sqle/src/page/SqlAnalyze/ManagementConf/__snapshots__/index.test.tsx.snap
@@ -1,5 +1,6378 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`SqlAnalyze/ManagementConfAnalyze filter sql execution plan cost 1`] = `
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ SELECT
+
+
+
+ *
+
+
+
+ from
+
+ tasks;
+
+
+
+
+
+
+
+
+
+
+
+ SQL执行计划 Cost趋势
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 影响行数
+
+
+ 区别于执行计划的rows列,显示SQL的实际影响行数
+
+
+
+
+ --
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CREATE
+
+
+
+ TABLE
+
+ \`global_configuration_ldap\` (
+ \`id\`
+
+ int
+
+ (
+
+ 10
+
+ ) unsigned
+
+ NOT
+
+
+
+ NULL
+
+ AUTO_INCREMENT,
+ \`created_at\` datetime
+
+ DEFAULT
+
+
+
+ CURRENT_TIMESTAMP
+
+ ,
+ \`updated_at\` datetime
+
+ DEFAULT
+
+
+
+ CURRENT_TIMESTAMP
+
+
+
+ ON
+
+
+
+ UPDATE
+
+
+
+ CURRENT_TIMESTAMP
+
+ ,
+ \`deleted_at\` datetime
+
+ DEFAULT
+
+
+
+ NULL
+
+ ,
+ \`enable\` tinyint(
+
+ 1
+
+ )
+
+ NOT
+
+
+
+ NULL
+
+ ,
+ \`enable_ssl\` tinyint(
+
+ 1
+
+ )
+
+ NOT
+
+
+
+ NULL
+
+ ,
+ \`host\`
+
+ varchar
+
+ (
+
+ 255
+
+ )
+
+ COLLATE
+
+ utf8mb4_unicode_ci
+
+ NOT
+
+
+
+ NULL
+
+ ,
+ \`port\`
+
+ varchar
+
+ (
+
+ 255
+
+ )
+
+ COLLATE
+
+ utf8mb4_unicode_ci
+
+ NOT
+
+
+
+ NULL
+
+ ,
+ \`connect_dn\`
+
+ varchar
+
+ (
+
+ 255
+
+ )
+
+ COLLATE
+
+ utf8mb4_unicode_ci
+
+ NOT
+
+
+
+ NULL
+
+ ,
+ \`connect_secret_password\`
+
+ varchar
+
+ (
+
+ 255
+
+ )
+
+ COLLATE
+
+ utf8mb4_unicode_ci
+
+ NOT
+
+
+
+ NULL
+
+ ,
+ \`base_dn\`
+
+ varchar
+
+ (
+
+ 255
+
+ )
+
+ COLLATE
+
+ utf8mb4_unicode_ci
+
+ NOT
+
+
+
+ NULL
+
+ ,
+ \`user_name_rdn_key\`
+
+ varchar
+
+ (
+
+ 255
+
+ )
+
+ COLLATE
+
+ utf8mb4_unicode_ci
+
+ NOT
+
+
+
+ NULL
+
+ ,
+ \`user_email_rdn_key\`
+
+ varchar
+
+ (
+
+ 255
+
+ )
+
+ COLLATE
+
+ utf8mb4_unicode_ci
+
+ NOT
+
+
+
+ NULL
+
+ ,
+
+
+ PRIMARY
+
+ KEY (\`id\`),
+ KEY \`idx_global_configuration_ldap_deleted_at\` (\`deleted_at\`)
+) ENGINE
+
+ =
+
+ InnoDB
+
+ DEFAULT
+
+ CHARSET
+
+ =
+
+ utf8mb4
+
+ COLLATE
+
+
+ =
+
+ utf8mb4_unicode_ci
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ CREATE
+
+
+
+ TABLE
+
+ \`execute_sql_detail\` (
+ \`id\`
+
+ int
+
+ (
+
+ 10
+
+ ) unsigned
+
+ NOT
+
+
+
+ NULL
+
+ AUTO_INCREMENT,
+ \`created_at\` datetime
+
+ DEFAULT
+
+
+
+ CURRENT_TIMESTAMP
+
+ ,
+ \`updated_at\` datetime
+
+ DEFAULT
+
+
+
+ CURRENT_TIMESTAMP
+
+
+
+ ON
+
+
+
+ UPDATE
+
+
+
+ CURRENT_TIMESTAMP
+
+ ,
+ \`deleted_at\` datetime
+
+ DEFAULT
+
+
+
+ NULL
+
+ ,
+ \`task_id\`
+
+ int
+
+ (
+
+ 10
+
+ ) unsigned
+
+ DEFAULT
+
+
+
+ NULL
+
+ ,
+ \`number\`
+
+ int
+
+ (
+
+ 10
+
+ ) unsigned
+
+ DEFAULT
+
+
+
+ NULL
+
+ ,
+ \`content\` longtext
+
+ COLLATE
+
+ utf8mb4_unicode_ci,
+ \`description\` text
+
+ COLLATE
+
+ utf8mb4_unicode_ci,
+ \`start_binlog_file\`
+
+ varchar
+
+ (
+
+ 255
+
+ )
+
+ COLLATE
+
+ utf8mb4_unicode_ci
+
+ DEFAULT
+
+
+
+ NULL
+
+ ,
+ \`start_binlog_pos\`
+
+ bigint
+
+ (
+
+ 20
+
+ )
+
+ DEFAULT
+
+
+
+ NULL
+
+ ,
+ \`end_binlog_file\`
+
+ varchar
+
+ (
+
+ 255
+
+ )
+
+ COLLATE
+
+ utf8mb4_unicode_ci
+
+ DEFAULT
+
+
+
+ NULL
+
+ ,
+ \`end_binlog_pos\`
+
+ bigint
+
+ (
+
+ 20
+
+ )
+
+ DEFAULT
+
+
+
+ NULL
+
+ ,
+ \`row_affects\`
+
+ bigint
+
+ (
+
+ 20
+
+ )
+
+ DEFAULT
+
+
+
+ NULL
+
+ ,
+ \`exec_status\`
+
+ varchar
+
+ (
+
+ 255
+
+ )
+
+ COLLATE
+
+ utf8mb4_unicode_ci
+
+ DEFAULT
+
+
+
+ 'initialized'
+
+ ,
+ \`exec_result\` text
+
+ COLLATE
+
+ utf8mb4_unicode_ci,
+ \`audit_status\`
+
+ varchar
+
+ (
+
+ 255
+
+ )
+
+ COLLATE
+
+ utf8mb4_unicode_ci
+
+ DEFAULT
+
+
+
+ 'initialized'
+
+ ,
+ \`audit_result\` text
+
+ COLLATE
+
+ utf8mb4_unicode_ci,
+ \`audit_fingerprint\`
+
+ char
+
+ (
+
+ 32
+
+ )
+
+ COLLATE
+
+ utf8mb4_unicode_ci
+
+ DEFAULT
+
+
+
+ NULL
+
+ ,
+ \`audit_level\`
+
+ varchar
+
+ (
+
+ 255
+
+ )
+
+ COLLATE
+
+ utf8mb4_unicode_ci
+
+ DEFAULT
+
+
+
+ NULL
+
+ ,
+
+
+ PRIMARY
+
+ KEY (\`id\`),
+ KEY \`idx_execute_sql_detail_deleted_at\` (\`deleted_at\`),
+ KEY \`idx_execute_sql_detail_task_id\` (\`task_id\`),
+ KEY \`idx_execute_sql_detail_audit_fingerprint\` (\`audit_fingerprint\`)
+) ENGINE
+
+ =
+
+ InnoDB AUTO_INCREMENT
+
+ =
+
+
+ 4
+
+
+
+ DEFAULT
+
+ CHARSET
+
+ =
+
+ utf8mb4
+
+ COLLATE
+
+
+ =
+
+ utf8mb4_unicode_ci
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+`;
+
exports[`SqlAnalyze/ManagementConfAnalyze should get analyze data from origin 1`] = `
+ >
+
+
+
+
+ SQL执行计划 Cost趋势
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 暂无数据,选择其他时间段查看
+
+
+
+
+
+
+
+
+
+
+
{
return {
@@ -31,6 +33,8 @@ describe('SqlAnalyze/ManagementConfAnalyze', () => {
const useParamsMock: jest.Mock = useParams as jest.Mock;
+ let getSqlManageSqlAnalysisChartSpy: jest.SpyInstance;
+ let currentTime = dayjs('2025-01-09 12:00:00');
beforeEach(() => {
MockDate.set(dayjs('2025-01-09 12:00:00').valueOf());
jest.useFakeTimers({ legacyFakeTimers: true });
@@ -40,6 +44,7 @@ describe('SqlAnalyze/ManagementConfAnalyze', () => {
id: '2',
projectName
});
+ getSqlManageSqlAnalysisChartSpy = sqlManage.getSqlManageSqlAnalysisChart();
});
afterEach(() => {
@@ -79,6 +84,34 @@ describe('SqlAnalyze/ManagementConfAnalyze', () => {
expect(container).toMatchSnapshot();
});
+ it('filter sql execution plan cost', async () => {
+ mockGetAnalyzeData();
+ const { container } = renderWithReduxAndTheme();
+ await act(async () => jest.advanceTimersByTime(3000));
+ expect(container).toMatchSnapshot();
+
+ expect(screen.getByText('SQL执行计划 Cost趋势')).toBeInTheDocument();
+ expect(getSqlManageSqlAnalysisChartSpy).toHaveBeenCalledTimes(1);
+ expect(getSqlManageSqlAnalysisChartSpy).toHaveBeenNthCalledWith(1, {
+ sql_manage_id: '2',
+ project_name: projectName,
+ metric_name: 'explain_cost',
+ start_time: translateTimeForRequest(currentTime.subtract(24, 'hour')),
+ end_time: translateTimeForRequest(currentTime)
+ });
+
+ fireEvent.click(screen.getByText('7天'));
+ await act(async () => jest.advanceTimersByTime(0));
+ expect(getSqlManageSqlAnalysisChartSpy).toHaveBeenCalledTimes(2);
+ expect(getSqlManageSqlAnalysisChartSpy).toHaveBeenNthCalledWith(2, {
+ sql_manage_id: '2',
+ project_name: projectName,
+ metric_name: 'explain_cost',
+ start_time: translateTimeForRequest(currentTime.subtract(7, 'day')),
+ end_time: translateTimeForRequest(currentTime)
+ });
+ });
+
test('should render error result of type "info" when response code is 8001', async () => {
const spy = mockGetAnalyzeData();
spy.mockImplementation(() =>
diff --git a/packages/sqle/src/page/SqlAnalyze/ManagementConf/index.tsx b/packages/sqle/src/page/SqlAnalyze/ManagementConf/index.tsx
index ea560bd17..5d95d45fb 100644
--- a/packages/sqle/src/page/SqlAnalyze/ManagementConf/index.tsx
+++ b/packages/sqle/src/page/SqlAnalyze/ManagementConf/index.tsx
@@ -12,6 +12,7 @@ import {
import instance_audit_plan from '@actiontech/shared/lib/api/sqle/service/instance_audit_plan';
import { useTypedParams } from '@actiontech/shared';
import { ROUTE_PATHS } from '@actiontech/shared/lib/data/routePaths';
+import useSqlExecPlanCost from '../hooks/useSqlExecPlanCost';
const ManagementConfAnalyze = () => {
const urlParams =
@@ -62,6 +63,13 @@ const ManagementConfAnalyze = () => {
getSqlAnalyzeFinish
]);
+ const {
+ data,
+ getSqlExecPlanCostDataSourceLoading,
+ getSqlExecPlanCostDataSource,
+ getSqlExecPlanCostDataSourceError
+ } = useSqlExecPlanCost(urlParams.id ?? '');
+
useEffect(() => {
getSqlAnalyze();
}, [getSqlAnalyze]);
@@ -74,6 +82,13 @@ const ManagementConfAnalyze = () => {
errorMessage={errorMessage}
performanceStatistics={performanceStatistics}
loading={loading}
+ sqlExecPlanCostDataSource={data}
+ getSqlExecPlanCostDataSourceLoading={getSqlExecPlanCostDataSourceLoading}
+ getSqlExecPlanCostDataSource={getSqlExecPlanCostDataSource}
+ getSqlExecPlanCostDataSourceError={
+ getSqlExecPlanCostDataSourceError?.message
+ }
+ showExecPlanCostChart
/>
);
};
diff --git a/packages/sqle/src/page/SqlAnalyze/SqlManage/index.tsx b/packages/sqle/src/page/SqlAnalyze/SqlManage/index.tsx
index dfbc5a8c6..0be709a89 100644
--- a/packages/sqle/src/page/SqlAnalyze/SqlManage/index.tsx
+++ b/packages/sqle/src/page/SqlAnalyze/SqlManage/index.tsx
@@ -12,9 +12,7 @@ import { useCurrentProject } from '@actiontech/shared/lib/global';
import SqlAnalyze from '../SqlAnalyze';
import { useTypedParams } from '@actiontech/shared';
import { ROUTE_PATHS } from '@actiontech/shared/lib/data/routePaths';
-import { useRequest } from 'ahooks';
-import dayjs, { Dayjs } from 'dayjs';
-import { translateTimeForRequest } from '@actiontech/shared/lib/utils/Common';
+import useSqlExecPlanCost from '../hooks/useSqlExecPlanCost';
const SQLManageAnalyze = () => {
const urlParams =
@@ -65,36 +63,10 @@ const SQLManageAnalyze = () => {
const {
data,
- loading: getSqlExecPlanCostDataSourceLoading,
- run: getSqlExecPlanCostDataSource,
- error: getSqlExecPlanCostDataSourceError
- } = useRequest((startTime?: Dayjs, endTime?: Dayjs) => {
- const startTimeParam = startTime ?? dayjs().subtract(24, 'hour');
- const endTimeParam = endTime ?? dayjs();
- return SqlManage.GetSqlManageSqlAnalysisChartV1({
- sql_manage_id: urlParams.sqlManageId ?? '',
- project_name: projectName,
- metric_name: 'explain_cost',
- start_time: translateTimeForRequest(startTimeParam),
- end_time: translateTimeForRequest(endTimeParam)
- }).then((res) => {
- if (res.data.code === ResponseCode.SUCCESS) {
- // 根据start_time和end_time填充数据
- const { points } = res.data.data ?? {};
- const firstPoint = points?.[0];
- const lastPoint = points?.[points.length - 1];
- if (startTimeParam?.isBefore(dayjs(firstPoint?.x))) {
- points?.unshift({
- x: translateTimeForRequest(startTimeParam)
- });
- }
- if (endTimeParam?.isAfter(dayjs(lastPoint?.x))) {
- points?.push({ x: translateTimeForRequest(endTimeParam) });
- }
- return points;
- }
- });
- });
+ getSqlExecPlanCostDataSourceLoading,
+ getSqlExecPlanCostDataSource,
+ getSqlExecPlanCostDataSourceError
+ } = useSqlExecPlanCost(urlParams.sqlManageId ?? '');
useEffect(() => {
getSqlAnalyze();
diff --git a/packages/sqle/src/page/SqlAnalyze/hooks/useSqlExecPlanCost.ts b/packages/sqle/src/page/SqlAnalyze/hooks/useSqlExecPlanCost.ts
new file mode 100644
index 000000000..2fd3b5b59
--- /dev/null
+++ b/packages/sqle/src/page/SqlAnalyze/hooks/useSqlExecPlanCost.ts
@@ -0,0 +1,51 @@
+import { useRequest } from 'ahooks';
+import dayjs, { Dayjs } from 'dayjs';
+import { translateTimeForRequest } from '@actiontech/shared/lib/utils/Common';
+import SqlManage from '@actiontech/shared/lib/api/sqle/service/SqlManage';
+import { useCurrentProject } from '@actiontech/shared/lib/global';
+import { ResponseCode } from '@actiontech/shared/lib/enum';
+
+const useSqlExecPlanCost = (id: string) => {
+ const { projectName } = useCurrentProject();
+ const {
+ data,
+ loading: getSqlExecPlanCostDataSourceLoading,
+ run: getSqlExecPlanCostDataSource,
+ error: getSqlExecPlanCostDataSourceError
+ } = useRequest((startTime?: Dayjs, endTime?: Dayjs) => {
+ const startTimeParam = startTime ?? dayjs().subtract(24, 'hour');
+ const endTimeParam = endTime ?? dayjs();
+ return SqlManage.GetSqlManageSqlAnalysisChartV1({
+ sql_manage_id: id ?? '',
+ project_name: projectName,
+ metric_name: 'explain_cost',
+ start_time: translateTimeForRequest(startTimeParam),
+ end_time: translateTimeForRequest(endTimeParam)
+ }).then((res) => {
+ if (res.data.code === ResponseCode.SUCCESS) {
+ // 根据start_time和end_time填充数据
+ const { points } = res.data.data ?? {};
+ const firstPoint = points?.[0];
+ const lastPoint = points?.[points.length - 1];
+ if (startTimeParam?.isBefore(dayjs(firstPoint?.x))) {
+ points?.unshift({
+ x: translateTimeForRequest(startTimeParam)
+ });
+ }
+ if (endTimeParam?.isAfter(dayjs(lastPoint?.x))) {
+ points?.push({ x: translateTimeForRequest(endTimeParam) });
+ }
+ return points;
+ }
+ });
+ });
+
+ return {
+ data,
+ getSqlExecPlanCostDataSourceLoading,
+ getSqlExecPlanCostDataSource,
+ getSqlExecPlanCostDataSourceError
+ };
+};
+
+export default useSqlExecPlanCost;