import {useState} from 'react'; import {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 TimeSince from 'sentry/components/timeSince'; import Version from 'sentry/components/version'; import VersionHoverCard from 'sentry/components/versionHoverCard'; import {IconChevron} from 'sentry/icons'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import {Series} from 'sentry/types/echarts'; import useOrganization from 'sentry/utils/useOrganization'; import usePageFilters from 'sentry/utils/usePageFilters'; import Chart from 'sentry/views/starfish/components/chart'; import Detail from 'sentry/views/starfish/components/detailPanel'; import {FormattedCode} from 'sentry/views/starfish/components/formattedCode'; import {generateMarkLine} from 'sentry/views/starfish/components/sparkline'; 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 { useQueryExampleTransaction, useQueryGetEvent, useQueryPanelEventCount, useQueryPanelGraph, useQueryPanelSparklines, useQueryPanelTable, useQueryTransactionByTPMAndDuration, } 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; example: string; 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 organization = useOrganization(); 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} = useQueryTransactionByTPMAndDuration( tableData.map(d => d.transaction).splice(0, 5), SPARKLINE_INTERVAL ); const {isLoading: isEventCountLoading, data: eventCountData} = useQueryPanelEventCount(row); const {isLoading: isExampleLoading, data: exampleTransaction} = useQueryExampleTransaction(row); const {isLoading: isFirstExampleLoading, data: firstSeenExample} = useQueryGetEvent( exampleTransaction?.[0]?.first ); const {isLoading: isLastExampleLoading, data: lastSeenExample} = useQueryGetEvent( exampleTransaction?.[0]?.latest ); const isDataLoading = isLoading || isTableLoading || isEventCountLoading || isRowLoading || isP75GraphLoading || isExampleLoading || isFirstExampleLoading || isLastExampleLoading || 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, p50Series, p95Series] = 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 && } {Math.abs(moment(row.firstSeen).diff(startTime, 'minutes')) < 720 ? ( More than {' '} ) : ( {' '} {firstSeenExample?.release && ( )} )} {t('Last Seen')} {row.retired === 1 && } {lastSeenExample?.release && ( )} {t('Total Time')} {row.total_time.toFixed(2)}ms {t('Query Description')} {highlightSql(row.formatted_desc, row)} {t('Throughput (Spans Per Minute)')} {row.epm.toFixed(3)} {t('Duration P50 / P95')} {row.p50.toFixed(3)}ms / {row.p95.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 (