diff --git a/pkg/apiserver/slowquery/model.go b/pkg/apiserver/slowquery/model.go index ff35921766..4ee7704a6d 100644 --- a/pkg/apiserver/slowquery/model.go +++ b/pkg/apiserver/slowquery/model.go @@ -55,8 +55,9 @@ type Model struct { PlanFromBinding int `gorm:"column:Plan_from_binding" json:"plan_from_binding"` // Connection - User string `gorm:"column:User" json:"user"` - Host string `gorm:"column:Host" json:"host"` + User string `gorm:"column:User" json:"user"` + Host string `gorm:"column:Host" json:"host"` + SessionConnectAttrs *string `gorm:"column:Session_connect_attrs" json:"session_connect_attrs"` // Time ProcessTime float64 `gorm:"column:Process_time" json:"process_time"` diff --git a/tests/fixtures/CLUSTER_SLOW_QUERY.yml b/tests/fixtures/CLUSTER_SLOW_QUERY.yml index 29594a8847..499447acc3 100644 --- a/tests/fixtures/CLUSTER_SLOW_QUERY.yml +++ b/tests/fixtures/CLUSTER_SLOW_QUERY.yml @@ -5,6 +5,7 @@ User: "1" Host: "1" Conn_ID: 1 + Session_connect_attrs: '{"_client_name":"Go-MySQL-Driver","_os":"linux","app_name":"test_app"}' Exec_retry_count: 1 Exec_retry_time: 1 Query_time: 0.462883101 @@ -78,6 +79,7 @@ User: root Host: 127.0.0.1 Conn_ID: 7 + Session_connect_attrs: '{"_client_name":"Go-MySQL-Driver","_os":"linux","app_name":"test_app"}' Exec_retry_count: 0 Exec_retry_time: 0 Query_time: 6.3039e-05 diff --git a/tests/integration/slowquery/mock_db_test.go b/tests/integration/slowquery/mock_db_test.go index 85117809d3..cb818b59fa 100644 --- a/tests/integration/slowquery/mock_db_test.go +++ b/tests/integration/slowquery/mock_db_test.go @@ -60,6 +60,17 @@ func (s *testMockDBSuite) mockDBSession() *gorm.DB { return s.db.Gorm().Debug().Table(TestSlowQueryTableName) } +func (s *testMockDBSuite) supportsSessionConnectAttrs() bool { + cls, err := slowquery.GetAvailableFields(s.sysSchema, s.db.Gorm().Debug().Table(slowquery.SlowQueryTable)) + s.Require().NoError(err) + for _, cl := range cls { + if cl == "session_connect_attrs" { + return true + } + } + return false +} + func (s *testMockDBSuite) TestGetListDefaultRequest() { ds := s.mustQuerySlowLogList(&slowquery.GetListRequest{}) @@ -114,6 +125,12 @@ func (s *testMockDBSuite) TestGetListAllFieldsRequest() { s.Require().NotEmpty(d.Digest) s.Require().NotEmpty(d.GetCommitTSTime) s.Require().NotEmpty(d.Host) + if s.supportsSessionConnectAttrs() { + s.Require().NotNil(d.SessionConnectAttrs) + s.Require().JSONEq(`{"_client_name":"Go-MySQL-Driver","_os":"linux","app_name":"test_app"}`, *d.SessionConnectAttrs) + } else { + s.Require().Nil(d.SessionConnectAttrs) + } s.Require().NotEmpty(d.IndexNames) s.Require().NotEmpty(d.Instance) s.Require().NotEmpty(d.IsInternal) @@ -142,6 +159,16 @@ func (s *testMockDBSuite) TestGetListAllFieldsRequest() { s.Require().NotEmpty(d.WriteSize) } +func (s *testMockDBSuite) TestGetAvailableFields() { + cls, err := slowquery.GetAvailableFields(s.sysSchema, s.mockDBSession()) + s.Require().NoError(err) + if s.supportsSessionConnectAttrs() { + s.Require().Contains(cls, "session_connect_attrs") + } else { + s.Require().NotContains(cls, "session_connect_attrs") + } +} + func (s *testMockDBSuite) TestGetListTimeRangeRequest() { // 1639928730 - 2021-12-19T23:45:30+08:00 // 1639928987 - 2021-12-19T23:49:48+08:00 @@ -282,4 +309,18 @@ func (s *testMockDBSuite) TestGetDetailRequest() { s.Require().Equal(ds2.Timestamp, 1639928987.802016) s.Require().Equal(ds2.Digest, "2375da6810d9c5a0d1c84875b1376bfd469ad952c1884f5dc1d6f36fc953b5df") s.Require().Equal(ds2.ConnectionID, "7") + if s.supportsSessionConnectAttrs() { + s.Require().Nil(ds2.SessionConnectAttrs) + ds3, err := s.mustQuerySlowLogDetail(&slowquery.GetDetailRequest{ + Digest: "TEST_ALL_FIELDS", + Timestamp: 1639928730.786521, + ConnectID: "1", + }) + s.Require().Nil(err) + s.Require().NotNil(ds3) + s.Require().NotNil(ds3.SessionConnectAttrs) + s.Require().JSONEq(`{"_client_name":"Go-MySQL-Driver","_os":"linux","app_name":"test_app"}`, *ds3.SessionConnectAttrs) + } else { + s.Require().Nil(ds2.SessionConnectAttrs) + } } diff --git a/tests/schema/test.CLUSTER_SLOW_QUERY-schema.sql b/tests/schema/test.CLUSTER_SLOW_QUERY-schema.sql index 86fa6159b5..7ef5ac6fa0 100644 --- a/tests/schema/test.CLUSTER_SLOW_QUERY-schema.sql +++ b/tests/schema/test.CLUSTER_SLOW_QUERY-schema.sql @@ -1,3 +1,3 @@ /*!40101 SET NAMES binary*/; /*T![placement] SET PLACEMENT_CHECKS = 0*/; -CREATE TABLE `CLUSTER_SLOW_QUERY` (`INSTANCE` varchar(64) DEFAULT NULL, `Time` timestamp(6) NOT NULL, `Txn_start_ts` bigint(20) unsigned DEFAULT NULL, `User` varchar(64) DEFAULT NULL, `Host` varchar(64) DEFAULT NULL, `Conn_ID` bigint(20) unsigned DEFAULT NULL, `Session_alias` varchar(64) DEFAULT NULL, `Exec_retry_count` bigint(20) unsigned DEFAULT NULL, `Exec_retry_time` double DEFAULT NULL, `Query_time` double DEFAULT NULL, `Parse_time` double DEFAULT NULL, `Compile_time` double DEFAULT NULL, `Rewrite_time` double DEFAULT NULL, `Preproc_subqueries` bigint(20) unsigned DEFAULT NULL, `Preproc_subqueries_time` double DEFAULT NULL, `Optimize_time` double DEFAULT NULL, `Wait_TS` double DEFAULT NULL, `Prewrite_time` double DEFAULT NULL, `Wait_prewrite_binlog_time` double DEFAULT NULL, `Commit_time` double DEFAULT NULL, `Get_commit_ts_time` double DEFAULT NULL, `Commit_backoff_time` double DEFAULT NULL, `Backoff_types` varchar(64) DEFAULT NULL, `Resolve_lock_time` double DEFAULT NULL, `Local_latch_wait_time` double DEFAULT NULL, `Write_keys` bigint(22) DEFAULT NULL, `Write_size` bigint(22) DEFAULT NULL, `Prewrite_region` bigint(22) DEFAULT NULL, `Txn_retry` bigint(22) DEFAULT NULL, `Cop_time` double DEFAULT NULL, `Process_time` double DEFAULT NULL, `Wait_time` double DEFAULT NULL, `Backoff_time` double DEFAULT NULL, `LockKeys_time` double DEFAULT NULL, `Request_count` bigint(20) unsigned DEFAULT NULL, `Total_keys` bigint(20) unsigned DEFAULT NULL, `Process_keys` bigint(20) unsigned DEFAULT NULL, `Rocksdb_delete_skipped_count` bigint(20) unsigned DEFAULT NULL, `Rocksdb_key_skipped_count` bigint(20) unsigned DEFAULT NULL, `Rocksdb_block_cache_hit_count` bigint(20) unsigned DEFAULT NULL, `Rocksdb_block_read_count` bigint(20) unsigned DEFAULT NULL, `Rocksdb_block_read_byte` bigint(20) unsigned DEFAULT NULL, `DB` varchar(64) DEFAULT NULL, `Index_names` varchar(100) DEFAULT NULL, `Is_internal` tinyint(1) DEFAULT NULL, `Digest` varchar(64) DEFAULT NULL, `Stats` varchar(512) DEFAULT NULL, `Cop_proc_avg` double DEFAULT NULL, `Cop_proc_p90` double DEFAULT NULL, `Cop_proc_max` double DEFAULT NULL, `Cop_proc_addr` varchar(64) DEFAULT NULL, `Cop_wait_avg` double DEFAULT NULL, `Cop_wait_p90` double DEFAULT NULL, `Cop_wait_max` double DEFAULT NULL, `Cop_wait_addr` varchar(64) DEFAULT NULL, `Mem_max` bigint(20) DEFAULT NULL, `Disk_max` bigint(20) DEFAULT NULL, `KV_total` double DEFAULT NULL, `PD_total` double DEFAULT NULL, `Backoff_total` double DEFAULT NULL, `Write_sql_response_total` double DEFAULT NULL, `Result_rows` bigint(22) DEFAULT NULL, `Warnings` longtext DEFAULT NULL, `Backoff_Detail` varchar(4096) DEFAULT NULL, `Prepared` tinyint(1) DEFAULT NULL, `Succ` tinyint(1) DEFAULT NULL, `IsExplicitTxn` tinyint(1) DEFAULT NULL, `IsWriteCacheTable` tinyint(1) DEFAULT NULL, `Plan_from_cache` tinyint(1) DEFAULT NULL, `Plan_from_binding` tinyint(1) DEFAULT NULL, `Has_more_results` tinyint(1) DEFAULT NULL, `Resource_group` varchar(64) DEFAULT NULL, `Request_unit_read` double DEFAULT NULL, `Request_unit_write` double DEFAULT NULL, `Time_queued_by_rc` double DEFAULT NULL, `Plan` longtext DEFAULT NULL, `Plan_digest` varchar(128) DEFAULT NULL, `Binary_plan` longtext DEFAULT NULL, `Prev_stmt` longtext DEFAULT NULL, `Query` longtext DEFAULT NULL, PRIMARY KEY (`Time`) /*T![clustered_index] CLUSTERED */ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; \ No newline at end of file +CREATE TABLE `CLUSTER_SLOW_QUERY` (`INSTANCE` varchar(64) DEFAULT NULL, `Time` timestamp(6) NOT NULL, `Txn_start_ts` bigint(20) unsigned DEFAULT NULL, `User` varchar(64) DEFAULT NULL, `Host` varchar(64) DEFAULT NULL, `Conn_ID` bigint(20) unsigned DEFAULT NULL, `Session_alias` varchar(64) DEFAULT NULL, `Session_connect_attrs` json DEFAULT NULL, `Exec_retry_count` bigint(20) unsigned DEFAULT NULL, `Exec_retry_time` double DEFAULT NULL, `Query_time` double DEFAULT NULL, `Parse_time` double DEFAULT NULL, `Compile_time` double DEFAULT NULL, `Rewrite_time` double DEFAULT NULL, `Preproc_subqueries` bigint(20) unsigned DEFAULT NULL, `Preproc_subqueries_time` double DEFAULT NULL, `Optimize_time` double DEFAULT NULL, `Wait_TS` double DEFAULT NULL, `Prewrite_time` double DEFAULT NULL, `Wait_prewrite_binlog_time` double DEFAULT NULL, `Commit_time` double DEFAULT NULL, `Get_commit_ts_time` double DEFAULT NULL, `Commit_backoff_time` double DEFAULT NULL, `Backoff_types` varchar(64) DEFAULT NULL, `Resolve_lock_time` double DEFAULT NULL, `Local_latch_wait_time` double DEFAULT NULL, `Write_keys` bigint(22) DEFAULT NULL, `Write_size` bigint(22) DEFAULT NULL, `Prewrite_region` bigint(22) DEFAULT NULL, `Txn_retry` bigint(22) DEFAULT NULL, `Cop_time` double DEFAULT NULL, `Process_time` double DEFAULT NULL, `Wait_time` double DEFAULT NULL, `Backoff_time` double DEFAULT NULL, `LockKeys_time` double DEFAULT NULL, `Request_count` bigint(20) unsigned DEFAULT NULL, `Total_keys` bigint(20) unsigned DEFAULT NULL, `Process_keys` bigint(20) unsigned DEFAULT NULL, `Rocksdb_delete_skipped_count` bigint(20) unsigned DEFAULT NULL, `Rocksdb_key_skipped_count` bigint(20) unsigned DEFAULT NULL, `Rocksdb_block_cache_hit_count` bigint(20) unsigned DEFAULT NULL, `Rocksdb_block_read_count` bigint(20) unsigned DEFAULT NULL, `Rocksdb_block_read_byte` bigint(20) unsigned DEFAULT NULL, `DB` varchar(64) DEFAULT NULL, `Index_names` varchar(100) DEFAULT NULL, `Is_internal` tinyint(1) DEFAULT NULL, `Digest` varchar(64) DEFAULT NULL, `Stats` varchar(512) DEFAULT NULL, `Cop_proc_avg` double DEFAULT NULL, `Cop_proc_p90` double DEFAULT NULL, `Cop_proc_max` double DEFAULT NULL, `Cop_proc_addr` varchar(64) DEFAULT NULL, `Cop_wait_avg` double DEFAULT NULL, `Cop_wait_p90` double DEFAULT NULL, `Cop_wait_max` double DEFAULT NULL, `Cop_wait_addr` varchar(64) DEFAULT NULL, `Mem_max` bigint(20) DEFAULT NULL, `Disk_max` bigint(20) DEFAULT NULL, `KV_total` double DEFAULT NULL, `PD_total` double DEFAULT NULL, `Backoff_total` double DEFAULT NULL, `Write_sql_response_total` double DEFAULT NULL, `Result_rows` bigint(22) DEFAULT NULL, `Warnings` longtext DEFAULT NULL, `Backoff_Detail` varchar(4096) DEFAULT NULL, `Prepared` tinyint(1) DEFAULT NULL, `Succ` tinyint(1) DEFAULT NULL, `IsExplicitTxn` tinyint(1) DEFAULT NULL, `IsWriteCacheTable` tinyint(1) DEFAULT NULL, `Plan_from_cache` tinyint(1) DEFAULT NULL, `Plan_from_binding` tinyint(1) DEFAULT NULL, `Has_more_results` tinyint(1) DEFAULT NULL, `Resource_group` varchar(64) DEFAULT NULL, `Request_unit_read` double DEFAULT NULL, `Request_unit_write` double DEFAULT NULL, `Time_queued_by_rc` double DEFAULT NULL, `Plan` longtext DEFAULT NULL, `Plan_digest` varchar(128) DEFAULT NULL, `Binary_plan` longtext DEFAULT NULL, `Prev_stmt` longtext DEFAULT NULL, `Query` longtext DEFAULT NULL, PRIMARY KEY (`Time`) /*T![clustered_index] CLUSTERED */ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin; diff --git a/ui/packages/tidb-dashboard-client/src/client/api/models/slowquery-model.ts b/ui/packages/tidb-dashboard-client/src/client/api/models/slowquery-model.ts index 7f9f418f43..750d9eb2e6 100644 --- a/ui/packages/tidb-dashboard-client/src/client/api/models/slowquery-model.ts +++ b/ui/packages/tidb-dashboard-client/src/client/api/models/slowquery-model.ts @@ -344,6 +344,12 @@ export interface SlowqueryModel { * @memberof SlowqueryModel */ 'ru'?: number; + /** + * + * @type {string} + * @memberof SlowqueryModel + */ + 'session_connect_attrs'?: string; /** * * @type {string} @@ -435,4 +441,3 @@ export interface SlowqueryModel { */ 'write_sql_response_total'?: number; } - diff --git a/ui/packages/tidb-dashboard-client/swagger/spec.json b/ui/packages/tidb-dashboard-client/swagger/spec.json index cae73c2d13..06588698fe 100644 --- a/ui/packages/tidb-dashboard-client/swagger/spec.json +++ b/ui/packages/tidb-dashboard-client/swagger/spec.json @@ -5367,6 +5367,9 @@ "description": "Resource Control", "type": "number" }, + "session_connect_attrs": { + "type": "string" + }, "stats": { "type": "string" }, diff --git a/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/pages/Detail/DetailTabs.tsx b/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/pages/Detail/DetailTabs.tsx index 1dc66fc4c3..0a4cbda7f3 100644 --- a/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/pages/Detail/DetailTabs.tsx +++ b/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/pages/Detail/DetailTabs.tsx @@ -13,6 +13,17 @@ import { tabTxnItems } from './DetailTabTxn' import { useSchemaColumns } from '../../utils/useSchemaColumns' import { SlowQueryContext } from '../../context' +function parseSessionConnectAttrs(raw?: string | null) { + if (!raw || raw === 'null') { + return null + } + try { + return JSON.parse(raw) + } catch { + return raw + } +} + export default function DetailTabs({ data }: { data: SlowqueryModel }) { const ctx = useContext(SlowQueryContext) @@ -92,7 +103,38 @@ export default function DetailTabs({ data }: { data: SlowqueryModel }) { } } ] - if (data.warnings) { + const hasSessionConnectAttrs = schemaColumns.includes( + 'session_connect_attrs' + ) + const sessionConnectAttrsRaw = hasSessionConnectAttrs + ? data.session_connect_attrs + : null + const sessionConnectAttrs = parseSessionConnectAttrs(sessionConnectAttrsRaw) + if (hasSessionConnectAttrs) { + tbs.push({ + key: 'session_attrs', + title: t('slow_query.detail.tabs.session_attrs'), + content: () => { + if ( + sessionConnectAttrs == null || + typeof sessionConnectAttrs === 'string' + ) { + return
{sessionConnectAttrsRaw ?? 'null'}
+ } + return ( + + ) + } + }) + } else if (data.warnings) { tbs.push({ key: 'warnings', title: t('slow_query.detail.tabs.warnings'), diff --git a/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/en.yaml b/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/en.yaml index 9c6d56d8fc..1aa727bab7 100644 --- a/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/en.yaml +++ b/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/en.yaml @@ -43,6 +43,8 @@ slow_query: user_tooltip: The user that executes the query host: Client Address host_tooltip: The address of the client that sends the query + session_connect_attrs: Session Connect Attrs + session_connect_attrs_tooltip: JSON-formatted connection attributes provided by the client session db: Execution Database db_tooltip: The database used to execute the query @@ -150,6 +152,7 @@ slow_query: time: Time copr: Coprocessor txn: Transaction + session_attrs: Session Attributes warnings: Warnings toolbar: schemas: diff --git a/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/zh.yaml b/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/zh.yaml index 1ce0e4b8a3..76e728e2a8 100644 --- a/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/zh.yaml +++ b/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/translations/zh.yaml @@ -42,6 +42,8 @@ slow_query: user_tooltip: 执行该 SQL 查询的用户名,可能存在多个执行用户,仅显示其中某一个 host: 客户端地址 host_tooltip: 发送 SQL 查询的客户端地址 + session_connect_attrs: 会话连接属性 + session_connect_attrs_tooltip: 客户端会话上报的 JSON 格式连接属性 db: 执行数据库 db_tooltip: 执行该 SQL 查询时使用的数据库名称 @@ -152,6 +154,7 @@ slow_query: time: 执行时间 copr: Coprocessor 读取 txn: 事务 + session_attrs: 会话属性 warnings: 警告 toolbar: schemas: diff --git a/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/utils/tableColumns.tsx b/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/utils/tableColumns.tsx index 9bb9592f33..e6c9f05aac 100644 --- a/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/utils/tableColumns.tsx +++ b/ui/packages/tidb-dashboard-lib/src/apps/SlowQuery/utils/tableColumns.tsx @@ -1,9 +1,10 @@ -import { Badge } from 'antd' +import { Badge, Tooltip } from 'antd' import { IColumn } from 'office-ui-fabric-react/lib/DetailsList' import React from 'react' import { useTranslation } from 'react-i18next' import { SlowqueryModel } from '@lib/client' +import { TextWrap } from '@lib/components' import { TableColumnFactory } from '@lib/utils/tableColumnFactory' ////////////////////////////////////////// @@ -31,6 +32,17 @@ export const derivedFields = { ] } +type SlowqueryModelWithSessionConnectAttrs = SlowqueryModel & { + session_connect_attrs?: string | null +} + +function formatSessionConnectAttrs(value?: string | null) { + if (!value || value === 'null') { + return '-' + } + return value +} + ////////////////////////////////////////// export function slowQueryColumns( @@ -78,6 +90,24 @@ export function slowQueryColumns( // connection tcf.textWithTooltip('user', rows), tcf.textWithTooltip('host', rows), + tcf + .control({ + name: 'session_connect_attrs', + key: 'session_connect_attrs', + fieldName: 'session_connect_attrs', + minWidth: 100, + maxWidth: 220 + }) + .patchConfig({ + onRender: (rec: SlowqueryModelWithSessionConnectAttrs) => { + const text = formatSessionConnectAttrs(rec.session_connect_attrs) + return ( + + {text} + + ) + } + }), // time tcf.bar.single('wait_time', 's', rows), tcf.bar.single('backoff_time', 's', rows), diff --git a/ui/packages/tidb-dashboard-lib/src/client/models.ts b/ui/packages/tidb-dashboard-lib/src/client/models.ts index 52bffb6369..dc0be644a2 100644 --- a/ui/packages/tidb-dashboard-lib/src/client/models.ts +++ b/ui/packages/tidb-dashboard-lib/src/client/models.ts @@ -2530,6 +2530,12 @@ export interface SlowqueryModel { * @memberof SlowqueryModel */ 'ru'?: number; + /** + * + * @type {string} + * @memberof SlowqueryModel + */ + 'session_connect_attrs'?: string; /** * * @type {string} @@ -4370,4 +4376,3 @@ export interface VersionInfo { */ 'standalone'?: string; } -