import {useState} from 'react'; import {Theme, useTheme} from '@emotion/react'; import styled from '@emotion/styled'; import keyBy from 'lodash/keyBy'; import moment from 'moment'; import Badge from 'sentry/components/badge'; import {Button} from 'sentry/components/button'; import ButtonBar from 'sentry/components/buttonBar'; import MarkLine from 'sentry/components/charts/components/markLine'; import {IconChevron} from 'sentry/icons'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import {Series, SeriesDataUnit} from 'sentry/types/echarts'; import usePageFilters from 'sentry/utils/usePageFilters'; import Chart from 'sentry/views/starfish/components/chart'; import Detail from 'sentry/views/starfish/components/detailPanel'; import ProfileView from 'sentry/views/starfish/modules/databaseModule/panel/profileView'; import QueryTransactionTable, { PanelSort, } from 'sentry/views/starfish/modules/databaseModule/panel/queryTransactionTable'; import SimilarQueryView from 'sentry/views/starfish/modules/databaseModule/panel/similarQueryView'; import { useQueryPanelEventCount, useQueryPanelGraph, useQueryPanelSparklines, useQueryPanelTable, useQueryTransactionByTPMAndP75, } from 'sentry/views/starfish/modules/databaseModule/queries'; import {queryToSeries} from 'sentry/views/starfish/modules/databaseModule/utils'; import {getDateFilters} from 'sentry/views/starfish/utils/dates'; import {zeroFillSeries} from 'sentry/views/starfish/utils/zeroFillSeries'; import {DataRow, MainTableSort} from '../databaseTableView'; const INTERVAL = 12; const SPARKLINE_INTERVAL = 24; type DbQueryDetailProps = { isDataLoading: boolean; mainTableSort: MainTableSort; onRowChange: (row: DataRow | undefined) => void; row: DataRow; nextRow?: DataRow; prevRow?: DataRow; transaction?: string; }; export type TransactionListDataRow = { count: number; frequency: number; group_id: string; p75: number; transaction: string; uniqueEvents: number; }; export default function QueryDetail({ row, nextRow, prevRow, isDataLoading, onClose, onRowChange, mainTableSort, transaction, }: Partial & { isDataLoading: boolean; mainTableSort: MainTableSort; onClose: () => void; onRowChange: (row: DataRow) => void; }) { return ( {row && ( )} ); } function QueryDetailBody({ row, nextRow, prevRow, onRowChange, transaction, isDataLoading: isRowLoading, }: DbQueryDetailProps) { const theme = useTheme(); const pageFilter = usePageFilters(); const {startTime, endTime} = getDateFilters(pageFilter); const isNew = row.newish === 1; const isOld = row.retired === 1; const [sort, setSort] = useState({ direction: undefined, sortHeader: undefined, }); const {isLoading, data: graphData} = useQueryPanelGraph(row, INTERVAL); const {isLoading: isTableLoading, data: tableData} = useQueryPanelTable( row, sort.sortHeader?.key, sort.direction, transaction ); const {isLoading: isSparklinesLoading, data: sparklineData} = useQueryPanelSparklines( row, sort.sortHeader?.key, sort.direction, SPARKLINE_INTERVAL, transaction ); const {isLoading: isP75GraphLoading, data: transactionGraphData} = useQueryTransactionByTPMAndP75( tableData.map(d => d.transaction).splice(0, 5), SPARKLINE_INTERVAL ); const {isLoading: isEventCountLoading, data: eventCountData} = useQueryPanelEventCount(row); const isDataLoading = isLoading || isTableLoading || isEventCountLoading || isRowLoading || isP75GraphLoading || isSparklinesLoading; const eventCountMap = keyBy(eventCountData, 'transaction'); const mergedTableData: TransactionListDataRow[] = tableData.map(data => { const tableTransaction = data.transaction; const eventData = eventCountMap[tableTransaction]; if (eventData?.uniqueEvents) { const frequency = data.count / eventData.uniqueEvents; return {...data, frequency, ...eventData} as TransactionListDataRow; } return data as TransactionListDataRow; }); const [countSeries, p75Series] = throughputQueryToChartData( graphData, startTime, endTime ); const spmTransactionSeries = queryToSeries( sparklineData, 'transaction', 'spm', startTime, endTime, SPARKLINE_INTERVAL ); const spanp50TransactionSeries = queryToSeries( sparklineData, 'transaction', 'p50', startTime, endTime, SPARKLINE_INTERVAL ); const tpmTransactionSeries = queryToSeries( transactionGraphData, 'group', 'epm()', startTime, endTime, SPARKLINE_INTERVAL ); const p50TransactionSeries = queryToSeries( transactionGraphData, 'group', 'p50(transaction.duration)', startTime, endTime, SPARKLINE_INTERVAL ); const markLine = spmTransactionSeries?.[0]?.data && (isNew || isOld) ? generateMarkLine( isNew ? 'First Seen' : 'Last Seen', isNew ? row.firstSeen : row.lastSeen, spmTransactionSeries[0].data, theme ) : undefined; return (

{t('Query Detail')}

onRowChange(prevRow)} onRightClick={() => onRowChange(nextRow)} />
{t('First Seen')} {row.newish === 1 && } {row.firstSeen} {t('Last Seen')} {row.retired === 1 && } {row.lastSeen} {t('Total Time')} {row.total_time.toFixed(2)}ms {t('Query Description')} {highlightSql(row.formatted_desc, row)} {t('Throughput')} {row.epm.toFixed(3)} {t('Duration (P75)')} {row.p75.toFixed(3)}ms setSort(s)} row={row} sort={sort} tableData={mergedTableData} spmData={spmTransactionSeries} tpmData={tpmTransactionSeries} spanP50Data={spanp50TransactionSeries} txnP50Data={p50TransactionSeries} markLine={markLine} /> {t('Example Profile')} d.transaction)} /> {t('Similar Queries')}
); } type SimplePaginationProps = { disableLeft?: boolean; disableRight?: boolean; onLeftClick?: () => void; onRightClick?: () => void; }; function SimplePagination(props: SimplePaginationProps) { return (