test_organization_spans_aggregation.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import hashlib
  2. from unittest import mock
  3. from django.urls import reverse
  4. from sentry.api.endpoints.organization_spans_aggregation import NULL_GROUP
  5. from sentry.testutils.cases import APITestCase, SnubaTestCase
  6. MOCK_SNUBA_RESPONSE = {
  7. "data": [
  8. {
  9. "transaction_id": "80fe542aea4945ffbe612646987ee449",
  10. "count": 71,
  11. "spans": [
  12. [
  13. "root_1",
  14. 1,
  15. "parent_1",
  16. "A",
  17. "A",
  18. "bind_organization_context",
  19. "other",
  20. "2023-09-13 17:12:19",
  21. 100,
  22. 0,
  23. 0.0,
  24. ],
  25. [
  26. "B1",
  27. 0,
  28. "root_1",
  29. "B",
  30. "B",
  31. "connect",
  32. "db",
  33. "2023-09-13 17:12:19",
  34. 150,
  35. 50,
  36. 50.0,
  37. ],
  38. [
  39. "C1",
  40. 0,
  41. "root_1",
  42. "C",
  43. "C",
  44. "resolve_conditions",
  45. "discover.endpoint",
  46. "2023-09-13 17:12:19",
  47. 155,
  48. 0,
  49. 10.0,
  50. ],
  51. [
  52. "D1",
  53. 0,
  54. "C1",
  55. "D",
  56. "D",
  57. "resolve_orderby",
  58. "discover.snql",
  59. "2023-09-13 17:12:19",
  60. 157,
  61. 0,
  62. 20.0,
  63. ],
  64. [
  65. "E1",
  66. 0,
  67. "C1",
  68. NULL_GROUP,
  69. "E",
  70. "resolve_columns",
  71. "discover.snql",
  72. "2023-09-13 17:12:19",
  73. 157,
  74. 0,
  75. 20.0,
  76. ],
  77. ],
  78. },
  79. {
  80. "transaction_id": "86b21833d1854d9b811000b91e7fccfa",
  81. "count": 71,
  82. "spans": [
  83. [
  84. "root_2",
  85. 1,
  86. "parent_2",
  87. "A",
  88. "A",
  89. "bind_organization_context",
  90. "other",
  91. "2023-09-13 17:12:39",
  92. 100,
  93. 0,
  94. 0.0,
  95. ],
  96. [
  97. "B2",
  98. 0,
  99. "root_2",
  100. "B",
  101. "B",
  102. "connect",
  103. "db",
  104. "2023-09-13 17:12:39",
  105. 110,
  106. 10,
  107. 30.0,
  108. ],
  109. [
  110. "C2",
  111. 0,
  112. "root_2",
  113. "C",
  114. "C",
  115. "resolve_conditions",
  116. "discover.endpoint",
  117. "2023-09-13 17:12:39",
  118. 115,
  119. 0,
  120. 40.0,
  121. ],
  122. [
  123. "D2",
  124. 0,
  125. "C2",
  126. "D",
  127. "D",
  128. "resolve_orderby",
  129. "discover.snql",
  130. "2023-09-13 17:12:39",
  131. 150,
  132. 0,
  133. 10.0,
  134. ],
  135. [
  136. "D2-duplicate",
  137. 0,
  138. "C2",
  139. "D",
  140. "D",
  141. "resolve_orderby",
  142. "discover.snql",
  143. "2023-09-13 17:12:40",
  144. 155,
  145. 0,
  146. 20.0,
  147. ],
  148. [
  149. "E2",
  150. 0,
  151. "C2",
  152. NULL_GROUP,
  153. "E",
  154. "resolve_columns",
  155. "discover.snql",
  156. "2023-09-13 17:12:39",
  157. 157,
  158. 0,
  159. 20.0,
  160. ],
  161. ],
  162. },
  163. ]
  164. }
  165. class OrganizationSpansAggregationTest(APITestCase, SnubaTestCase):
  166. url_name = "sentry-api-0-organization-spans-aggregation"
  167. FEATURES = [
  168. "organizations:starfish-aggregate-span-waterfall",
  169. "organizations:performance-view",
  170. ]
  171. def setUp(self):
  172. super().setUp()
  173. self.login_as(user=self.user)
  174. self.url = reverse(
  175. self.url_name,
  176. kwargs={"organization_slug": self.project.organization.slug},
  177. )
  178. @mock.patch("sentry.api.endpoints.organization_spans_aggregation.raw_snql_query")
  179. def test_simple(self, mock_query):
  180. mock_query.side_effect = [MOCK_SNUBA_RESPONSE]
  181. with self.feature(self.FEATURES):
  182. response = self.client.get(
  183. self.url,
  184. data={"transaction": "foo"},
  185. format="json",
  186. )
  187. assert response.data
  188. data = response.data
  189. root_fingerprint = hashlib.md5(b"A").hexdigest()[:16]
  190. assert root_fingerprint in data
  191. assert data[root_fingerprint]["description"] == "bind_organization_context"
  192. assert data[root_fingerprint]["count()"] == 2
  193. fingerprint = hashlib.md5(b"A-B").hexdigest()[:16]
  194. assert data[fingerprint]["description"] == "connect"
  195. assert data[fingerprint]["avg(duration)"] == 30.0
  196. fingerprint = hashlib.md5(b"A-C-D").hexdigest()[:16]
  197. assert data[fingerprint]["description"] == "resolve_orderby"
  198. assert data[fingerprint]["avg(exclusive_time)"] == 15.0
  199. assert data[fingerprint]["count()"] == 2
  200. fingerprint = hashlib.md5(b"A-C-D2").hexdigest()[:16]
  201. assert data[fingerprint]["description"] == "resolve_orderby"
  202. assert data[fingerprint]["avg(exclusive_time)"] == 20.0
  203. assert data[fingerprint]["count()"] == 1
  204. @mock.patch("sentry.api.endpoints.organization_spans_aggregation.raw_snql_query")
  205. def test_offset_logic(self, mock_query):
  206. mock_query.side_effect = [MOCK_SNUBA_RESPONSE]
  207. with self.feature(self.FEATURES):
  208. response = self.client.get(
  209. self.url,
  210. data={"transaction": "foo"},
  211. format="json",
  212. )
  213. assert response.data
  214. data = response.data
  215. root_fingerprint = hashlib.md5(b"A").hexdigest()[:16]
  216. assert root_fingerprint in data
  217. assert data[root_fingerprint]["avg(absolute_offset)"] == 0.0
  218. fingerprint = hashlib.md5(b"A-B").hexdigest()[:16]
  219. assert data[fingerprint]["avg(absolute_offset)"] == 30.0
  220. fingerprint = hashlib.md5(b"A-C").hexdigest()[:16]
  221. assert data[fingerprint]["avg(absolute_offset)"] == 35.0
  222. fingerprint = hashlib.md5(b"A-C-D").hexdigest()[:16]
  223. assert data[fingerprint]["avg(absolute_offset)"] == 53.5
  224. fingerprint = hashlib.md5(b"A-C-D2").hexdigest()[:16]
  225. assert data[fingerprint]["avg(absolute_offset)"] == 1075.0
  226. @mock.patch("sentry.api.endpoints.organization_spans_aggregation.raw_snql_query")
  227. def test_null_group_falls_back_to_span_op(self, mock_query):
  228. mock_query.side_effect = [MOCK_SNUBA_RESPONSE]
  229. with self.feature(self.FEATURES):
  230. response = self.client.get(
  231. self.url,
  232. data={"transaction": "foo"},
  233. format="json",
  234. )
  235. assert response.data
  236. data = response.data
  237. root_fingerprint = hashlib.md5(b"A-C-discover.snql").hexdigest()[:16]
  238. assert root_fingerprint in data
  239. assert data[root_fingerprint]["description"] == "<<unparametrized>> resolve_columns"
  240. assert data[root_fingerprint]["count()"] == 2