test_organization_events_span_metrics.py 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977
  1. import pytest
  2. from django.urls import reverse
  3. from sentry.search.events import constants
  4. from sentry.search.utils import map_device_class_level
  5. from sentry.testutils.cases import MetricsEnhancedPerformanceTestCase
  6. from sentry.testutils.helpers.datetime import before_now
  7. from sentry.testutils.silo import region_silo_test
  8. pytestmark = pytest.mark.sentry_metrics
  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_fixed_percentile_functions(self):
  142. self.store_span_metric(
  143. 1,
  144. internal_metric=constants.SELF_TIME_LIGHT,
  145. timestamp=self.min_ago,
  146. )
  147. for function in ["p50()", "p75()", "p95()", "p99()", "p100()"]:
  148. response = self.do_request(
  149. {
  150. "field": [function],
  151. "query": "",
  152. "project": self.project.id,
  153. "dataset": "spansMetrics",
  154. }
  155. )
  156. assert response.status_code == 200, response.content
  157. data = response.data["data"]
  158. meta = response.data["meta"]
  159. assert len(data) == 1
  160. assert data[0][function] == 1, function
  161. assert meta["dataset"] == "spansMetrics", function
  162. assert meta["fields"][function] == "duration", function
  163. def test_fixed_percentile_functions_with_duration(self):
  164. self.store_span_metric(
  165. 1,
  166. internal_metric=constants.SPAN_METRICS_MAP["span.duration"],
  167. timestamp=self.min_ago,
  168. )
  169. for function in [
  170. "p50(span.duration)",
  171. "p75(span.duration)",
  172. "p95(span.duration)",
  173. "p99(span.duration)",
  174. "p100(span.duration)",
  175. ]:
  176. response = self.do_request(
  177. {
  178. "field": [function],
  179. "query": "",
  180. "project": self.project.id,
  181. "dataset": "spansMetrics",
  182. }
  183. )
  184. assert response.status_code == 200, response.content
  185. data = response.data["data"]
  186. meta = response.data["meta"]
  187. assert len(data) == 1, function
  188. assert data[0][function] == 1, function
  189. assert meta["dataset"] == "spansMetrics", function
  190. assert meta["fields"][function] == "duration", function
  191. def test_avg(self):
  192. self.store_span_metric(
  193. 1,
  194. internal_metric=constants.SELF_TIME_LIGHT,
  195. timestamp=self.min_ago,
  196. )
  197. response = self.do_request(
  198. {
  199. "field": ["avg()"],
  200. "query": "",
  201. "project": self.project.id,
  202. "dataset": "spansMetrics",
  203. }
  204. )
  205. assert response.status_code == 200, response.content
  206. data = response.data["data"]
  207. meta = response.data["meta"]
  208. assert len(data) == 1
  209. assert data[0]["avg()"] == 1
  210. assert meta["dataset"] == "spansMetrics"
  211. def test_eps(self):
  212. for _ in range(6):
  213. self.store_span_metric(
  214. 1,
  215. internal_metric=constants.SELF_TIME_LIGHT,
  216. timestamp=self.min_ago,
  217. )
  218. response = self.do_request(
  219. {
  220. "field": ["eps()", "sps()"],
  221. "query": "",
  222. "project": self.project.id,
  223. "dataset": "spansMetrics",
  224. "statsPeriod": "10m",
  225. }
  226. )
  227. assert response.status_code == 200, response.content
  228. data = response.data["data"]
  229. meta = response.data["meta"]
  230. assert len(data) == 1
  231. assert data[0]["eps()"] == 0.01
  232. assert data[0]["sps()"] == 0.01
  233. assert meta["fields"]["eps()"] == "rate"
  234. assert meta["fields"]["sps()"] == "rate"
  235. assert meta["units"]["eps()"] == "1/second"
  236. assert meta["units"]["sps()"] == "1/second"
  237. assert meta["dataset"] == "spansMetrics"
  238. def test_epm(self):
  239. for _ in range(6):
  240. self.store_span_metric(
  241. 1,
  242. internal_metric=constants.SELF_TIME_LIGHT,
  243. timestamp=self.min_ago,
  244. )
  245. response = self.do_request(
  246. {
  247. "field": ["epm()", "spm()"],
  248. "query": "",
  249. "project": self.project.id,
  250. "dataset": "spansMetrics",
  251. "statsPeriod": "10m",
  252. }
  253. )
  254. assert response.status_code == 200, response.content
  255. data = response.data["data"]
  256. meta = response.data["meta"]
  257. assert len(data) == 1
  258. assert data[0]["epm()"] == 0.6
  259. assert data[0]["spm()"] == 0.6
  260. assert meta["fields"]["epm()"] == "rate"
  261. assert meta["fields"]["spm()"] == "rate"
  262. assert meta["units"]["epm()"] == "1/minute"
  263. assert meta["units"]["spm()"] == "1/minute"
  264. assert meta["dataset"] == "spansMetrics"
  265. def test_time_spent_percentage(self):
  266. for _ in range(4):
  267. self.store_span_metric(
  268. 1,
  269. internal_metric=constants.SELF_TIME_LIGHT,
  270. tags={"transaction": "foo_transaction"},
  271. timestamp=self.min_ago,
  272. )
  273. self.store_span_metric(
  274. 1,
  275. tags={"transaction": "foo_transaction"},
  276. timestamp=self.min_ago,
  277. )
  278. self.store_span_metric(
  279. 1,
  280. internal_metric=constants.SELF_TIME_LIGHT,
  281. tags={"transaction": "bar_transaction"},
  282. timestamp=self.min_ago,
  283. )
  284. self.store_span_metric(
  285. 1,
  286. tags={"transaction": "bar_transaction"},
  287. timestamp=self.min_ago,
  288. )
  289. response = self.do_request(
  290. {
  291. "field": ["transaction", "time_spent_percentage()"],
  292. "query": "",
  293. "orderby": ["-time_spent_percentage()"],
  294. "project": self.project.id,
  295. "dataset": "spansMetrics",
  296. "statsPeriod": "10m",
  297. }
  298. )
  299. assert response.status_code == 200, response.content
  300. data = response.data["data"]
  301. meta = response.data["meta"]
  302. assert len(data) == 2
  303. assert data[0]["time_spent_percentage()"] == 0.8
  304. assert data[0]["transaction"] == "foo_transaction"
  305. assert data[1]["time_spent_percentage()"] == 0.2
  306. assert data[1]["transaction"] == "bar_transaction"
  307. assert meta["dataset"] == "spansMetrics"
  308. def test_time_spent_percentage_local(self):
  309. response = self.do_request(
  310. {
  311. "field": ["time_spent_percentage(local)"],
  312. "query": "",
  313. "orderby": ["-time_spent_percentage(local)"],
  314. "project": self.project.id,
  315. "dataset": "spansMetrics",
  316. "statsPeriod": "10m",
  317. }
  318. )
  319. assert response.status_code == 200, response.content
  320. data = response.data["data"]
  321. meta = response.data["meta"]
  322. assert len(data) == 1
  323. assert data[0]["time_spent_percentage(local)"] is None
  324. assert meta["dataset"] == "spansMetrics"
  325. def test_http_error_rate_and_count(self):
  326. for _ in range(4):
  327. self.store_span_metric(
  328. 1,
  329. internal_metric=constants.SELF_TIME_LIGHT,
  330. tags={"span.status_code": "500"},
  331. timestamp=self.min_ago,
  332. )
  333. self.store_span_metric(
  334. 1,
  335. internal_metric=constants.SELF_TIME_LIGHT,
  336. tags={"span.status_code": "200"},
  337. timestamp=self.min_ago,
  338. )
  339. response = self.do_request(
  340. {
  341. "field": ["http_error_count()", "http_error_rate()"],
  342. "query": "",
  343. "orderby": ["-http_error_rate()"],
  344. "project": self.project.id,
  345. "dataset": "spansMetrics",
  346. "statsPeriod": "10m",
  347. }
  348. )
  349. assert response.status_code == 200, response.content
  350. data = response.data["data"]
  351. meta = response.data["meta"]
  352. assert len(data) == 1
  353. assert data[0]["http_error_rate()"] == 0.8
  354. assert meta["dataset"] == "spansMetrics"
  355. assert meta["fields"]["http_error_count()"] == "integer"
  356. assert meta["fields"]["http_error_rate()"] == "percentage"
  357. def test_use_self_time_light(self):
  358. self.store_span_metric(
  359. 100,
  360. internal_metric=constants.SELF_TIME_LIGHT,
  361. tags={"transaction": "foo_transaction"},
  362. timestamp=self.min_ago,
  363. )
  364. response = self.do_request(
  365. {
  366. "field": ["p50(span.self_time)"],
  367. # Should be 0 since its filtering on transaction
  368. "query": "transaction:foo_transaction",
  369. "orderby": ["-p50(span.self_time)"],
  370. "project": self.project.id,
  371. "dataset": "spansMetrics",
  372. "statsPeriod": "10m",
  373. }
  374. )
  375. assert response.status_code == 200, response.content
  376. data = response.data["data"]
  377. meta = response.data["meta"]
  378. assert len(data) == 1
  379. assert data[0]["p50(span.self_time)"] == 0
  380. assert meta["dataset"] == "spansMetrics"
  381. assert meta["fields"]["p50(span.self_time)"] == "duration"
  382. response = self.do_request(
  383. {
  384. # Should be 0 since it has a transaction column
  385. "field": ["transaction", "p50(span.self_time)"],
  386. "query": "",
  387. "orderby": ["-p50(span.self_time)"],
  388. "project": self.project.id,
  389. "dataset": "spansMetrics",
  390. "statsPeriod": "10m",
  391. }
  392. )
  393. assert response.status_code == 200, response.content
  394. data = response.data["data"]
  395. meta = response.data["meta"]
  396. assert len(data) == 0
  397. response = self.do_request(
  398. {
  399. "field": ["p50(span.self_time)"],
  400. # Should be 100 since its not filtering on transaction
  401. "query": "",
  402. "orderby": ["-p50(span.self_time)"],
  403. "project": self.project.id,
  404. "dataset": "spansMetrics",
  405. "statsPeriod": "10m",
  406. }
  407. )
  408. assert response.status_code == 200, response.content
  409. data = response.data["data"]
  410. meta = response.data["meta"]
  411. assert len(data) == 1
  412. assert data[0]["p50(span.self_time)"] == 100
  413. assert meta["dataset"] == "spansMetrics"
  414. assert meta["fields"]["p50(span.self_time)"] == "duration"
  415. def test_span_module(self):
  416. self.store_span_metric(
  417. 1,
  418. internal_metric=constants.SELF_TIME_LIGHT,
  419. timestamp=self.six_min_ago,
  420. tags={"span.category": "http", "span.description": "f"},
  421. )
  422. self.store_span_metric(
  423. 3,
  424. internal_metric=constants.SELF_TIME_LIGHT,
  425. timestamp=self.six_min_ago,
  426. tags={"span.category": "db", "span.description": "e"},
  427. )
  428. self.store_span_metric(
  429. 5,
  430. internal_metric=constants.SELF_TIME_LIGHT,
  431. timestamp=self.six_min_ago,
  432. tags={"span.category": "foobar", "span.description": "d"},
  433. )
  434. self.store_span_metric(
  435. 7,
  436. internal_metric=constants.SELF_TIME_LIGHT,
  437. timestamp=self.six_min_ago,
  438. tags={"span.category": "cache", "span.description": "c"},
  439. )
  440. self.store_span_metric(
  441. 9,
  442. internal_metric=constants.SELF_TIME_LIGHT,
  443. timestamp=self.six_min_ago,
  444. tags={"span.category": "db", "span.op": "db.redis", "span.description": "b"},
  445. )
  446. self.store_span_metric(
  447. 11,
  448. internal_metric=constants.SELF_TIME_LIGHT,
  449. timestamp=self.six_min_ago,
  450. tags={"span.category": "db", "span.op": "db.sql.room", "span.description": "a"},
  451. )
  452. response = self.do_request(
  453. {
  454. "field": ["span.module", "span.description", "p50(span.self_time)"],
  455. "query": "",
  456. "orderby": ["-p50(span.self_time)"],
  457. "project": self.project.id,
  458. "dataset": "spansMetrics",
  459. "statsPeriod": "10m",
  460. }
  461. )
  462. assert response.status_code == 200, response.content
  463. data = response.data["data"]
  464. meta = response.data["meta"]
  465. assert len(data) == 6
  466. assert data[0]["p50(span.self_time)"] == 11
  467. assert data[0]["span.module"] == "other"
  468. assert data[0]["span.description"] == "a"
  469. assert data[1]["p50(span.self_time)"] == 9
  470. assert data[1]["span.module"] == "cache"
  471. assert data[1]["span.description"] == "b"
  472. assert data[2]["p50(span.self_time)"] == 7
  473. assert data[2]["span.module"] == "cache"
  474. assert data[2]["span.description"] == "c"
  475. assert data[3]["p50(span.self_time)"] == 5
  476. assert data[3]["span.module"] == "other"
  477. assert data[3]["span.description"] == "d"
  478. assert data[4]["p50(span.self_time)"] == 3
  479. assert data[4]["span.module"] == "db"
  480. assert data[4]["span.description"] == "e"
  481. assert data[5]["p50(span.self_time)"] == 1
  482. assert data[5]["span.module"] == "http"
  483. assert data[5]["span.description"] == "f"
  484. assert meta["dataset"] == "spansMetrics"
  485. assert meta["fields"]["p50(span.self_time)"] == "duration"
  486. def test_tag_search(self):
  487. self.store_span_metric(
  488. 321,
  489. internal_metric=constants.SELF_TIME_LIGHT,
  490. timestamp=self.min_ago,
  491. tags={"span.description": "foo"},
  492. )
  493. self.store_span_metric(
  494. 99,
  495. internal_metric=constants.SELF_TIME_LIGHT,
  496. timestamp=self.min_ago,
  497. tags={"span.description": "bar"},
  498. )
  499. response = self.do_request(
  500. {
  501. "field": ["sum(span.self_time)"],
  502. "query": "span.description:bar",
  503. "project": self.project.id,
  504. "dataset": "spansMetrics",
  505. }
  506. )
  507. assert response.status_code == 200, response.content
  508. data = response.data["data"]
  509. meta = response.data["meta"]
  510. assert len(data) == 1
  511. assert data[0]["sum(span.self_time)"] == 99
  512. assert meta["dataset"] == "spansMetrics"
  513. def test_free_text_search(self):
  514. self.store_span_metric(
  515. 321,
  516. internal_metric=constants.SELF_TIME_LIGHT,
  517. timestamp=self.min_ago,
  518. tags={"span.description": "foo"},
  519. )
  520. self.store_span_metric(
  521. 99,
  522. internal_metric=constants.SELF_TIME_LIGHT,
  523. timestamp=self.min_ago,
  524. tags={"span.description": "bar"},
  525. )
  526. response = self.do_request(
  527. {
  528. "field": ["sum(span.self_time)"],
  529. "query": "foo",
  530. "project": self.project.id,
  531. "dataset": "spansMetrics",
  532. }
  533. )
  534. assert response.status_code == 200, response.content
  535. data = response.data["data"]
  536. meta = response.data["meta"]
  537. assert len(data) == 1
  538. assert data[0]["sum(span.self_time)"] == 321
  539. assert meta["dataset"] == "spansMetrics"
  540. def test_avg_compare(self):
  541. self.store_span_metric(
  542. 100,
  543. internal_metric=constants.SELF_TIME_LIGHT,
  544. timestamp=self.min_ago,
  545. tags={"release": "foo"},
  546. )
  547. self.store_span_metric(
  548. 10,
  549. internal_metric=constants.SELF_TIME_LIGHT,
  550. timestamp=self.min_ago,
  551. tags={"release": "bar"},
  552. )
  553. for function_name in [
  554. "avg_compare(span.self_time, release, foo, bar)",
  555. 'avg_compare(span.self_time, release, "foo", "bar")',
  556. ]:
  557. response = self.do_request(
  558. {
  559. "field": [function_name],
  560. "query": "",
  561. "project": self.project.id,
  562. "dataset": "spansMetrics",
  563. }
  564. )
  565. assert response.status_code == 200, response.content
  566. data = response.data["data"]
  567. meta = response.data["meta"]
  568. assert len(data) == 1
  569. assert data[0][function_name] == -0.9
  570. assert meta["dataset"] == "spansMetrics"
  571. assert meta["fields"][function_name] == "percent_change"
  572. def test_avg_compare_invalid_column(self):
  573. response = self.do_request(
  574. {
  575. "field": ["avg_compare(span.self_time, transaction, foo, bar)"],
  576. "query": "",
  577. "project": self.project.id,
  578. "dataset": "spansMetrics",
  579. }
  580. )
  581. assert response.status_code == 400, response.content
  582. def test_span_domain_array(self):
  583. self.store_span_metric(
  584. 321,
  585. internal_metric=constants.SELF_TIME_LIGHT,
  586. timestamp=self.min_ago,
  587. tags={"span.domain": ",sentry_table1,"},
  588. )
  589. self.store_span_metric(
  590. 21,
  591. internal_metric=constants.SELF_TIME_LIGHT,
  592. timestamp=self.min_ago,
  593. tags={"span.domain": ",sentry_table1,sentry_table2,"},
  594. )
  595. response = self.do_request(
  596. {
  597. "field": ["span.domain", "p75(span.self_time)"],
  598. "query": "",
  599. "project": self.project.id,
  600. "orderby": ["-p75(span.self_time)"],
  601. "dataset": "spansMetrics",
  602. }
  603. )
  604. assert response.status_code == 200, response.content
  605. data = response.data["data"]
  606. meta = response.data["meta"]
  607. assert len(data) == 2
  608. assert data[0]["span.domain"] == ["sentry_table1"]
  609. assert data[1]["span.domain"] == ["sentry_table1", "sentry_table2"]
  610. assert meta["dataset"] == "spansMetrics"
  611. assert meta["fields"]["span.domain"] == "array"
  612. def test_span_domain_array_filter(self):
  613. self.store_span_metric(
  614. 321,
  615. internal_metric=constants.SELF_TIME_LIGHT,
  616. timestamp=self.min_ago,
  617. tags={"span.domain": ",sentry_table1,"},
  618. )
  619. self.store_span_metric(
  620. 21,
  621. internal_metric=constants.SELF_TIME_LIGHT,
  622. timestamp=self.min_ago,
  623. tags={"span.domain": ",sentry_table1,sentry_table2,"},
  624. )
  625. response = self.do_request(
  626. {
  627. "field": ["span.domain", "p75(span.self_time)"],
  628. "query": "span.domain:sentry_table2",
  629. "project": self.project.id,
  630. "dataset": "spansMetrics",
  631. }
  632. )
  633. assert response.status_code == 200, response.content
  634. data = response.data["data"]
  635. meta = response.data["meta"]
  636. assert len(data) == 1
  637. assert data[0]["span.domain"] == ["sentry_table1", "sentry_table2"]
  638. assert meta["dataset"] == "spansMetrics"
  639. assert meta["fields"]["span.domain"] == "array"
  640. def test_span_domain_array_filter_wildcard(self):
  641. self.store_span_metric(
  642. 321,
  643. internal_metric=constants.SELF_TIME_LIGHT,
  644. timestamp=self.min_ago,
  645. tags={"span.domain": ",sentry_table1,"},
  646. )
  647. self.store_span_metric(
  648. 21,
  649. internal_metric=constants.SELF_TIME_LIGHT,
  650. timestamp=self.min_ago,
  651. tags={"span.domain": ",sentry_table1,sentry_table2,"},
  652. )
  653. for query in ["sentry*2", "*table2", "sentry_table2*"]:
  654. response = self.do_request(
  655. {
  656. "field": ["span.domain", "p75(span.self_time)"],
  657. "query": f"span.domain:{query}",
  658. "project": self.project.id,
  659. "dataset": "spansMetrics",
  660. }
  661. )
  662. assert response.status_code == 200, response.content
  663. data = response.data["data"]
  664. meta = response.data["meta"]
  665. assert len(data) == 1, query
  666. assert data[0]["span.domain"] == ["sentry_table1", "sentry_table2"], query
  667. assert meta["dataset"] == "spansMetrics", query
  668. assert meta["fields"]["span.domain"] == "array"
  669. def test_span_domain_array_has_filter(self):
  670. self.store_span_metric(
  671. 321,
  672. internal_metric=constants.SELF_TIME_LIGHT,
  673. timestamp=self.min_ago,
  674. tags={"span.domain": ""},
  675. )
  676. self.store_span_metric(
  677. 21,
  678. internal_metric=constants.SELF_TIME_LIGHT,
  679. timestamp=self.min_ago,
  680. tags={"span.domain": ",sentry_table1,sentry_table2,"},
  681. )
  682. response = self.do_request(
  683. {
  684. "field": ["span.domain", "p75(span.self_time)"],
  685. "query": "has:span.domain",
  686. "project": self.project.id,
  687. "dataset": "spansMetrics",
  688. }
  689. )
  690. assert response.status_code == 200, response.content
  691. data = response.data["data"]
  692. meta = response.data["meta"]
  693. assert len(data) == 1
  694. assert data[0]["span.domain"] == ["sentry_table1", "sentry_table2"]
  695. assert meta["dataset"] == "spansMetrics"
  696. response = self.do_request(
  697. {
  698. "field": ["span.domain", "p75(span.self_time)"],
  699. "query": "!has:span.domain",
  700. "project": self.project.id,
  701. "dataset": "spansMetrics",
  702. }
  703. )
  704. assert response.status_code == 200, response.content
  705. data = response.data["data"]
  706. meta = response.data["meta"]
  707. assert len(data) == 1
  708. assert meta["dataset"] == "spansMetrics"
  709. assert meta["fields"]["span.domain"] == "array"
  710. def test_unique_values_span_domain(self):
  711. self.store_span_metric(
  712. 321,
  713. internal_metric=constants.SELF_TIME_LIGHT,
  714. timestamp=self.min_ago,
  715. tags={"span.domain": ",sentry_table1,"},
  716. )
  717. self.store_span_metric(
  718. 21,
  719. internal_metric=constants.SELF_TIME_LIGHT,
  720. timestamp=self.min_ago,
  721. tags={"span.domain": ",sentry_table2,sentry_table3,"},
  722. )
  723. response = self.do_request(
  724. {
  725. "field": ["unique.span_domains", "count()"],
  726. "query": "",
  727. "orderby": "unique.span_domains",
  728. "project": self.project.id,
  729. "dataset": "spansMetrics",
  730. }
  731. )
  732. assert response.status_code == 200, response.content
  733. data = response.data["data"]
  734. meta = response.data["meta"]
  735. assert len(data) == 3
  736. assert data[0]["unique.span_domains"] == "sentry_table1"
  737. assert data[1]["unique.span_domains"] == "sentry_table2"
  738. assert data[2]["unique.span_domains"] == "sentry_table3"
  739. assert meta["fields"]["unique.span_domains"] == "string"
  740. def test_unique_values_span_domain_with_filter(self):
  741. self.store_span_metric(
  742. 321,
  743. internal_metric=constants.SELF_TIME_LIGHT,
  744. timestamp=self.min_ago,
  745. tags={"span.domain": ",sentry_tible1,"},
  746. )
  747. self.store_span_metric(
  748. 21,
  749. internal_metric=constants.SELF_TIME_LIGHT,
  750. timestamp=self.min_ago,
  751. tags={"span.domain": ",sentry_table2,sentry_table3,"},
  752. )
  753. response = self.do_request(
  754. {
  755. "field": ["unique.span_domains", "count()"],
  756. "query": "span.domain:sentry_tab*",
  757. "orderby": "unique.span_domains",
  758. "project": self.project.id,
  759. "dataset": "spansMetrics",
  760. }
  761. )
  762. assert response.status_code == 200, response.content
  763. data = response.data["data"]
  764. meta = response.data["meta"]
  765. assert len(data) == 2
  766. assert data[0]["unique.span_domains"] == "sentry_table2"
  767. assert data[1]["unique.span_domains"] == "sentry_table3"
  768. assert meta["fields"]["unique.span_domains"] == "string"
  769. def test_avg_if(self):
  770. self.store_span_metric(
  771. 100,
  772. internal_metric=constants.SELF_TIME_LIGHT,
  773. timestamp=self.min_ago,
  774. tags={"release": "foo"},
  775. )
  776. self.store_span_metric(
  777. 200,
  778. internal_metric=constants.SELF_TIME_LIGHT,
  779. timestamp=self.min_ago,
  780. tags={"release": "foo"},
  781. )
  782. self.store_span_metric(
  783. 10,
  784. internal_metric=constants.SELF_TIME_LIGHT,
  785. timestamp=self.min_ago,
  786. tags={"release": "bar"},
  787. )
  788. response = self.do_request(
  789. {
  790. "field": ["avg_if(span.self_time, release, foo)"],
  791. "query": "",
  792. "project": self.project.id,
  793. "dataset": "spansMetrics",
  794. }
  795. )
  796. assert response.status_code == 200, response.content
  797. data = response.data["data"]
  798. meta = response.data["meta"]
  799. assert len(data) == 1
  800. assert data[0]["avg_if(span.self_time, release, foo)"] == 150
  801. assert meta["dataset"] == "spansMetrics"
  802. assert meta["fields"]["avg_if(span.self_time, release, foo)"] == "duration"
  803. def test_device_class(self):
  804. self.store_span_metric(
  805. 123,
  806. internal_metric=constants.SELF_TIME_LIGHT,
  807. timestamp=self.min_ago,
  808. tags={"device.class": "1"},
  809. )
  810. self.store_span_metric(
  811. 678,
  812. internal_metric=constants.SELF_TIME_LIGHT,
  813. timestamp=self.min_ago,
  814. tags={"device.class": "2"},
  815. )
  816. self.store_span_metric(
  817. 999,
  818. internal_metric=constants.SELF_TIME_LIGHT,
  819. timestamp=self.min_ago,
  820. tags={"device.class": ""},
  821. )
  822. response = self.do_request(
  823. {
  824. "field": ["device.class", "p95()"],
  825. "query": "",
  826. "orderby": "p95()",
  827. "project": self.project.id,
  828. "dataset": "spansMetrics",
  829. }
  830. )
  831. assert response.status_code == 200, response.content
  832. data = response.data["data"]
  833. meta = response.data["meta"]
  834. assert len(data) == 3
  835. # Need to actually check the dict since the level for 1 isn't guaranteed to stay `low` or `medium`
  836. assert data[0]["device.class"] == map_device_class_level("1")
  837. assert data[1]["device.class"] == map_device_class_level("2")
  838. assert data[2]["device.class"] == "Unknown"
  839. assert meta["fields"]["device.class"] == "string"
  840. def test_device_class_filter(self):
  841. self.store_span_metric(
  842. 123,
  843. internal_metric=constants.SELF_TIME_LIGHT,
  844. timestamp=self.min_ago,
  845. tags={"device.class": "1"},
  846. )
  847. # Need to actually check the dict since the level for 1 isn't guaranteed to stay `low`
  848. level = map_device_class_level("1")
  849. response = self.do_request(
  850. {
  851. "field": ["device.class", "count()"],
  852. "query": f"device.class:{level}",
  853. "orderby": "count()",
  854. "project": self.project.id,
  855. "dataset": "spansMetrics",
  856. }
  857. )
  858. assert response.status_code == 200, response.content
  859. data = response.data["data"]
  860. meta = response.data["meta"]
  861. assert len(data) == 1
  862. assert data[0]["device.class"] == level
  863. assert meta["fields"]["device.class"] == "string"
  864. @region_silo_test
  865. class OrganizationEventsMetricsEnhancedPerformanceEndpointTestWithMetricLayer(
  866. OrganizationEventsMetricsEnhancedPerformanceEndpointTest
  867. ):
  868. def setUp(self):
  869. super().setUp()
  870. self.features["organizations:use-metrics-layer"] = True
  871. @pytest.mark.xfail(reason="Not implemented")
  872. def test_time_spent_percentage(self):
  873. super().test_time_spent_percentage()
  874. @pytest.mark.xfail(reason="Not implemented")
  875. def test_time_spent_percentage_local(self):
  876. super().test_time_spent_percentage_local()
  877. @pytest.mark.xfail(reason="Cannot group by function 'if'")
  878. def test_span_module(self):
  879. super().test_span_module()
  880. @pytest.mark.xfail(reason="Cannot search by tags")
  881. def test_tag_search(self):
  882. super().test_tag_search()
  883. @pytest.mark.xfail(reason="Cannot search by tags")
  884. def test_free_text_search(self):
  885. super().test_free_text_search()
  886. @pytest.mark.xfail(reason="Not implemented")
  887. def test_avg_compare(self):
  888. super().test_avg_compare()
  889. @pytest.mark.xfail(reason="Not implemented")
  890. def test_span_domain_array(self):
  891. super().test_span_domain_array()
  892. @pytest.mark.xfail(reason="Not implemented")
  893. def test_span_domain_array_filter(self):
  894. super().test_span_domain_array_filter()
  895. @pytest.mark.xfail(reason="Not implemented")
  896. def test_span_domain_array_filter_wildcard(self):
  897. super().test_span_domain_array_filter_wildcard()
  898. @pytest.mark.xfail(reason="Not implemented")
  899. def test_span_domain_array_has_filter(self):
  900. super().test_span_domain_array_has_filter()
  901. @pytest.mark.xfail(reason="Not implemented")
  902. def test_unique_values_span_domain(self):
  903. super().test_unique_values_span_domain()
  904. @pytest.mark.xfail(reason="Not implemented")
  905. def test_unique_values_span_domain_with_filter(self):
  906. super().test_unique_values_span_domain_with_filter()
  907. @pytest.mark.xfail(reason="Not implemented")
  908. def test_avg_if(self):
  909. super().test_avg_if()
  910. @pytest.mark.xfail(reason="Not implemented")
  911. def test_device_class_filter(self):
  912. super().test_device_class_filter()
  913. @pytest.mark.xfail(reason="Not implemented")
  914. def test_device_class(self):
  915. super().test_device_class()