test_snuba.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. import copy
  2. import time
  3. import uuid
  4. from datetime import datetime, timedelta
  5. from unittest import mock
  6. import pytest
  7. from django.utils import timezone
  8. from sentry.testutils import SnubaTestCase, TestCase
  9. from sentry.testutils.helpers.datetime import before_now, iso_format
  10. from sentry.utils import snuba
  11. from sentry.utils.snql import SNQLOption
  12. class SnubaTest(TestCase, SnubaTestCase):
  13. should_use_snql = None
  14. def _insert_event_for_time(self, ts, hash="a" * 32, group_id=None):
  15. self.snuba_insert(
  16. (
  17. 2,
  18. "insert",
  19. {
  20. "event_id": uuid.uuid4().hex,
  21. "primary_hash": hash,
  22. "group_id": group_id if group_id else int(hash[:16], 16),
  23. "project_id": self.project.id,
  24. "message": "message",
  25. "platform": "python",
  26. "datetime": ts.strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
  27. "data": {"received": time.mktime(ts.timetuple())},
  28. },
  29. )
  30. )
  31. def test(self) -> None:
  32. "This is just a simple 'hello, world' example test."
  33. now = datetime.now()
  34. events = [
  35. (
  36. 2,
  37. "insert",
  38. {
  39. "event_id": "a" * 32,
  40. "primary_hash": "1" * 32,
  41. "group_id": 1,
  42. "project_id": self.project.id,
  43. "message": "message",
  44. "platform": "python",
  45. "datetime": now.strftime("%Y-%m-%dT%H:%M:%S.%fZ"),
  46. "data": {"received": time.mktime(now.timetuple())},
  47. },
  48. )
  49. ]
  50. self.snuba_insert(events)
  51. snql_option = 1.0 if self.should_use_snql else 0.0
  52. with self.options({"snuba.snql.referrer-rate": snql_option}):
  53. assert (
  54. snuba.query(
  55. start=now - timedelta(days=1),
  56. end=now + timedelta(days=1),
  57. groupby=["project_id"],
  58. filter_keys={"project_id": [self.project.id]},
  59. referrer="testing.test" if self.should_use_snql else "",
  60. )
  61. == {self.project.id: 1}
  62. )
  63. def test_fail(self) -> None:
  64. now = datetime.now()
  65. with pytest.raises(snuba.SnubaError):
  66. snql_option = 1.0 if self.should_use_snql else 0.0
  67. with self.options({"snuba.snql.referrer-rate": snql_option}):
  68. snuba.query(
  69. start=now - timedelta(days=1),
  70. end=now + timedelta(days=1),
  71. filter_keys={"project_id": [self.project.id]},
  72. groupby=[")("],
  73. referrer="testing.test" if self.should_use_snql else "",
  74. )
  75. def test_organization_retention_respected(self) -> None:
  76. base_time = datetime.utcnow()
  77. self._insert_event_for_time(base_time - timedelta(minutes=1))
  78. self._insert_event_for_time(base_time - timedelta(days=2))
  79. def _get_event_count():
  80. # attempt to query back 90 days
  81. snql_option = 1.0 if self.should_use_snql else 0.0
  82. with self.options({"snuba.snql.referrer-rate": snql_option}):
  83. return snuba.query(
  84. start=base_time - timedelta(days=90),
  85. end=base_time + timedelta(days=1),
  86. groupby=["project_id"],
  87. filter_keys={"project_id": [self.project.id]},
  88. referrer="testing.test" if self.should_use_snql else "",
  89. )
  90. assert _get_event_count() == {self.project.id: 2}
  91. with self.options({"system.event-retention-days": 1}):
  92. assert _get_event_count() == {self.project.id: 1}
  93. def test_organization_retention_larger_than_end_date(self) -> None:
  94. base_time = datetime.utcnow()
  95. with self.options({"system.event-retention-days": 1}):
  96. snql_option = 1.0 if self.should_use_snql else 0.0
  97. with self.options({"snuba.snql.referrer-rate": snql_option}):
  98. assert (
  99. snuba.query(
  100. start=base_time - timedelta(days=90),
  101. end=base_time - timedelta(days=60),
  102. groupby=["project_id"],
  103. filter_keys={"project_id": [self.project.id]},
  104. referrer="testing.test" if self.should_use_snql else "",
  105. )
  106. == {}
  107. )
  108. def test_should_use_snql(self) -> None:
  109. base_time = datetime.utcnow()
  110. with self.options({"snuba.snql.snql_only": 1.0}):
  111. assert (
  112. snuba.query(
  113. start=base_time - timedelta(days=1),
  114. end=base_time,
  115. aggregations=[["count", None, "count"]],
  116. groupby=["project_id"],
  117. filter_keys={"project_id": [self.project.id]},
  118. referrer="sessions.stability-sort",
  119. )
  120. == {}
  121. )
  122. class SnQLSnubaTest(SnubaTest):
  123. should_use_snql = True
  124. class BulkRawQueryTest(TestCase, SnubaTestCase):
  125. def test_simple(self) -> None:
  126. one_min_ago = iso_format(before_now(minutes=1))
  127. event_1 = self.store_event(
  128. data={"fingerprint": ["group-1"], "message": "hello", "timestamp": one_min_ago},
  129. project_id=self.project.id,
  130. )
  131. event_2 = self.store_event(
  132. data={"fingerprint": ["group-2"], "message": "hello", "timestamp": one_min_ago},
  133. project_id=self.project.id,
  134. )
  135. results = snuba.bulk_raw_query(
  136. [
  137. snuba.SnubaQueryParams(
  138. start=timezone.now() - timedelta(days=1),
  139. end=timezone.now(),
  140. selected_columns=["event_id", "group_id", "timestamp"],
  141. filter_keys={"project_id": [self.project.id], "group_id": [event_1.group.id]},
  142. ),
  143. snuba.SnubaQueryParams(
  144. start=timezone.now() - timedelta(days=1),
  145. end=timezone.now(),
  146. selected_columns=["event_id", "group_id", "timestamp"],
  147. filter_keys={"project_id": [self.project.id], "group_id": [event_2.group.id]},
  148. ),
  149. ],
  150. )
  151. assert [{(item["group_id"], item["event_id"]) for item in r["data"]} for r in results] == [
  152. {(event_1.group.id, event_1.event_id)},
  153. {(event_2.group.id, event_2.event_id)},
  154. ]
  155. def test_simple_use_snql(self) -> None:
  156. one_min_ago = iso_format(before_now(minutes=1))
  157. event_1 = self.store_event(
  158. data={"fingerprint": ["group-1"], "message": "hello", "timestamp": one_min_ago},
  159. project_id=self.project.id,
  160. )
  161. event_2 = self.store_event(
  162. data={"fingerprint": ["group-2"], "message": "hello", "timestamp": one_min_ago},
  163. project_id=self.project.id,
  164. )
  165. results = snuba.bulk_raw_query(
  166. [
  167. snuba.SnubaQueryParams(
  168. start=timezone.now() - timedelta(days=1),
  169. end=timezone.now(),
  170. selected_columns=["event_id", "group_id", "timestamp"],
  171. filter_keys={"project_id": [self.project.id], "group_id": [event_1.group.id]},
  172. ),
  173. snuba.SnubaQueryParams(
  174. start=timezone.now() - timedelta(days=1),
  175. end=timezone.now(),
  176. selected_columns=["event_id", "group_id", "timestamp"],
  177. filter_keys={"project_id": [self.project.id], "group_id": [event_2.group.id]},
  178. ),
  179. ],
  180. snql_option=SNQLOption("auto", True),
  181. )
  182. assert [{(item["group_id"], item["event_id"]) for item in r["data"]} for r in results] == [
  183. {(event_1.group.id, event_1.event_id)},
  184. {(event_2.group.id, event_2.event_id)},
  185. ]
  186. @mock.patch("sentry.utils.snuba._bulk_snuba_query", side_effect=snuba._bulk_snuba_query)
  187. def test_cache(self, _bulk_snuba_query):
  188. one_min_ago = iso_format(before_now(minutes=1))
  189. event_1 = self.store_event(
  190. data={"fingerprint": ["group-1"], "message": "hello", "timestamp": one_min_ago},
  191. project_id=self.project.id,
  192. )
  193. event_2 = self.store_event(
  194. data={"fingerprint": ["group-2"], "message": "hello", "timestamp": one_min_ago},
  195. project_id=self.project.id,
  196. )
  197. params = [
  198. snuba.SnubaQueryParams(
  199. start=timezone.now() - timedelta(days=1),
  200. end=timezone.now(),
  201. selected_columns=["event_id", "group_id", "timestamp"],
  202. filter_keys={"project_id": [self.project.id], "group_id": [event_1.group.id]},
  203. ),
  204. snuba.SnubaQueryParams(
  205. start=timezone.now() - timedelta(days=1),
  206. end=timezone.now(),
  207. selected_columns=["event_id", "group_id", "timestamp"],
  208. filter_keys={"project_id": [self.project.id], "group_id": [event_2.group.id]},
  209. ),
  210. ]
  211. results = snuba.bulk_raw_query(
  212. copy.deepcopy(params),
  213. use_cache=True,
  214. )
  215. assert [{(item["group_id"], item["event_id"]) for item in r["data"]} for r in results] == [
  216. {(event_1.group.id, event_1.event_id)},
  217. {(event_2.group.id, event_2.event_id)},
  218. ]
  219. assert _bulk_snuba_query.call_count == 1
  220. _bulk_snuba_query.reset_mock()
  221. # # Make sure this doesn't appear in the cached results
  222. self.store_event(
  223. data={"fingerprint": ["group-2"], "message": "hello there", "timestamp": one_min_ago},
  224. project_id=self.project.id,
  225. )
  226. results = snuba.bulk_raw_query(
  227. copy.deepcopy(params),
  228. use_cache=True,
  229. )
  230. assert [{(item["group_id"], item["event_id"]) for item in r["data"]} for r in results] == [
  231. {(event_1.group.id, event_1.event_id)},
  232. {(event_2.group.id, event_2.event_id)},
  233. ]
  234. assert _bulk_snuba_query.call_count == 0