queries.ts 20 KB

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