queries.ts 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732
  1. import {Moment, unix} from 'moment';
  2. import {EventTransaction, NewQuery} from 'sentry/types';
  3. import {
  4. DiscoverQueryComponentProps,
  5. DiscoverQueryPropsWithThresholds,
  6. useDiscoverQuery,
  7. } from 'sentry/utils/discover/discoverQuery';
  8. import EventView from 'sentry/utils/discover/eventView';
  9. import {useGenericDiscoverQuery} from 'sentry/utils/discover/genericDiscoverQuery';
  10. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  11. import {DefinedUseQueryResult, useQuery} from 'sentry/utils/queryClient';
  12. import {useLocation} from 'sentry/utils/useLocation';
  13. import useOrganization from 'sentry/utils/useOrganization';
  14. import usePageFilters from 'sentry/utils/usePageFilters';
  15. import {DataRow} from 'sentry/views/starfish/components/databaseTableView';
  16. import {HOST} from 'sentry/views/starfish/utils/constants';
  17. import {
  18. datetimeToClickhouseFilterTimestamps,
  19. getDateFilters,
  20. } from 'sentry/views/starfish/utils/dates';
  21. import {getDateQueryFilter} from 'sentry/views/starfish/utils/getDateQueryFilter';
  22. import {
  23. UseSpansQueryReturnType,
  24. useWrappedDiscoverTimeseriesQuery,
  25. } from 'sentry/views/starfish/utils/useSpansQuery';
  26. export type TransactionListDataRow = {
  27. count: number;
  28. example: string;
  29. frequency: number;
  30. group_id: string;
  31. p75: number;
  32. transaction: string;
  33. uniqueEvents: number;
  34. };
  35. export const DEFAULT_WHERE = `
  36. startsWith(span_operation, 'db') and
  37. span_operation != 'db.redis' and
  38. module = 'db' and
  39. action != ''
  40. `;
  41. const SPM =
  42. 'if(duration > 0, divide(count(), (max(start_timestamp) - min(start_timestamp) as duration)/60), 0)';
  43. const ORDERBY = `
  44. -sum(exclusive_time), -count()
  45. `;
  46. const getActionSubquery = (date_filters: string) => {
  47. return `
  48. select action
  49. from default.spans_experimental_starfish
  50. where
  51. ${DEFAULT_WHERE}
  52. ${date_filters}
  53. group by action
  54. order by ${ORDERBY}
  55. limit 5
  56. `;
  57. };
  58. const getDomainSubquery = (date_filters: string) => {
  59. return `
  60. select domain
  61. from default.spans_experimental_starfish
  62. where
  63. ${DEFAULT_WHERE}
  64. ${date_filters} and
  65. domain != ''
  66. group by domain
  67. having
  68. ${SPM} > 0.05
  69. order by ${ORDERBY}
  70. limit 5
  71. `;
  72. };
  73. const getTransactionsFromTableSubquery = (tableNames: string[], dateFilters: string) => {
  74. const tableFilter = `domain IN ('${tableNames.join(`', '`)}')`;
  75. const filters = [DEFAULT_WHERE, tableFilter];
  76. return `
  77. SELECT
  78. transaction
  79. FROM default.spans_experimental_starfish
  80. WHERE
  81. ${filters.join(' AND ')}
  82. ${dateFilters}
  83. GROUP BY transaction
  84. ORDER BY ${ORDERBY}
  85. LIMIT 5
  86. `;
  87. };
  88. const SEVEN_DAYS = 7 * 24 * 60 * 60;
  89. const getNewColumn = (duration: number, startTime: Moment, endTime: Moment) => {
  90. const {start_timestamp, end_timestamp} = datetimeToClickhouseFilterTimestamps({
  91. start: unix(startTime.unix() + duration / 10).format('YYYY-MM-DD HH:mm:ss'),
  92. end: unix(endTime.unix() - duration / 10).format('YYYY-MM-DD HH:mm:ss'),
  93. });
  94. return duration > SEVEN_DAYS
  95. ? `(
  96. greater(min(start_timestamp), '${start_timestamp}') and
  97. greater(max(start_timestamp), '${end_timestamp}')
  98. ) as newish`
  99. : '0 as newish';
  100. };
  101. const getRetiredColumn = (duration: number, startTime: Moment, endTime: Moment) => {
  102. const {start_timestamp, end_timestamp} = datetimeToClickhouseFilterTimestamps({
  103. start: unix(startTime.unix() + duration / 10).format('YYYY-MM-DD HH:mm:ss'),
  104. end: unix(endTime.unix() - duration / 10).format('YYYY-MM-DD HH:mm:ss'),
  105. });
  106. return duration > SEVEN_DAYS
  107. ? `(
  108. less(max(start_timestamp), '${end_timestamp}') and
  109. less(min(start_timestamp), '${start_timestamp}')
  110. ) as retired`
  111. : '0 as retired';
  112. };
  113. export const useQueryDbTables = (): DefinedUseQueryResult<
  114. {key: string; value: string}[]
  115. > => {
  116. const pageFilter = usePageFilters();
  117. const {startTime, endTime} = getDateFilters(pageFilter);
  118. const dateFilters = getDateQueryFilter(startTime, endTime);
  119. const query = `
  120. select
  121. domain as key,
  122. quantile(0.75)(exclusive_time) as value
  123. from default.spans_experimental_starfish
  124. where
  125. ${DEFAULT_WHERE}
  126. ${dateFilters}
  127. group by domain
  128. order by ${ORDERBY}
  129. `;
  130. return useQuery({
  131. queryKey: ['table', pageFilter.selection.datetime],
  132. queryFn: () => fetch(`${HOST}/?query=${query}`).then(res => res.json()),
  133. retry: false,
  134. initialData: [],
  135. });
  136. };
  137. export const useQueryTopDbOperationsChart = (
  138. interval: number
  139. ): DefinedUseQueryResult<
  140. {action: string; count: number; interval: string; p50: number}[]
  141. > => {
  142. const pageFilter = usePageFilters();
  143. const {startTime, endTime} = getDateFilters(pageFilter);
  144. const dateFilters = getDateQueryFilter(startTime, endTime);
  145. const query = `
  146. select
  147. floor(quantile(0.50)(exclusive_time), 5) as p50,
  148. action,
  149. count() as count,
  150. toStartOfInterval(start_timestamp, INTERVAL ${interval} hour) as interval
  151. from default.spans_experimental_starfish
  152. where
  153. ${DEFAULT_WHERE}
  154. ${dateFilters} and
  155. action in (${getActionSubquery(dateFilters)})
  156. group by action, interval
  157. order by action, interval
  158. `;
  159. return useQuery({
  160. queryKey: ['topGraph', pageFilter.selection.datetime],
  161. queryFn: () => fetch(`${HOST}/?query=${query}`).then(res => res.json()),
  162. retry: false,
  163. initialData: [],
  164. });
  165. };
  166. type TopTransactionData = {
  167. interval: string;
  168. transaction: string;
  169. epm?: number;
  170. p75?: number;
  171. };
  172. export const useGetTransactionsForTables = (
  173. tableNames: string[],
  174. interval: number
  175. ): DefinedUseQueryResult<TopTransactionData[]> => {
  176. const pageFilter = usePageFilters();
  177. const location = useLocation();
  178. const {startTime, endTime} = getDateFilters(pageFilter);
  179. const dateFilters = getDateQueryFilter(startTime, endTime);
  180. const transactionNameQuery = getTransactionsFromTableSubquery(tableNames, dateFilters);
  181. const {start, end, period} = pageFilter.selection.datetime;
  182. const result1 = useQuery<{transaction: string}[]>({
  183. enabled: !!tableNames?.length,
  184. queryKey: ['topTransactionNames', tableNames.join(','), start, end],
  185. queryFn: () =>
  186. fetch(`${HOST}/?query=${transactionNameQuery}`).then(res => res.json()),
  187. retry: false,
  188. initialData: [],
  189. });
  190. const query: NewQuery = {
  191. id: undefined,
  192. name: 'Db module - epm/p75 for top transactions',
  193. query: `transaction:[${result1.data?.map(d => d.transaction).join(',')}]`,
  194. projects: [1],
  195. fields: ['transaction', 'epm()', 'p75(transaction.duration)'],
  196. version: 1,
  197. topEvents: '5',
  198. start: start?.toString(),
  199. end: end?.toString(),
  200. dataset: DiscoverDatasets.METRICS_ENHANCED,
  201. interval: `${interval}h`,
  202. yAxis: ['epm()', 'p75(transaction.duration)'],
  203. };
  204. const eventView = EventView.fromNewQueryWithLocation(query, location);
  205. eventView.statsPeriod = period ?? undefined;
  206. const result2 = useDiscoverEventsStatsQuery({
  207. eventView,
  208. referrer: 'api.starfish.database.charts',
  209. location,
  210. orgSlug: 'sentry',
  211. queryExtras: {
  212. interval: `${interval}h`, // This interval isn't being propogated from eventView
  213. yAxis: ['epm()', 'p75(transaction.duration)'], // workaround - eventView actually doesn't support multiple yAxis
  214. excludeOther: '1',
  215. topEvents: '5',
  216. per_page: undefined,
  217. },
  218. });
  219. const data: TopTransactionData[] = [];
  220. if (!result2.isLoading && result2.data) {
  221. Object.entries(result2.data).forEach(([transactionName, result]: [string, any]) => {
  222. result['epm()'].data.forEach(entry => {
  223. data.push({
  224. transaction: transactionName,
  225. interval: unix(entry[0]).format('YYYY-MM-DDTHH:mm:ss'),
  226. epm: entry[1][0].count,
  227. });
  228. });
  229. result['p75(transaction.duration)'].data.forEach(entry => {
  230. data.push({
  231. transaction: transactionName,
  232. interval: unix(entry[0]).format('YYYY-MM-DDTHH:mm:ss'),
  233. p75: entry[1][0].count,
  234. });
  235. });
  236. });
  237. }
  238. return {...result2, data} as DefinedUseQueryResult<TopTransactionData[]>;
  239. };
  240. type TopTableQuery = {
  241. count: number;
  242. domain: string;
  243. interval: string;
  244. p50: number;
  245. }[];
  246. export const useQueryTopTablesChart = (
  247. interval: number
  248. ): DefinedUseQueryResult<TopTableQuery> => {
  249. const pageFilter = usePageFilters();
  250. const {startTime, endTime} = getDateFilters(pageFilter);
  251. const dateFilters = getDateQueryFilter(startTime, endTime);
  252. const query = `
  253. select
  254. floor(quantile(0.50)(exclusive_time), 5) as p50,
  255. domain,
  256. divide(count(), multiply(${interval}, 60)) as count,
  257. toStartOfInterval(start_timestamp, INTERVAL ${interval} hour) as interval
  258. from default.spans_experimental_starfish
  259. where
  260. ${DEFAULT_WHERE}
  261. ${dateFilters} and
  262. domain in (${getDomainSubquery(dateFilters)})
  263. group by interval, domain
  264. order by interval, domain
  265. `;
  266. const result1 = useQuery<TopTableQuery>({
  267. queryKey: ['topTable', pageFilter.selection.datetime],
  268. queryFn: () => fetch(`${HOST}/?query=${query}`).then(res => res.json()),
  269. retry: false,
  270. initialData: [],
  271. });
  272. const tables = [...new Set(result1.data.map(d => d.domain))];
  273. const query2 = `
  274. select
  275. floor(quantile(0.50)(exclusive_time), 5) as p50,
  276. divide(count(), multiply(${interval}, 60)) as count,
  277. toStartOfInterval(start_timestamp, INTERVAL ${interval} hour) as interval
  278. from default.spans_experimental_starfish
  279. where
  280. domain not in ('${tables.join(`', '`)}')
  281. AND ${DEFAULT_WHERE}
  282. ${dateFilters}
  283. group by interval
  284. order by interval
  285. `;
  286. const result2 = useQuery<TopTableQuery>({
  287. enabled: !result1.isLoading && !!result1.data?.length,
  288. queryKey: ['topTableOther', pageFilter.selection.datetime],
  289. queryFn: () => fetch(`${HOST}/?query=${query2}`).then(res => res.json()),
  290. retry: false,
  291. initialData: [],
  292. });
  293. result2.data.forEach(d => (d.domain = 'other'));
  294. const joinedData = [...result1.data, ...result2.data];
  295. return {...result2, data: joinedData};
  296. };
  297. export const useQueryPanelTable = (
  298. row: DataRow,
  299. sortKey: string | undefined,
  300. sortDirection: string | undefined,
  301. transaction: string | undefined
  302. ): DefinedUseQueryResult<
  303. Pick<TransactionListDataRow, 'transaction' | 'count' | 'p75'>[]
  304. > => {
  305. const pageFilter = usePageFilters();
  306. const {startTime, endTime} = getDateFilters(pageFilter);
  307. const dateFilters = getDateQueryFilter(startTime, endTime);
  308. const orderBy = getOrderByFromKey(sortKey, sortDirection) ?? ORDERBY;
  309. const transactionFilter = transaction ? `and transaction='${transaction}'` : '';
  310. const query = `
  311. SELECT
  312. transaction,
  313. count() AS count,
  314. quantile(0.75)(exclusive_time) as p75,
  315. any(transaction_id) as example
  316. FROM spans_experimental_starfish
  317. WHERE
  318. ${DEFAULT_WHERE}
  319. ${dateFilters} AND
  320. group_id = '${row.group_id}'
  321. ${transactionFilter}
  322. GROUP BY transaction
  323. ORDER BY ${orderBy}
  324. LIMIT 5
  325. `;
  326. return useQuery({
  327. queryKey: [
  328. 'dbQueryDetailsTable',
  329. row.group_id,
  330. pageFilter.selection.datetime,
  331. sortKey,
  332. sortDirection,
  333. ],
  334. queryFn: () => fetch(`${HOST}/?query=${query}`).then(res => res.json()),
  335. retry: true,
  336. initialData: [],
  337. });
  338. };
  339. export const useQueryExampleTransaction = (
  340. row: DataRow
  341. ): DefinedUseQueryResult<{first: string; latest: string}[]> => {
  342. const pageFilter = usePageFilters();
  343. const {startTime, endTime} = getDateFilters(pageFilter);
  344. const dateFilters = getDateQueryFilter(startTime, endTime);
  345. const query = `
  346. SELECT
  347. minIf(transaction_id, equals(timestamp, '${row.lastSeen}')) as latest,
  348. minIf(transaction_id, equals(timestamp, '${row.firstSeen}')) as first
  349. FROM spans_experimental_starfish
  350. WHERE
  351. ${DEFAULT_WHERE}
  352. ${dateFilters} AND
  353. group_id = '${row.group_id}'
  354. HAVING latest > 0 and first > 0
  355. LIMIT 10
  356. `;
  357. return useQuery({
  358. queryKey: ['getExampleTransaction', row.group_id],
  359. queryFn: () => fetch(`${HOST}/?query=${query}`).then(res => res.json()),
  360. retry: true,
  361. initialData: [],
  362. });
  363. };
  364. export const useQueryPanelSparklines = (
  365. row: DataRow,
  366. sortKey: string | undefined,
  367. sortDirection: string | undefined,
  368. interval: number,
  369. transaction: string | undefined
  370. ): DefinedUseQueryResult<{interval: string; spm: number; transaction: string}[]> => {
  371. const pageFilter = usePageFilters();
  372. const {startTime, endTime} = getDateFilters(pageFilter);
  373. const dateFilters = getDateQueryFilter(startTime, endTime);
  374. const orderBy = getOrderByFromKey(sortKey, sortDirection) ?? ORDERBY;
  375. const transactionFilter = transaction ? `and transaction='${transaction}'` : '';
  376. const query = `
  377. SELECT
  378. transaction,
  379. toStartOfInterval(start_timestamp, INTERVAL ${interval} hour) as interval,
  380. quantile(0.50)(exclusive_time) AS p50,
  381. divide(count(), multiply(${interval}, 60)) as spm
  382. FROM spans_experimental_starfish
  383. WHERE
  384. transaction in (
  385. SELECT
  386. transaction
  387. FROM spans_experimental_starfish
  388. WHERE
  389. ${DEFAULT_WHERE}
  390. ${dateFilters} AND
  391. group_id = '${row.group_id}'
  392. ${transactionFilter}
  393. GROUP BY transaction
  394. ORDER BY ${orderBy}
  395. LIMIT 5
  396. ) and
  397. ${DEFAULT_WHERE}
  398. ${dateFilters} AND
  399. group_id = '${row.group_id}'
  400. GROUP BY transaction, interval
  401. ORDER BY transaction, interval, ${orderBy}
  402. `;
  403. return useQuery({
  404. queryKey: [
  405. 'dbQueryDetailsSparklines',
  406. row.group_id,
  407. pageFilter.selection.datetime,
  408. sortKey,
  409. sortDirection,
  410. ],
  411. queryFn: () => fetch(`${HOST}/?query=${query}`).then(res => res.json()),
  412. retry: true,
  413. initialData: [],
  414. });
  415. };
  416. export const useQueryPanelGraph = (row: DataRow, interval: number) => {
  417. const pageFilter = usePageFilters();
  418. const {startTime, endTime} = getDateFilters(pageFilter);
  419. const dateFilters = getDateQueryFilter(startTime, endTime);
  420. const query = `
  421. SELECT
  422. toStartOfInterval(start_timestamp, INTERVAL ${interval} HOUR) as interval,
  423. quantile(0.95)(exclusive_time) as p95,
  424. quantile(0.50)(exclusive_time) as p50,
  425. divide(count(), multiply(${interval}, 60)) as count
  426. FROM spans_experimental_starfish
  427. WHERE
  428. ${DEFAULT_WHERE}
  429. ${dateFilters} AND
  430. group_id = '${row.group_id}'
  431. GROUP BY interval
  432. ORDER BY interval
  433. `;
  434. return useQuery({
  435. queryKey: ['dbQueryDetailsGraph', row.group_id, pageFilter.selection.datetime],
  436. queryFn: () => fetch(`${HOST}/?query=${query}&format=sql`).then(res => res.json()),
  437. retry: false,
  438. initialData: [],
  439. });
  440. };
  441. export const useQueryPanelEventCount = (
  442. row: DataRow
  443. ): DefinedUseQueryResult<Pick<TransactionListDataRow, 'uniqueEvents' | 'count'>[]> => {
  444. const pageFilter = usePageFilters();
  445. const {startTime, endTime} = getDateFilters(pageFilter);
  446. const dateFilters = getDateQueryFilter(startTime, endTime);
  447. const query = `
  448. SELECT
  449. transaction,
  450. count(DISTINCT transaction_id) as uniqueEvents
  451. FROM spans_experimental_starfish
  452. WHERE
  453. ${DEFAULT_WHERE}
  454. ${dateFilters} AND
  455. group_id = '${row.group_id}'
  456. GROUP BY transaction
  457. ORDER BY ${ORDERBY}
  458. `;
  459. return useQuery({
  460. queryKey: ['dbQueryDetailsEventCount', row.group_id, pageFilter.selection.datetime],
  461. queryFn: () => fetch(`${HOST}/?query=${query}`).then(res => res.json()),
  462. retry: true,
  463. initialData: [],
  464. });
  465. };
  466. export const useQueryMainTable = (options: {
  467. action?: string;
  468. filterNew?: boolean;
  469. filterOld?: boolean;
  470. filterOutlier?: boolean;
  471. limit?: number;
  472. sortDirection?: string;
  473. sortKey?: string;
  474. table?: string;
  475. transaction?: string;
  476. }): DefinedUseQueryResult<DataRow[]> => {
  477. const {
  478. action,
  479. filterNew,
  480. filterOld,
  481. filterOutlier,
  482. sortDirection,
  483. sortKey,
  484. table,
  485. transaction,
  486. limit,
  487. } = options;
  488. const pageFilter = usePageFilters();
  489. const {startTime, endTime} = getDateFilters(pageFilter);
  490. const dateFilters = getDateQueryFilter(startTime, endTime);
  491. const transactionFilter = transaction ? `transaction='${transaction}'` : null;
  492. const tableFilter = table && table !== 'ALL' ? `domain = '${table}'` : undefined;
  493. const actionFilter = action && action !== 'ALL' ? `action = '${action}'` : undefined;
  494. const newFilter: string | undefined = filterNew ? 'newish = 1' : undefined;
  495. const oldFilter: string | undefined = filterOld ? 'retired = 1' : undefined;
  496. const outlierFilter: string | undefined = filterOutlier ? `${SPM} > 0.02` : undefined;
  497. const filters = [DEFAULT_WHERE, transactionFilter, tableFilter, actionFilter].filter(
  498. fil => !!fil
  499. );
  500. const duration = endTime.unix() - startTime.unix();
  501. const newColumn = getNewColumn(duration, startTime, endTime);
  502. const retiredColumn = getRetiredColumn(duration, startTime, endTime);
  503. const havingFilters = [newFilter, oldFilter, outlierFilter].filter(fil => !!fil);
  504. const orderBy = getOrderByFromKey(sortKey, sortDirection) ?? ORDERBY;
  505. const query = `
  506. select
  507. description,
  508. group_id, count() as count,
  509. ${SPM} as epm,
  510. quantile(0.75)(exclusive_time) as p75,
  511. quantile(0.50)(exclusive_time) as p50,
  512. quantile(0.95)(exclusive_time) as p95,
  513. uniq(transaction) as transactions,
  514. sum(exclusive_time) as total_time,
  515. domain,
  516. action,
  517. data_keys,
  518. data_values,
  519. min(start_timestamp) as firstSeen,
  520. max(start_timestamp) as lastSeen,
  521. ${newColumn},
  522. ${retiredColumn}
  523. from default.spans_experimental_starfish
  524. where
  525. ${filters.join(' AND ')}
  526. ${dateFilters}
  527. group by
  528. action,
  529. description,
  530. group_id,
  531. domain,
  532. data_keys,
  533. data_values
  534. ${havingFilters.length > 0 ? 'having' : ''}
  535. ${havingFilters.join(' and ')}
  536. order by ${orderBy}
  537. limit ${limit ?? 50}
  538. `;
  539. return useQuery<DataRow[]>({
  540. queryKey: [
  541. 'endpoints',
  542. transaction,
  543. table,
  544. pageFilter.selection.datetime,
  545. sortKey,
  546. sortDirection,
  547. newFilter,
  548. oldFilter,
  549. outlierFilter,
  550. ],
  551. cacheTime: 10000,
  552. queryFn: () => fetch(`${HOST}/?query=${query}&format=sql`).then(res => res.json()),
  553. retry: false,
  554. initialData: [],
  555. });
  556. };
  557. type QueryTransactionByTPMAndP75ReturnType = {
  558. count: number;
  559. 'count()': number;
  560. interval: string;
  561. 'p50(transaction.duration)': number;
  562. 'p95(transaction.duration)': number;
  563. transaction: string;
  564. }[];
  565. export const useQueryTransactionByTPMAndDuration = (
  566. transactionNames: string[],
  567. interval: number
  568. ): UseSpansQueryReturnType<QueryTransactionByTPMAndP75ReturnType> => {
  569. const {
  570. selection: {datetime},
  571. } = usePageFilters();
  572. return useWrappedDiscoverTimeseriesQuery({
  573. eventView: EventView.fromSavedQuery({
  574. name: '',
  575. fields: [
  576. 'transaction',
  577. 'epm()',
  578. 'p50(transaction.duration)',
  579. 'p95(transaction.duration)',
  580. ],
  581. yAxis: ['epm()', 'p50(transaction.duration)', 'p95(transaction.duration)'],
  582. orderby: '-count',
  583. query: `transaction:["${transactionNames.join('","')}"]`,
  584. topEvents: '5',
  585. start: datetime.start as string,
  586. end: datetime.end as string,
  587. range: datetime.period as string,
  588. dataset: DiscoverDatasets.METRICS,
  589. interval: `${interval}h`,
  590. projects: [1],
  591. version: 2,
  592. }),
  593. initialData: [],
  594. });
  595. };
  596. export const useQueryGetProfileIds = (transactionNames: string[]) => {
  597. const location = useLocation();
  598. const {slug: orgSlug} = useOrganization();
  599. const eventView = EventView.fromNewQueryWithLocation(
  600. {
  601. fields: ['transaction'],
  602. name: 'Db module - profile',
  603. query: `transaction:[${transactionNames.join(',')}] has:profile.id`,
  604. projects: [1],
  605. version: 1,
  606. orderby: 'id',
  607. },
  608. location
  609. );
  610. return useDiscoverQuery({eventView, location, orgSlug, queryExtras: {per_page: '10'}});
  611. };
  612. export const useQueryGetEvent = (
  613. transactionEventId?: string
  614. ): DefinedUseQueryResult<EventTransaction> => {
  615. const path = `/api/0/projects/sentry/sentry/events/${transactionEventId?.replaceAll(
  616. '-',
  617. ''
  618. )}/`;
  619. return useQuery({
  620. enabled: !!transactionEventId,
  621. queryKey: ['event', transactionEventId],
  622. queryFn: () => fetch(path).then(res => res.json()),
  623. retry: false,
  624. initialData: {},
  625. });
  626. };
  627. const getOrderByFromKey = (
  628. sortKey: string | undefined,
  629. sortDirection: string | undefined
  630. ) => {
  631. if (!sortDirection || !sortKey) {
  632. return undefined;
  633. }
  634. sortDirection ??= '';
  635. return `${sortKey} ${sortDirection}`;
  636. };
  637. const shouldRefetchData = (
  638. prevProps: DiscoverQueryPropsWithThresholds,
  639. nextProps: DiscoverQueryPropsWithThresholds
  640. ) => {
  641. return (
  642. prevProps.transactionName !== nextProps.transactionName ||
  643. prevProps.transactionThreshold !== nextProps.transactionThreshold ||
  644. prevProps.transactionThresholdMetric !== nextProps.transactionThresholdMetric
  645. );
  646. };
  647. // We should find a way to use this in discover
  648. export function useDiscoverEventsStatsQuery<T>(
  649. props: Omit<DiscoverQueryComponentProps, 'children'>
  650. ) {
  651. const afterFetch = (data, _) => {
  652. const {fields, ...otherMeta} = data.meta ?? {};
  653. return {
  654. ...data,
  655. meta: {...fields, ...otherMeta},
  656. };
  657. };
  658. return useGenericDiscoverQuery<T, unknown>({
  659. route: 'events-stats',
  660. shouldRefetchData,
  661. afterFetch,
  662. ...props,
  663. });
  664. }
  665. export const getDbAggregatesQuery = ({datetime, transaction}) => {
  666. const {start_timestamp, end_timestamp} = datetimeToClickhouseFilterTimestamps(datetime);
  667. return `
  668. SELECT
  669. description,
  670. toStartOfInterval(start_timestamp, INTERVAL 12 HOUR) as interval,
  671. divide(count(), multiply(12, 60)) as count,
  672. quantile(0.50)(exclusive_time) as p50,
  673. quantile(0.95)(exclusive_time) as p95
  674. FROM spans_experimental_starfish
  675. WHERE module = 'db'
  676. ${transaction ? `AND transaction = '${transaction}'` : ''}
  677. ${start_timestamp ? `AND greaterOrEquals(start_timestamp, '${start_timestamp}')` : ''}
  678. ${end_timestamp ? `AND lessOrEquals(start_timestamp, '${end_timestamp}')` : ''}
  679. GROUP BY description, interval
  680. ORDER BY interval asc
  681. `;
  682. };