queries.ts 22 KB

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