test_backend.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. from unittest import mock
  2. from sentry.eventstore.base import Filter
  3. from sentry.eventstore.models import Event
  4. from sentry.eventstore.snuba.backend import SnubaEventStorage
  5. from sentry.issues.query import apply_performance_conditions
  6. from sentry.testutils import SnubaTestCase, TestCase
  7. from sentry.testutils.helpers.datetime import before_now, iso_format
  8. from sentry.testutils.silo import region_silo_test
  9. from sentry.types.issues import GroupType
  10. from sentry.utils.samples import load_data
  11. @region_silo_test
  12. class SnubaEventStorageTest(TestCase, SnubaTestCase):
  13. def setUp(self):
  14. super().setUp()
  15. self.min_ago = iso_format(before_now(minutes=1))
  16. self.two_min_ago = iso_format(before_now(minutes=2))
  17. self.project1 = self.create_project()
  18. self.project2 = self.create_project()
  19. self.event1 = self.store_event(
  20. data={
  21. "event_id": "a" * 32,
  22. "type": "default",
  23. "platform": "python",
  24. "fingerprint": ["group1"],
  25. "timestamp": self.two_min_ago,
  26. "tags": {"foo": "1"},
  27. },
  28. project_id=self.project1.id,
  29. )
  30. self.event2 = self.store_event(
  31. data={
  32. "event_id": "b" * 32,
  33. "type": "default",
  34. "platform": "python",
  35. "fingerprint": ["group1"],
  36. "timestamp": self.min_ago,
  37. "tags": {"foo": "1"},
  38. },
  39. project_id=self.project2.id,
  40. )
  41. self.event3 = self.store_event(
  42. data={
  43. "event_id": "c" * 32,
  44. "type": "default",
  45. "platform": "python",
  46. "fingerprint": ["group2"],
  47. "timestamp": self.min_ago,
  48. "tags": {"foo": "1"},
  49. },
  50. project_id=self.project2.id,
  51. )
  52. event_data = load_data("transaction")
  53. event_data["timestamp"] = iso_format(before_now(minutes=1))
  54. event_data["start_timestamp"] = iso_format(before_now(minutes=1, seconds=1))
  55. event_data["event_id"] = "d" * 32
  56. self.transaction_event = self.store_event(data=event_data, project_id=self.project1.id)
  57. event_data_2 = load_data(
  58. platform="transaction",
  59. fingerprint=[f"{GroupType.PERFORMANCE_RENDER_BLOCKING_ASSET_SPAN.value}-group3"],
  60. )
  61. event_data_2["timestamp"] = iso_format(before_now(seconds=30))
  62. event_data_2["start_timestamp"] = iso_format(before_now(seconds=31))
  63. event_data_2["event_id"] = "e" * 32
  64. self.transaction_event_2 = self.store_event(data=event_data_2, project_id=self.project2.id)
  65. event_data_3 = load_data(
  66. "transaction", fingerprint=[f"{GroupType.PERFORMANCE_SLOW_SPAN.value}-group3"]
  67. )
  68. event_data_3["timestamp"] = iso_format(before_now(seconds=30))
  69. event_data_3["start_timestamp"] = iso_format(before_now(seconds=31))
  70. event_data_3["event_id"] = "f" * 32
  71. self.transaction_event_3 = self.store_event(data=event_data_3, project_id=self.project2.id)
  72. """
  73. event_data_4 = load_data("transaction")
  74. event_data_4["timestamp"] = iso_format(before_now(seconds=30))
  75. event_data_4["start_timestamp"] = iso_format(before_now(seconds=31))
  76. event_data_4["event_id"] = "g" * 32
  77. self.transaction_event_4 = self.store_event(data=event_data_4, project_id=self.project2.id)
  78. """
  79. self.eventstore = SnubaEventStorage()
  80. def test_get_events(self):
  81. events = self.eventstore.get_events(
  82. filter=Filter(
  83. project_ids=[self.project1.id, self.project2.id],
  84. conditions=[
  85. ["type", "!=", "transaction"]
  86. ], # TODO: Remove once errors storage rolled out
  87. )
  88. )
  89. assert len(events) == 3
  90. # Default sort is timestamp desc, event_id desc
  91. assert events[0].event_id == "c" * 32
  92. assert events[1].event_id == "b" * 32
  93. assert events[2].event_id == "a" * 32
  94. # No events found
  95. project = self.create_project()
  96. events = self.eventstore.get_events(filter=Filter(project_ids=[project.id]))
  97. assert events == []
  98. # Test with a list of event IDs and project ID filters
  99. events = self.eventstore.get_events(
  100. filter=Filter(
  101. project_ids=[self.project1.id, self.project2.id],
  102. event_ids=["a" * 32, "b" * 32, "c" * 32, "x" * 32, "y" * 32, "z" * 32],
  103. )
  104. )
  105. assert len(events) == 3
  106. assert events[0].event_id == "c" * 32
  107. assert events[1].event_id == "b" * 32
  108. assert events[2].event_id == "a" * 32
  109. @mock.patch("sentry.nodestore.get_multi")
  110. def test_get_unfetched_events(self, get_multi):
  111. events = self.eventstore.get_unfetched_events(filter=Filter(project_ids=[self.project1.id]))
  112. assert len(events) == 1
  113. assert get_multi.call_count == 0
  114. @mock.patch("sentry.nodestore.get_multi")
  115. def test_get_unfetched_transactions(self, get_multi):
  116. transactions_proj1 = self.eventstore.get_unfetched_transactions(
  117. filter=Filter(project_ids=[self.project1.id])
  118. )
  119. assert len(transactions_proj1) == 1
  120. assert get_multi.call_count == 0
  121. transactions_proj2 = self.eventstore.get_unfetched_transactions(
  122. filter=Filter(project_ids=[self.project2.id])
  123. )
  124. assert len(transactions_proj2) == 2
  125. assert get_multi.call_count == 0
  126. def test_get_event_by_id(self):
  127. # Get valid event
  128. event = self.eventstore.get_event_by_id(self.project1.id, "a" * 32)
  129. assert event.event_id == "a" * 32
  130. assert event.project_id == self.project1.id
  131. assert event.group_id == event.group.id
  132. # Get non existent event
  133. event = self.eventstore.get_event_by_id(self.project2.id, "z" * 32)
  134. assert event is None
  135. # Get transaction
  136. event = self.eventstore.get_event_by_id(self.project2.id, self.transaction_event_2.event_id)
  137. assert event.event_id == "e" * 32
  138. assert event.get_event_type() == "transaction"
  139. assert event.project_id == self.project2.id
  140. def test_get_event_by_id_cached(self):
  141. # Simulate getting an event that exists in eventstore but has not yet been written to snuba.
  142. with mock.patch("sentry.eventstore.snuba.backend.Event") as mock_event:
  143. dummy_event = Event(
  144. project_id=self.project2.id,
  145. event_id="f" * 32,
  146. data={"something": "hi", "timestamp": self.min_ago},
  147. )
  148. mock_event.return_value = dummy_event
  149. event = self.eventstore.get_event_by_id(self.project2.id, "f" * 32)
  150. # Result of query should be None
  151. assert event is None
  152. # Now we store the event properly, so it will exist in Snuba.
  153. self.store_event(
  154. data={"event_id": "f" * 32, "timestamp": self.min_ago},
  155. project_id=self.project2.id,
  156. )
  157. # Make sure that the negative cache isn't causing the event to not show up
  158. event = self.eventstore.get_event_by_id(self.project2.id, "f" * 32)
  159. assert event.event_id == "f" * 32
  160. assert event.project_id == self.project2.id
  161. assert event.group_id == event.group.id
  162. def test_get_event_beyond_retention(self):
  163. event = self.store_event(
  164. data={
  165. "event_id": "d" * 32,
  166. "type": "default",
  167. "platform": "python",
  168. "fingerprint": ["group2"],
  169. "timestamp": iso_format(before_now(days=14)),
  170. "tags": {"foo": "1"},
  171. },
  172. project_id=self.project2.id,
  173. )
  174. with mock.patch("sentry.quotas.get_event_retention") as get_event_retention:
  175. get_event_retention.return_value = 7
  176. event = self.eventstore.get_event_by_id(self.project2.id, "d" * 32)
  177. assert event is None
  178. def test_get_next_prev_event_id(self):
  179. event = self.eventstore.get_event_by_id(self.project2.id, "b" * 32)
  180. _filter = Filter(project_ids=[self.project1.id, self.project2.id])
  181. prev_event = self.eventstore.get_prev_event_id(event, filter=_filter)
  182. next_event = self.eventstore.get_next_event_id(event, filter=_filter)
  183. assert prev_event == (str(self.project1.id), "a" * 32)
  184. # Events with the same timestamp are sorted by event_id
  185. assert next_event == (str(self.project2.id), "c" * 32)
  186. # Returns None if no event
  187. assert self.eventstore.get_prev_event_id(None, filter=_filter) is None
  188. assert self.eventstore.get_next_event_id(None, filter=_filter) is None
  189. def test_next_prev_event_id_same_timestamp(self):
  190. project = self.create_project()
  191. event1 = self.store_event(
  192. data={
  193. "event_id": "a" * 32,
  194. "type": "default",
  195. "platform": "python",
  196. "fingerprint": ["group"],
  197. "timestamp": self.min_ago,
  198. },
  199. project_id=project.id,
  200. )
  201. event2 = self.store_event(
  202. data={
  203. "event_id": "b" * 32,
  204. "type": "default",
  205. "platform": "python",
  206. "fingerprint": ["group"],
  207. "timestamp": self.min_ago,
  208. },
  209. project_id=project.id,
  210. )
  211. # the 2 events should be in the same group
  212. assert event1.group_id == event2.group_id
  213. # the 2 events should have the same timestamp
  214. assert event1.datetime == event2.datetime
  215. _filter = Filter(
  216. project_ids=[project.id],
  217. conditions=[["event.type", "!=", "transaction"]],
  218. group_ids=[event1.group_id],
  219. )
  220. event = self.eventstore.get_event_by_id(project.id, "a" * 32)
  221. prev_event = self.eventstore.get_prev_event_id(event, filter=_filter)
  222. next_event = self.eventstore.get_next_event_id(event, filter=_filter)
  223. assert prev_event is None
  224. assert next_event == (str(project.id), "b" * 32)
  225. event = self.eventstore.get_event_by_id(project.id, "b" * 32)
  226. prev_event = self.eventstore.get_prev_event_id(event, filter=_filter)
  227. next_event = self.eventstore.get_next_event_id(event, filter=_filter)
  228. assert prev_event == (str(project.id), "a" * 32)
  229. assert next_event is None
  230. def test_transaction_get_next_prev_event_id(self):
  231. group = self.transaction_event_2.groups[0]
  232. _filter = Filter(
  233. project_ids=[self.project2.id],
  234. conditions=apply_performance_conditions([], group),
  235. )
  236. event = self.eventstore.get_event_by_id(self.project2.id, "f" * 32)
  237. prev_event = self.eventstore.get_prev_event_id(event, filter=_filter)
  238. next_event = self.eventstore.get_next_event_id(event, filter=_filter)
  239. assert prev_event == (str(self.project2.id), "e" * 32)
  240. assert next_event is None
  241. event = self.eventstore.get_event_by_id(self.project2.id, "e" * 32)
  242. prev_event = self.eventstore.get_prev_event_id(event, filter=_filter)
  243. next_event = self.eventstore.get_next_event_id(event, filter=_filter)
  244. assert prev_event is None
  245. assert next_event == (str(self.project2.id), "f" * 32)