test_organization_events_span_metrics.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. import pytest
  2. from django.urls import reverse
  3. from sentry.search.events import constants
  4. from sentry.testutils import MetricsEnhancedPerformanceTestCase
  5. from sentry.testutils.helpers.datetime import before_now
  6. from sentry.testutils.silo import region_silo_test
  7. pytestmark = pytest.mark.sentry_metrics
  8. @region_silo_test
  9. class OrganizationEventsMetricsEnhancedPerformanceEndpointTest(MetricsEnhancedPerformanceTestCase):
  10. viewname = "sentry-api-0-organization-events"
  11. # Poor intentionally omitted for test_measurement_rating_that_does_not_exist
  12. METRIC_STRINGS = [
  13. "foo_transaction",
  14. "bar_transaction",
  15. ]
  16. def setUp(self):
  17. super().setUp()
  18. self.min_ago = before_now(minutes=1)
  19. self.six_min_ago = before_now(minutes=6)
  20. self.three_days_ago = before_now(days=3)
  21. self.features = {
  22. "organizations:starfish-view": True,
  23. }
  24. def do_request(self, query, features=None):
  25. if features is None:
  26. features = {"organizations:discover-basic": True}
  27. features.update(self.features)
  28. self.login_as(user=self.user)
  29. url = reverse(
  30. self.viewname,
  31. kwargs={"organization_slug": self.organization.slug},
  32. )
  33. with self.feature(features):
  34. return self.client.get(url, query, format="json")
  35. def test_p50_with_no_data(self):
  36. response = self.do_request(
  37. {
  38. "field": ["p50()"],
  39. "query": "",
  40. "project": self.project.id,
  41. "dataset": "spansMetrics",
  42. }
  43. )
  44. assert response.status_code == 200, response.content
  45. data = response.data["data"]
  46. meta = response.data["meta"]
  47. assert len(data) == 1
  48. assert data[0]["p50()"] == 0
  49. assert meta["dataset"] == "spansMetrics"
  50. def test_count(self):
  51. self.store_span_metric(
  52. 1,
  53. internal_metric=constants.SELF_TIME_LIGHT,
  54. timestamp=self.three_days_ago,
  55. )
  56. response = self.do_request(
  57. {
  58. "field": ["count()"],
  59. "query": "",
  60. "project": self.project.id,
  61. "dataset": "spansMetrics",
  62. "statsPeriod": "7d",
  63. }
  64. )
  65. assert response.status_code == 200, response.content
  66. data = response.data["data"]
  67. meta = response.data["meta"]
  68. assert len(data) == 1
  69. assert data[0]["count()"] == 1
  70. assert meta["dataset"] == "spansMetrics"
  71. def test_count_unique(self):
  72. self.store_span_metric(
  73. 1,
  74. "user",
  75. timestamp=self.min_ago,
  76. )
  77. self.store_span_metric(
  78. 2,
  79. "user",
  80. timestamp=self.min_ago,
  81. )
  82. response = self.do_request(
  83. {
  84. "field": ["count_unique(user)"],
  85. "query": "",
  86. "project": self.project.id,
  87. "dataset": "spansMetrics",
  88. }
  89. )
  90. assert response.status_code == 200, response.content
  91. data = response.data["data"]
  92. meta = response.data["meta"]
  93. assert len(data) == 1
  94. assert data[0]["count_unique(user)"] == 2
  95. assert meta["dataset"] == "spansMetrics"
  96. def test_sum(self):
  97. self.store_span_metric(
  98. 321,
  99. internal_metric=constants.SELF_TIME_LIGHT,
  100. timestamp=self.min_ago,
  101. )
  102. self.store_span_metric(
  103. 99,
  104. internal_metric=constants.SELF_TIME_LIGHT,
  105. timestamp=self.min_ago,
  106. )
  107. response = self.do_request(
  108. {
  109. "field": ["sum(span.self_time)"],
  110. "query": "",
  111. "project": self.project.id,
  112. "dataset": "spansMetrics",
  113. }
  114. )
  115. assert response.status_code == 200, response.content
  116. data = response.data["data"]
  117. meta = response.data["meta"]
  118. assert len(data) == 1
  119. assert data[0]["sum(span.self_time)"] == 420
  120. assert meta["dataset"] == "spansMetrics"
  121. def test_percentile(self):
  122. self.store_span_metric(
  123. 1,
  124. internal_metric=constants.SELF_TIME_LIGHT,
  125. timestamp=self.min_ago,
  126. )
  127. response = self.do_request(
  128. {
  129. "field": ["percentile(span.self_time, 0.95)"],
  130. "query": "",
  131. "project": self.project.id,
  132. "dataset": "spansMetrics",
  133. }
  134. )
  135. assert response.status_code == 200, response.content
  136. data = response.data["data"]
  137. meta = response.data["meta"]
  138. assert len(data) == 1
  139. assert data[0]["percentile(span.self_time, 0.95)"] == 1
  140. assert meta["dataset"] == "spansMetrics"
  141. def test_p50(self):
  142. self.store_span_metric(
  143. 1,
  144. internal_metric=constants.SELF_TIME_LIGHT,
  145. timestamp=self.min_ago,
  146. )
  147. response = self.do_request(
  148. {
  149. "field": ["p50()"],
  150. "query": "",
  151. "project": self.project.id,
  152. "dataset": "spansMetrics",
  153. }
  154. )
  155. assert response.status_code == 200, response.content
  156. data = response.data["data"]
  157. meta = response.data["meta"]
  158. assert len(data) == 1
  159. assert data[0]["p50()"] == 1
  160. assert meta["dataset"] == "spansMetrics"
  161. def test_p50_with_duration(self):
  162. self.store_span_metric(
  163. 1,
  164. internal_metric=constants.SPAN_METRICS_MAP["span.duration"],
  165. timestamp=self.min_ago,
  166. )
  167. response = self.do_request(
  168. {
  169. "field": ["p50(span.duration)"],
  170. "query": "",
  171. "project": self.project.id,
  172. "dataset": "spansMetrics",
  173. }
  174. )
  175. assert response.status_code == 200, response.content
  176. data = response.data["data"]
  177. meta = response.data["meta"]
  178. assert len(data) == 1
  179. assert data[0]["p50(span.duration)"] == 1
  180. assert meta["dataset"] == "spansMetrics"
  181. def test_avg(self):
  182. self.store_span_metric(
  183. 1,
  184. internal_metric=constants.SELF_TIME_LIGHT,
  185. timestamp=self.min_ago,
  186. )
  187. response = self.do_request(
  188. {
  189. "field": ["avg()"],
  190. "query": "",
  191. "project": self.project.id,
  192. "dataset": "spansMetrics",
  193. }
  194. )
  195. assert response.status_code == 200, response.content
  196. data = response.data["data"]
  197. meta = response.data["meta"]
  198. assert len(data) == 1
  199. assert data[0]["avg()"] == 1
  200. assert meta["dataset"] == "spansMetrics"
  201. def test_eps(self):
  202. for _ in range(6):
  203. self.store_span_metric(
  204. 1,
  205. internal_metric=constants.SELF_TIME_LIGHT,
  206. timestamp=self.min_ago,
  207. )
  208. response = self.do_request(
  209. {
  210. "field": ["eps()", "sps()"],
  211. "query": "",
  212. "project": self.project.id,
  213. "dataset": "spansMetrics",
  214. "statsPeriod": "10m",
  215. }
  216. )
  217. assert response.status_code == 200, response.content
  218. data = response.data["data"]
  219. meta = response.data["meta"]
  220. assert len(data) == 1
  221. assert data[0]["eps()"] == 0.01
  222. assert data[0]["sps()"] == 0.01
  223. assert meta["fields"]["eps()"] == "rate"
  224. assert meta["fields"]["sps()"] == "rate"
  225. assert meta["units"]["eps()"] == "1/second"
  226. assert meta["units"]["sps()"] == "1/second"
  227. assert meta["dataset"] == "spansMetrics"
  228. def test_epm(self):
  229. for _ in range(6):
  230. self.store_span_metric(
  231. 1,
  232. internal_metric=constants.SELF_TIME_LIGHT,
  233. timestamp=self.min_ago,
  234. )
  235. response = self.do_request(
  236. {
  237. "field": ["epm()", "spm()"],
  238. "query": "",
  239. "project": self.project.id,
  240. "dataset": "spansMetrics",
  241. "statsPeriod": "10m",
  242. }
  243. )
  244. assert response.status_code == 200, response.content
  245. data = response.data["data"]
  246. meta = response.data["meta"]
  247. assert len(data) == 1
  248. assert data[0]["epm()"] == 0.6
  249. assert data[0]["spm()"] == 0.6
  250. assert meta["fields"]["epm()"] == "rate"
  251. assert meta["fields"]["spm()"] == "rate"
  252. assert meta["units"]["epm()"] == "1/minute"
  253. assert meta["units"]["spm()"] == "1/minute"
  254. assert meta["dataset"] == "spansMetrics"
  255. def test_time_spent_percentage(self):
  256. for _ in range(4):
  257. self.store_span_metric(
  258. 1,
  259. internal_metric=constants.SELF_TIME_LIGHT,
  260. tags={"transaction": "foo_transaction"},
  261. timestamp=self.min_ago,
  262. )
  263. self.store_span_metric(
  264. 1,
  265. tags={"transaction": "foo_transaction"},
  266. timestamp=self.min_ago,
  267. )
  268. self.store_span_metric(
  269. 1,
  270. internal_metric=constants.SELF_TIME_LIGHT,
  271. tags={"transaction": "bar_transaction"},
  272. timestamp=self.min_ago,
  273. )
  274. self.store_span_metric(
  275. 1,
  276. tags={"transaction": "bar_transaction"},
  277. timestamp=self.min_ago,
  278. )
  279. response = self.do_request(
  280. {
  281. "field": ["transaction", "time_spent_percentage()"],
  282. "query": "",
  283. "orderby": ["-time_spent_percentage()"],
  284. "project": self.project.id,
  285. "dataset": "spansMetrics",
  286. "statsPeriod": "10m",
  287. }
  288. )
  289. assert response.status_code == 200, response.content
  290. data = response.data["data"]
  291. meta = response.data["meta"]
  292. assert len(data) == 2
  293. assert data[0]["time_spent_percentage()"] == 0.8
  294. assert data[0]["transaction"] == "foo_transaction"
  295. assert data[1]["time_spent_percentage()"] == 0.2
  296. assert data[1]["transaction"] == "bar_transaction"
  297. assert meta["dataset"] == "spansMetrics"
  298. def test_time_spent_percentage_local(self):
  299. response = self.do_request(
  300. {
  301. "field": ["time_spent_percentage(local)"],
  302. "query": "",
  303. "orderby": ["-time_spent_percentage(local)"],
  304. "project": self.project.id,
  305. "dataset": "spansMetrics",
  306. "statsPeriod": "10m",
  307. }
  308. )
  309. assert response.status_code == 200, response.content
  310. data = response.data["data"]
  311. meta = response.data["meta"]
  312. assert len(data) == 1
  313. assert data[0]["time_spent_percentage(local)"] is None
  314. assert meta["dataset"] == "spansMetrics"
  315. def test_http_error_rate_and_count(self):
  316. for _ in range(4):
  317. self.store_span_metric(
  318. 1,
  319. internal_metric=constants.SELF_TIME_LIGHT,
  320. tags={"span.status_code": "500"},
  321. timestamp=self.min_ago,
  322. )
  323. self.store_span_metric(
  324. 1,
  325. internal_metric=constants.SELF_TIME_LIGHT,
  326. tags={"span.status_code": "200"},
  327. timestamp=self.min_ago,
  328. )
  329. response = self.do_request(
  330. {
  331. "field": ["http_error_count()", "http_error_rate()"],
  332. "query": "",
  333. "orderby": ["-http_error_rate()"],
  334. "project": self.project.id,
  335. "dataset": "spansMetrics",
  336. "statsPeriod": "10m",
  337. }
  338. )
  339. assert response.status_code == 200, response.content
  340. data = response.data["data"]
  341. meta = response.data["meta"]
  342. assert len(data) == 1
  343. assert data[0]["http_error_rate()"] == 0.8
  344. assert meta["dataset"] == "spansMetrics"
  345. assert meta["fields"]["http_error_count()"] == "integer"
  346. assert meta["fields"]["http_error_rate()"] == "percentage"
  347. def test_use_self_time_light(self):
  348. self.store_span_metric(
  349. 100,
  350. internal_metric=constants.SELF_TIME_LIGHT,
  351. tags={"transaction": "foo_transaction"},
  352. timestamp=self.min_ago,
  353. )
  354. response = self.do_request(
  355. {
  356. "field": ["p50(span.self_time)"],
  357. # Should be 0 since its filtering on transaction
  358. "query": "transaction:foo_transaction",
  359. "orderby": ["-p50(span.self_time)"],
  360. "project": self.project.id,
  361. "dataset": "spansMetrics",
  362. "statsPeriod": "10m",
  363. }
  364. )
  365. assert response.status_code == 200, response.content
  366. data = response.data["data"]
  367. meta = response.data["meta"]
  368. assert len(data) == 1
  369. assert data[0]["p50(span.self_time)"] == 0
  370. assert meta["dataset"] == "spansMetrics"
  371. assert meta["fields"]["p50(span.self_time)"] == "duration"
  372. response = self.do_request(
  373. {
  374. # Should be 0 since it has a transaction column
  375. "field": ["transaction", "p50(span.self_time)"],
  376. "query": "",
  377. "orderby": ["-p50(span.self_time)"],
  378. "project": self.project.id,
  379. "dataset": "spansMetrics",
  380. "statsPeriod": "10m",
  381. }
  382. )
  383. assert response.status_code == 200, response.content
  384. data = response.data["data"]
  385. meta = response.data["meta"]
  386. assert len(data) == 0
  387. response = self.do_request(
  388. {
  389. "field": ["p50(span.self_time)"],
  390. # Should be 100 since its not filtering on transaction
  391. "query": "",
  392. "orderby": ["-p50(span.self_time)"],
  393. "project": self.project.id,
  394. "dataset": "spansMetrics",
  395. "statsPeriod": "10m",
  396. }
  397. )
  398. assert response.status_code == 200, response.content
  399. data = response.data["data"]
  400. meta = response.data["meta"]
  401. assert len(data) == 1
  402. assert data[0]["p50(span.self_time)"] == 100
  403. assert meta["dataset"] == "spansMetrics"
  404. assert meta["fields"]["p50(span.self_time)"] == "duration"
  405. def test_span_module(self):
  406. self.store_span_metric(
  407. 1,
  408. internal_metric=constants.SELF_TIME_LIGHT,
  409. timestamp=self.six_min_ago,
  410. tags={"span.category": "http"},
  411. )
  412. self.store_span_metric(
  413. 3,
  414. internal_metric=constants.SELF_TIME_LIGHT,
  415. timestamp=self.six_min_ago,
  416. tags={"span.category": "db"},
  417. )
  418. self.store_span_metric(
  419. 5,
  420. internal_metric=constants.SELF_TIME_LIGHT,
  421. timestamp=self.six_min_ago,
  422. tags={"span.category": "foobar"},
  423. )
  424. self.store_span_metric(
  425. 7,
  426. internal_metric=constants.SELF_TIME_LIGHT,
  427. timestamp=self.six_min_ago,
  428. tags={"span.category": "cache"},
  429. )
  430. response = self.do_request(
  431. {
  432. "field": ["span.module", "p50(span.self_time)"],
  433. "query": "",
  434. "orderby": ["-p50(span.self_time)"],
  435. "project": self.project.id,
  436. "dataset": "spansMetrics",
  437. "statsPeriod": "10m",
  438. }
  439. )
  440. assert response.status_code == 200, response.content
  441. data = response.data["data"]
  442. meta = response.data["meta"]
  443. assert len(data) == 4
  444. assert data[0]["p50(span.self_time)"] == 7
  445. assert data[0]["span.module"] == "cache"
  446. assert data[1]["p50(span.self_time)"] == 5
  447. assert data[1]["span.module"] == "other"
  448. assert data[2]["p50(span.self_time)"] == 3
  449. assert data[2]["span.module"] == "db"
  450. assert data[3]["p50(span.self_time)"] == 1
  451. assert data[3]["span.module"] == "http"
  452. assert meta["dataset"] == "spansMetrics"
  453. assert meta["fields"]["p50(span.self_time)"] == "duration"
  454. @region_silo_test
  455. class OrganizationEventsMetricsEnhancedPerformanceEndpointTestWithMetricLayer(
  456. OrganizationEventsMetricsEnhancedPerformanceEndpointTest
  457. ):
  458. def setUp(self):
  459. super().setUp()
  460. self.features["organizations:use-metrics-layer"] = True
  461. @pytest.mark.xfail(reason="Not implemented")
  462. def test_time_spent_percentage(self):
  463. super().test_time_spent_percentage()
  464. @pytest.mark.xfail(reason="Not implemented")
  465. def test_time_spent_percentage_local(self):
  466. super().test_time_spent_percentage_local()
  467. @pytest.mark.xfail(reason="Not implemented")
  468. def test_http_error_rate_and_count(self):
  469. super().test_http_error_rate_and_count()
  470. @pytest.mark.xfail(reason="Cannot group by transform")
  471. def test_span_module(self):
  472. super().test_span_module()