test_organization_events_span_metrics.py 19 KB

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