Skip to content

Commit

Permalink
feat: fix and refine execution plan (#1568)
Browse files Browse the repository at this point in the history
  • Loading branch information
baurine committed Sep 11, 2023
1 parent 32ee1bb commit 9c80a91
Show file tree
Hide file tree
Showing 19 changed files with 720 additions and 260 deletions.
13 changes: 8 additions & 5 deletions pkg/apiserver/slowquery/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,10 @@ type Model struct {
TxnStartTS string `gorm:"column:Txn_start_ts" json:"txn_start_ts"`

// Detail
PrevStmt string `gorm:"column:Prev_stmt" json:"prev_stmt"`
Plan string `gorm:"column:Plan" json:"plan"` // deprecated, replaced by BinaryPlanText
BinaryPlan string `gorm:"column:Binary_plan" json:"binary_plan"`
BinaryPlanText string `gorm:"column:Binary_plan_text" proj:"tidb_decode_binary_plan(Binary_plan)" json:"binary_plan_text"`
Warnings datatypes.JSON `gorm:"column:Warnings" json:"warnings"`
PrevStmt string `gorm:"column:Prev_stmt" json:"prev_stmt"`
Plan string `gorm:"column:Plan" json:"plan"` // deprecated, replaced by BinaryPlanText
BinaryPlan string `gorm:"column:Binary_plan" json:"binary_plan"`
Warnings datatypes.JSON `gorm:"column:Warnings" json:"warnings"`

// Basic
IsInternal int `gorm:"column:Is_internal" json:"is_internal"`
Expand Down Expand Up @@ -96,6 +95,10 @@ type Model struct {
RocksdbBlockCacheHitCount uint `gorm:"column:Rocksdb_block_cache_hit_count" json:"rocksdb_block_cache_hit_count"`
RocksdbBlockReadCount uint `gorm:"column:Rocksdb_block_read_count" json:"rocksdb_block_read_count"`
RocksdbBlockReadByte uint `gorm:"column:Rocksdb_block_read_byte" json:"rocksdb_block_read_byte"`

// Computed fields
BinaryPlanJSON string `json:"binary_plan_json"` // binary plan json format
BinaryPlanText string `json:"binary_plan_text"` // binary plan plain text
}

type Field struct {
Expand Down
10 changes: 1 addition & 9 deletions pkg/apiserver/slowquery/queries.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,19 +109,11 @@ func QuerySlowLogList(req *GetListRequest, sysSchema *utils.SysSchema, db *gorm.
func QuerySlowLogDetail(req *GetDetailRequest, db *gorm.DB) (*Model, error) {
var result Model
err := db.
Select("*, (UNIX_TIMESTAMP(Time) + 0E0) AS timestamp, tidb_decode_binary_plan(Binary_plan) AS Binary_plan_text").
Select("*, (UNIX_TIMESTAMP(Time) + 0E0) AS timestamp").
Where("Digest = ?", req.Digest).
Where("Time = FROM_UNIXTIME(?)", req.Timestamp).
Where("Conn_id = ?", req.ConnectID).
First(&result).Error
if err != nil {
err = db.
Select("*, (UNIX_TIMESTAMP(Time) + 0E0) AS timestamp").
Where("Digest = ?", req.Digest).
Where("Time = FROM_UNIXTIME(?)", req.Timestamp).
Where("Conn_id = ?", req.ConnectID).
First(&result).Error
}
if err != nil {
return nil, err
}
Expand Down
13 changes: 11 additions & 2 deletions pkg/apiserver/slowquery/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,23 @@ func (s *Service) getDetails(c *gin.Context) {
return
}

// generate binary plan
// generate binary plan json
//
// Due to a kernel bug, the binary plan may fail to parse due to
// encoding issues. Additionally, since the binary plan field is
// not a required field, we can mask this error.
//
// See: https://github.com/pingcap/tidb-dashboard/issues/1515
result.BinaryPlan, _ = utils.GenerateBinaryPlanJSON(result.BinaryPlan)
if result.BinaryPlan != "" {
// may failed but it's ok
result.BinaryPlanText, _ = utils.GenerateBinaryPlanText(db, result.BinaryPlan)
// may failed but it's ok
result.BinaryPlanJSON, _ = utils.GenerateBinaryPlanJSON(result.BinaryPlan)

// reduce response size
result.BinaryPlan = ""
result.Plan = ""
}

c.JSON(http.StatusOK, *result)
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/apiserver/statement/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,8 @@ type Model struct {
AggIndexNames string `json:"index_names" agg:"ANY_VALUE(index_names)"`
AggPlanCount int `json:"plan_count" agg:"COUNT(DISTINCT plan_digest)" related:"plan_digest"`
AggPlan string `json:"plan" agg:"ANY_VALUE(plan)"` // deprecated, replaced by BinaryPlanText
AggPlanDigest string `json:"plan_digest" agg:"ANY_VALUE(plan_digest)"`
AggBinaryPlan string `json:"binary_plan" agg:"ANY_VALUE(binary_plan)"`
AggBinaryPlanText string `json:"binary_plan_text" related:"binary_plan" agg:"tidb_decode_binary_plan(ANY_VALUE(binary_plan))"`
AggPlanDigest string `json:"plan_digest" agg:"ANY_VALUE(plan_digest)"`
AggPlanHint *string `json:"plan_hint" agg:"ANY_VALUE(plan_hint)"`
// RocksDB
AggMaxRocksdbDeleteSkippedCount uint `json:"max_rocksdb_delete_skipped_count" agg:"MAX(max_rocksdb_delete_skipped_count)"`
Expand All @@ -99,6 +98,8 @@ type Model struct {
// Computed fields
RelatedSchemas string `json:"related_schemas"`
PlanCanBeBound bool `json:"plan_can_be_bound"`
BinaryPlanJSON string `json:"binary_plan_json"`
BinaryPlanText string `json:"binary_plan_text"`
}

// tableNames example: "d1.a1,d2.a2,d1.a1,d3.a3"
Expand Down
13 changes: 10 additions & 3 deletions pkg/apiserver/statement/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,16 @@ func (s *Service) planDetailHandler(c *gin.Context) {
return
}

// get binary plan
// may failed but it's ok
result.AggBinaryPlan, _ = utils.GenerateBinaryPlanJSON(result.AggBinaryPlan)
if result.AggBinaryPlan != "" {
// may failed but it's ok
result.BinaryPlanText, _ = utils.GenerateBinaryPlanText(db, result.AggBinaryPlan)
// may failed but it's ok
result.BinaryPlanJSON, _ = utils.GenerateBinaryPlanJSON(result.AggBinaryPlan)

// reduce response size
result.AggBinaryPlan = ""
result.AggPlan = ""
}

c.JSON(http.StatusOK, result)
}
Expand Down
15 changes: 15 additions & 0 deletions pkg/apiserver/utils/binary_plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/pingcap/tipb/go-tipb"
json "google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/runtime/protoimpl"
"gorm.io/gorm"
)

const (
Expand Down Expand Up @@ -975,3 +976,17 @@ func formatJSON(s string) (*simplejson.Json, error) {

return simplejson.NewJson([]byte(s))
}

/////////////////

func GenerateBinaryPlanText(db *gorm.DB, b string) (string, error) {
type binaryPlanText struct {
Text string `gorm:"column:binary_plan_text"`
}
ret := &binaryPlanText{}
err := db.Raw(fmt.Sprintf("select tidb_decode_binary_plan('%s') as binary_plan_text", b)).Find(ret).Error
if err != nil {
return "", err
}
return ret.Text, err
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,13 @@ export interface SlowqueryModel {
*/
'binary_plan'?: string;
/**
*
* Computed fields
* @type {string}
* @memberof SlowqueryModel
*/
'binary_plan_json'?: string;
/**
* binary plan plain text
* @type {string}
* @memberof SlowqueryModel
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,12 @@ export interface StatementModel {
* @memberof StatementModel
*/
'binary_plan'?: string;
/**
*
* @type {string}
* @memberof StatementModel
*/
'binary_plan_json'?: string;
/**
*
* @type {string}
Expand Down
8 changes: 8 additions & 0 deletions ui/packages/tidb-dashboard-client/swagger/spec.json
Original file line number Diff line number Diff line change
Expand Up @@ -4906,7 +4906,12 @@
"binary_plan": {
"type": "string"
},
"binary_plan_json": {
"description": "Computed fields",
"type": "string"
},
"binary_plan_text": {
"description": "binary plan plain text",
"type": "string"
},
"commit_backoff_time": {
Expand Down Expand Up @@ -5314,6 +5319,9 @@
"binary_plan": {
"type": "string"
},
"binary_plan_json": {
"type": "string"
},
"binary_plan_text": {
"type": "string"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState, useContext } from 'react'
import React, { useState, useContext, useMemo } from 'react'
import { Space, Modal, Tabs, Typography } from 'antd'
import { useTranslation } from 'react-i18next'
import { useLocation, useNavigate } from 'react-router-dom'
Expand All @@ -10,26 +10,25 @@ import formatSql from '@lib/utils/sqlFormatter'
import {
AnimatedSkeleton,
BinaryPlanTable,
PlanText,
CopyLink,
Descriptions,
ErrorBar,
Expand,
Head,
HighlightSQL,
Pre,
TextWithInfo
} from '@lib/components'
import {
VisualPlanThumbnailView,
VisualPlanView
} from '@lib/components/VisualPlan'
import { useVersionedLocalStorageState } from '@lib/utils/useVersionedLocalStorageState'
import { telemetry } from '../../utils/telemetry'

import DetailTabs from './DetailTabs'
import { SlowQueryContext } from '../../context'

import {
VisualPlanThumbnailView,
VisualPlanView
} from '@lib/components/VisualPlan'

export interface IPageQuery {
connectId?: string
digest?: string
Expand All @@ -56,7 +55,13 @@ function DetailPage() {
)
)

const binaryPlan = data?.binary_plan && JSON.parse(data.binary_plan)
const binaryPlanObj = useMemo(() => {
const json = data?.binary_plan_json ?? data?.binary_plan
if (json) {
return JSON.parse(json)
}
return undefined
}, [data?.binary_plan, data?.binary_plan_json])

const [detailExpand, setDetailExpand] = useVersionedLocalStorageState(
SLOW_QUERY_DETAIL_EXPAND,
Expand All @@ -73,13 +78,11 @@ function DetailPage() {
setDetailExpand((prev) => ({ ...prev, prev_query: !prev.prev_query }))
const toggleQuery = () =>
setDetailExpand((prev) => ({ ...prev, query: !prev.query }))
// const togglePlan = () =>
// setDetailExpand((prev) => ({ ...prev, plan: !prev.plan }))

const [isVpVisible, setIsVpVisable] = useState(false)
const [isVpVisible, setIsVpVisible] = useState(false)
const toggleVisualPlan = (action: 'open' | 'close') => {
telemetry.toggleVisualPlanModal(action)
setIsVpVisable(!isVpVisible)
setIsVpVisible(!isVpVisible)
}

return (
Expand Down Expand Up @@ -167,56 +170,45 @@ function DetailPage() {
)
})()}
</Descriptions>
{(binaryPlan || !!data.plan) && (
{(binaryPlanObj || !!data.plan) && (
<>
<Space size="middle" style={{ color: '#8c8c8c' }}>
{t('slow_query.detail.plan.title')}
</Space>
<Tabs
defaultActiveKey="text_plan"
defaultActiveKey={
!!data.binary_plan_text
? 'binary_plan_table'
: 'text_plan'
}
onTabClick={(key) =>
telemetry.clickPlanTabs(key, data.digest!)
}
>
<Tabs.TabPane
tab={t('slow_query.detail.plan.text')}
key="text_plan"
>
<Descriptions>
<Descriptions.Item
span={2}
multiline={true}
// multiline={detailExpand.plan}
label={
<Space size="middle">
{/* <Expand.Link
expanded={detailExpand.plan}
onClick={togglePlan}
/> */}
<CopyLink
data={data.binary_plan_text ?? data.plan ?? ''}
/>
</Space>
}
>
{/* <Expand expanded={detailExpand.plan}>
</Expand> */}
<Pre noWrap>{data.binary_plan_text ?? data.plan}</Pre>
</Descriptions.Item>
</Descriptions>
</Tabs.TabPane>

{binaryPlan && !binaryPlan.discardedDueToTooLong && (
{!!data.binary_plan_text && (
<Tabs.TabPane
tab={t('slow_query.detail.plan.table')}
key="binary_plan_table"
>
<BinaryPlanTable data={binaryPlan} />
<BinaryPlanTable
data={data.binary_plan_text}
downloadFileName={`${data.digest}.txt`}
/>
<div style={{ height: 24 }} />
</Tabs.TabPane>
)}

{binaryPlan && !binaryPlan.discardedDueToTooLong && (
<Tabs.TabPane
tab={t('slow_query.detail.plan.text')}
key="text_plan"
>
<PlanText
data={data.binary_plan_text || data.plan || ''}
downloadFileName={`${data.digest}.txt`}
/>
</Tabs.TabPane>

{binaryPlanObj && !binaryPlanObj.discardedDueToTooLong && (
<Tabs.TabPane
tab={t('slow_query.detail.plan.visual')}
key="binary_plan"
Expand All @@ -234,12 +226,12 @@ function DetailPage() {
height: window.innerHeight - 100
}}
>
<VisualPlanView data={binaryPlan} />
<VisualPlanView data={binaryPlanObj} />
</Modal>
<Descriptions>
<Descriptions.Item span={2}>
<div onClick={() => toggleVisualPlan('open')}>
<VisualPlanThumbnailView data={binaryPlan} />
<VisualPlanThumbnailView data={binaryPlanObj} />
</div>
</Descriptions.Item>
</Descriptions>
Expand Down
Loading

0 comments on commit 9c80a91

Please sign in to comment.