queries.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. import {DateTimeObject} from 'sentry/components/charts/utils';
  2. import {DefinedUseQueryResult, useQueries, useQuery} from 'sentry/utils/queryClient';
  3. import usePageFilters from 'sentry/utils/usePageFilters';
  4. import {getEndpointDetailSeriesQuery} from 'sentry/views/starfish/modules/APIModule/queries';
  5. import {HOST} from 'sentry/views/starfish/utils/constants';
  6. import {
  7. datetimeToClickhouseFilterTimestamps,
  8. getDateFilters,
  9. } from 'sentry/views/starfish/utils/dates';
  10. import {getDateQueryFilter} from 'sentry/views/starfish/utils/getDateQueryFilter';
  11. export enum SamplePopulationType {
  12. FASTEST = 'fastest',
  13. MEDIAN = 'median',
  14. SLOWEST = 'slowest',
  15. }
  16. export const useQueryGetSpanSamples = (options: {
  17. groupId: string;
  18. transactionName: string;
  19. p50?: number;
  20. user?: string;
  21. }) => {
  22. const {groupId, transactionName, user, p50} = options;
  23. const pageFilter = usePageFilters();
  24. const commonQueryOptions = {
  25. queryKey: [
  26. groupId,
  27. transactionName,
  28. user,
  29. pageFilter.selection.datetime,
  30. 'spanSamples',
  31. ],
  32. retry: false,
  33. initialData: [],
  34. enabled: Boolean(groupId && transactionName && p50),
  35. };
  36. const commonSamplesQueryOptions: GetSamplesQueryOptions = {
  37. groupId,
  38. transactionName,
  39. user,
  40. datetime: pageFilter.selection.datetime,
  41. p50,
  42. };
  43. const results = useQueries({
  44. queries: [
  45. {
  46. ...commonQueryOptions,
  47. queryKey: [...commonQueryOptions.queryKey, 'spanSamplesSlowest'],
  48. queryFn: () =>
  49. fetch(
  50. `${HOST}/?query=${getSpanSamplesQuery({
  51. ...commonSamplesQueryOptions,
  52. populationType: SamplePopulationType.SLOWEST,
  53. })}`
  54. ).then(res => res.json()),
  55. },
  56. {
  57. ...commonQueryOptions,
  58. queryKey: [...commonQueryOptions.queryKey, 'spanSamplesMedian'],
  59. queryFn: () =>
  60. fetch(
  61. `${HOST}/?query=${getSpanSamplesQuery({
  62. ...commonSamplesQueryOptions,
  63. populationType: SamplePopulationType.MEDIAN,
  64. })}`
  65. ).then(res => res.json()),
  66. },
  67. {
  68. ...commonQueryOptions,
  69. queryKey: [...commonQueryOptions.queryKey, 'spanSamplesFastest'],
  70. queryFn: () =>
  71. fetch(
  72. `${HOST}/?query=${getSpanSamplesQuery({
  73. ...commonSamplesQueryOptions,
  74. populationType: SamplePopulationType.FASTEST,
  75. })}`
  76. ).then(res => res.json()),
  77. },
  78. ],
  79. });
  80. return results;
  81. };
  82. export const useQueryGetFacetsBreakdown = (options: {
  83. groupId: string;
  84. transactionName: string;
  85. }): DefinedUseQueryResult<{domain: string; user: string}[]> => {
  86. const {groupId, transactionName} = options;
  87. const pageFilter = usePageFilters();
  88. const {startTime, endTime} = getDateFilters(pageFilter);
  89. const dateFilters = getDateQueryFilter(startTime, endTime);
  90. const query = `
  91. SELECT
  92. user, domain
  93. FROM spans_experimental_starfish
  94. WHERE
  95. group_id = '${groupId}'
  96. AND transaction = '${transactionName}'
  97. ${dateFilters}
  98. `;
  99. return useQuery({
  100. queryKey: ['facetBreakdown', groupId, transactionName],
  101. queryFn: () => fetch(`${HOST}/?query=${query}`).then(res => res.json()),
  102. retry: false,
  103. initialData: [],
  104. });
  105. };
  106. export const useQuerySpansInTransaction = (options: {
  107. groupId: string;
  108. }): DefinedUseQueryResult<
  109. {
  110. action: string;
  111. count: number;
  112. description: string;
  113. formatted_desc: string;
  114. module: 'http' | 'db' | 'cache' | 'none';
  115. p50: number;
  116. p95: number;
  117. span_operation: string;
  118. }[]
  119. > => {
  120. const {groupId} = options;
  121. const pageFilter = usePageFilters();
  122. const {startTime, endTime} = getDateFilters(pageFilter);
  123. const dateFilters = getDateQueryFilter(startTime, endTime);
  124. const query = `
  125. SELECT
  126. count() AS count,
  127. quantile(0.5)(exclusive_time) as p50,
  128. quantile(0.95)(exclusive_time) as p95,
  129. span_operation,
  130. action,
  131. module,
  132. description
  133. FROM
  134. spans_experimental_starfish
  135. WHERE
  136. group_id = '${groupId}'
  137. ${dateFilters}
  138. GROUP BY
  139. span_operation,
  140. description,
  141. action,
  142. module
  143. `;
  144. return useQuery({
  145. queryKey: ['spansInTransaction', groupId, dateFilters],
  146. queryFn: () => fetch(`${HOST}/?query=${query}&format=sql`).then(res => res.json()),
  147. retry: false,
  148. initialData: [],
  149. });
  150. };
  151. export const useQueryGetSpanAggregatesQuery = (options: {
  152. groupId: string;
  153. transactionName: string;
  154. description?: string;
  155. module?: string;
  156. }): DefinedUseQueryResult<
  157. {
  158. count: number;
  159. count_unique_transaction: number;
  160. count_unique_transaction_id: number;
  161. failure_rate: number;
  162. faiure_count: number;
  163. first_seen: string;
  164. last_seen: string;
  165. p50: number;
  166. p95: number;
  167. total_exclusive_time: number;
  168. }[]
  169. > => {
  170. const {description, groupId, transactionName, module} = options;
  171. const pageFilter = usePageFilters();
  172. const aggregatesQuery = getSidebarAggregatesQuery({
  173. description,
  174. transactionName,
  175. datetime: pageFilter.selection.datetime,
  176. groupId,
  177. module,
  178. });
  179. return useQuery({
  180. queryKey: ['span-aggregates', transactionName, groupId, description],
  181. queryFn: () =>
  182. fetch(`${HOST}/?query=${aggregatesQuery}&referrer=sidebar-aggregates`).then(res =>
  183. res.json()
  184. ),
  185. retry: false,
  186. initialData: [],
  187. });
  188. };
  189. export const useQueryGetSpanSeriesData = (options: {
  190. groupId: string;
  191. spanGroupOperation: string;
  192. transactionName: string;
  193. description?: string;
  194. module?: string;
  195. }): DefinedUseQueryResult<
  196. {
  197. count: number;
  198. failure_count: number;
  199. failure_rate: number;
  200. interval: string;
  201. p50: number;
  202. p95: number;
  203. spm: number;
  204. }[]
  205. > => {
  206. const {description, groupId, spanGroupOperation, transactionName, module} = options;
  207. const pageFilter = usePageFilters();
  208. const {getSeriesQuery} = getQueries(spanGroupOperation);
  209. const aggregatesQuery = getSeriesQuery({
  210. datetime: pageFilter.selection.datetime,
  211. groupId,
  212. module,
  213. description,
  214. interval: 12,
  215. transactionName,
  216. });
  217. return useQuery({
  218. queryKey: [
  219. 'seriesdata',
  220. transactionName,
  221. module,
  222. pageFilter.selection.datetime,
  223. groupId,
  224. ],
  225. queryFn: () =>
  226. fetch(`${HOST}/?query=${aggregatesQuery}&referrer=sidebar-aggregates`).then(res =>
  227. res.json()
  228. ),
  229. retry: false,
  230. initialData: [],
  231. });
  232. };
  233. type GetSamplesQueryOptions = {
  234. groupId: string;
  235. transactionName: string;
  236. datetime?: DateTimeObject;
  237. p50?: number;
  238. populationType?: SamplePopulationType;
  239. user?: string;
  240. };
  241. const getSpanSamplesQuery = ({
  242. groupId,
  243. transactionName,
  244. user,
  245. populationType,
  246. datetime,
  247. p50,
  248. }: GetSamplesQueryOptions) => {
  249. const {start_timestamp, end_timestamp} = datetimeToClickhouseFilterTimestamps(datetime);
  250. return `
  251. SELECT transaction_id, transaction, description, user, domain, span_id, sum(exclusive_time) as exclusive_time, abs(minus(exclusive_time, ${p50})) as diff
  252. FROM spans_experimental_starfish
  253. WHERE group_id = '${groupId}'
  254. ${transactionName ? `AND transaction = '${transactionName}'` : ''}
  255. ${user ? `AND user = '${user}'` : ''}
  256. ${start_timestamp ? `AND greaterOrEquals(start_timestamp, '${start_timestamp}')` : ''}
  257. ${end_timestamp ? `AND lessOrEquals(start_timestamp, '${end_timestamp}')` : ''}
  258. GROUP BY transaction_id, transaction, description, user, domain, span_id
  259. ORDER BY ${
  260. populationType === SamplePopulationType.SLOWEST || !populationType
  261. ? 'exclusive_time desc'
  262. : populationType === SamplePopulationType.FASTEST
  263. ? 'exclusive_time asc'
  264. : 'diff asc'
  265. }
  266. LIMIT 3
  267. `;
  268. };
  269. // Metrics request to get total count of events for a transaction
  270. export const useQueryGetUniqueTransactionCount = (options: {transactionName: string}) => {
  271. const {transactionName} = options;
  272. const {
  273. selection: {datetime},
  274. } = usePageFilters();
  275. const query = `?field=count%28%29&query=transaction%3A${encodeURIComponent(
  276. transactionName
  277. )}${
  278. datetime
  279. ? datetime.period
  280. ? `&statsPeriod=${datetime.period}`
  281. : datetime.start && datetime.end
  282. ? `&start=${encodeURIComponent(
  283. (datetime.start as Date).toISOString()
  284. )}&end=${encodeURIComponent((datetime.end as Date).toISOString())}`
  285. : null
  286. : null
  287. }&dataset=metricsEnhanced&project=1`;
  288. return useQuery({
  289. queryKey: ['uniqueTransactionCount', transactionName],
  290. queryFn: () =>
  291. fetch(`/api/0/organizations/sentry/events/${query}`).then(res => res.json()),
  292. retry: false,
  293. initialData: [],
  294. });
  295. };
  296. const getSidebarSeriesQuery = ({
  297. description,
  298. transactionName,
  299. datetime,
  300. groupId,
  301. module,
  302. interval,
  303. }) => {
  304. const {start_timestamp, end_timestamp} = datetimeToClickhouseFilterTimestamps(datetime);
  305. return `SELECT
  306. toStartOfInterval(start_timestamp, INTERVAL 12 HOUR) as interval,
  307. quantile(0.5)(exclusive_time) as p50,
  308. quantile(0.95)(exclusive_time) as p95,
  309. count() as count,
  310. divide(count(), multiply(${interval}, 60)) as spm,
  311. countIf(greaterOrEquals(status, 400) AND lessOrEquals(status, 599)) as failure_count,
  312. failure_count / count as failure_rate
  313. FROM spans_experimental_starfish
  314. WHERE module = '${module}'
  315. ${description ? `AND description = '${description}'` : ''}
  316. ${groupId ? `AND group_id = '${groupId}'` : ''}
  317. ${transactionName ? `AND transaction = '${transactionName}'` : ''}
  318. ${
  319. start_timestamp ? `AND greaterOrEquals(start_timestamp, '${start_timestamp}')` : ''
  320. }
  321. ${end_timestamp ? `AND lessOrEquals(start_timestamp, '${end_timestamp}')` : ''}
  322. GROUP BY interval
  323. ORDER BY interval asc
  324. `;
  325. };
  326. const getSidebarAggregatesQuery = ({
  327. description,
  328. transactionName,
  329. datetime,
  330. groupId,
  331. module,
  332. }) => {
  333. const {start_timestamp, end_timestamp} = datetimeToClickhouseFilterTimestamps(datetime);
  334. return `
  335. SELECT
  336. count() AS count,
  337. quantile(0.5)(exclusive_time) as p50,
  338. quantile(0.95)(exclusive_time) as p95,
  339. countIf(greaterOrEquals(status, 400) AND lessOrEquals(status, 599)) as failure_count,
  340. failure_count / count() as failure_rate,
  341. sum(exclusive_time) as total_exclusive_time,
  342. count(DISTINCT transaction_id) as count_unique_transaction_id,
  343. count(DISTINCT transaction) as count_unique_transaction,
  344. min(timestamp) as first_seen,
  345. max(timestamp) as last_seen
  346. FROM spans_experimental_starfish
  347. WHERE 1 == 1
  348. ${module ? `AND module = '${module}'` : ''}
  349. ${description ? `AND description = '${description}'` : ''}
  350. ${groupId ? `AND group_id = '${groupId}'` : ''}
  351. ${transactionName ? `AND transaction = '${transactionName}'` : ''}
  352. ${start_timestamp ? `AND greaterOrEquals(start_timestamp, '${start_timestamp}')` : ''}
  353. ${end_timestamp ? `AND lessOrEquals(start_timestamp, '${end_timestamp}')` : ''}
  354. ORDER BY count DESC
  355. LIMIT 5
  356. `;
  357. };
  358. function getQueries(spanGroupOperation: string) {
  359. switch (spanGroupOperation) {
  360. case 'db':
  361. case 'cache':
  362. return {
  363. getSeriesQuery: getSidebarSeriesQuery,
  364. getAggregatesQuery: getSidebarAggregatesQuery,
  365. };
  366. case 'http.client':
  367. return {
  368. getSeriesQuery: getEndpointDetailSeriesQuery,
  369. getAggregatesQuery: getSidebarAggregatesQuery,
  370. };
  371. default:
  372. return {
  373. getSeriesQuery: getSidebarSeriesQuery,
  374. getAggregatesQuery: getSidebarAggregatesQuery,
  375. };
  376. }
  377. }