Skip to content

Commit cbcecec

Browse files
committed
Migrate eventstore __get_events (used by get_events & get_unfetched_events) to query EAP
1 parent 5357aef commit cbcecec

File tree

15 files changed

+462
-9
lines changed

15 files changed

+462
-9
lines changed

src/sentry/api/serializers/models/userreport.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from sentry.models.group import Group
99
from sentry.models.project import Project
1010
from sentry.models.userreport import UserReport
11+
from sentry.search.eap.occurrences.query_utils import build_event_id_in_filter
1112
from sentry.services import eventstore
1213
from sentry.services.eventstore.models import Event
1314
from sentry.snuba.dataset import Dataset
@@ -46,12 +47,14 @@ def get_attrs(self, item_list, user, **kwargs):
4647
project = Project.objects.get(id=item_list[0].project_id)
4748
retention = quotas.backend.get_event_retention(organization=project.organization)
4849

50+
event_ids = [item.event_id for item in item_list]
4951
events = eventstore.backend.get_events(
5052
filter=eventstore.Filter(
51-
event_ids=[item.event_id for item in item_list],
53+
event_ids=event_ids,
5254
project_ids=[project.id],
5355
start=timezone.now() - timedelta(days=retention) if retention else None,
5456
),
57+
eap_conditions=build_event_id_in_filter(event_ids),
5558
referrer="UserReportSerializer.get_attrs",
5659
dataset=Dataset.Events,
5760
tenant_ids={"organization_id": project.organization_id},

src/sentry/deletions/tasks/nodestore.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
from sentry.exceptions import DeleteAborted
1414
from sentry.models.eventattachment import EventAttachment
1515
from sentry.models.userreport import UserReport
16+
from sentry.search.eap.occurrences.query_utils import (
17+
build_group_id_in_filter,
18+
build_keyset_pagination_filter,
19+
)
20+
from sentry.search.eap.rpc_utils import and_trace_item_filters
1621
from sentry.services import eventstore
1722
from sentry.services.eventstore.models import Event
1823
from sentry.silo.base import SiloMode
@@ -155,20 +160,29 @@ def fetch_events_from_eventstore(
155160
) -> list[Event]:
156161
logger.info("Fetching %s events for deletion.", limit)
157162
conditions = []
163+
eap_conditions = build_group_id_in_filter(group_ids)
158164
if last_event_id and last_event_timestamp:
159165
conditions.extend(
160166
[
161167
["timestamp", "<=", last_event_timestamp],
162168
[["timestamp", "<", last_event_timestamp], ["event_id", "<", last_event_id]],
163169
]
164170
)
171+
eap_conditions = and_trace_item_filters(
172+
eap_conditions,
173+
build_keyset_pagination_filter(
174+
timestamp_value=last_event_timestamp,
175+
event_id=last_event_id,
176+
),
177+
)
165178

166179
events = eventstore.backend.get_unfetched_events(
167180
filter=eventstore.Filter(
168181
conditions=conditions,
169182
project_ids=[project_id],
170183
group_ids=group_ids,
171184
),
185+
eap_conditions=eap_conditions,
172186
limit=limit,
173187
referrer=referrer,
174188
orderby=["-timestamp", "-event_id"],

src/sentry/feedback/tasks/update_user_reports.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from sentry.feedback.usecases.ingest.shim_to_feedback import shim_to_feedback
1010
from sentry.models.project import Project
1111
from sentry.models.userreport import UserReport
12+
from sentry.search.eap.occurrences.query_utils import build_event_id_in_filter
1213
from sentry.services import eventstore
1314
from sentry.silo.base import SiloMode
1415
from sentry.snuba.referrer import Referrer
@@ -90,7 +91,9 @@ def update_user_reports(
9091
)
9192
try:
9293
events_chunk = eventstore.backend.get_events(
93-
filter=snuba_filter, referrer=Referrer.TASKS_UPDATE_USER_REPORTS.value
94+
filter=snuba_filter,
95+
eap_conditions=build_event_id_in_filter(event_id_chunk),
96+
referrer=Referrer.TASKS_UPDATE_USER_REPORTS.value,
9497
)
9598
events.extend(events_chunk)
9699
except Exception:

src/sentry/issues/endpoints/organization_eventid.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from drf_spectacular.utils import extend_schema
44
from rest_framework.request import Request
55
from rest_framework.response import Response
6+
from sentry_protos.snuba.v1.trace_item_attribute_pb2 import AttributeKey, AttributeValue
7+
from sentry_protos.snuba.v1.trace_item_filter_pb2 import ComparisonFilter, TraceItemFilter
68

79
from sentry.api.api_owners import ApiOwner
810
from sentry.api.api_publish_status import ApiPublishStatus
@@ -18,6 +20,8 @@
1820
from sentry.models.organization import Organization
1921
from sentry.models.project import Project
2022
from sentry.ratelimits.config import RateLimitConfig
23+
from sentry.search.eap.occurrences.query_utils import build_event_id_in_filter
24+
from sentry.search.eap.rpc_utils import and_trace_item_filters
2125
from sentry.services import eventstore
2226
from sentry.types.ratelimit import RateLimit, RateLimitCategory
2327
from sentry.utils.validators import INVALID_ID_DETAILS, is_event_id
@@ -76,8 +80,21 @@ def get(self, request: Request, organization: Organization, event_id: str) -> Re
7680
project_ids=list(project_slugs_by_id.keys()),
7781
event_ids=[event_id],
7882
)
83+
eap_conditions = and_trace_item_filters(
84+
build_event_id_in_filter([event_id]),
85+
TraceItemFilter(
86+
comparison_filter=ComparisonFilter(
87+
key=AttributeKey(name="type", type=AttributeKey.TYPE_STRING),
88+
op=ComparisonFilter.OP_NOT_EQUALS,
89+
value=AttributeValue(val_str="transaction"),
90+
)
91+
),
92+
)
7993
event = eventstore.backend.get_events(
80-
filter=snuba_filter, limit=1, tenant_ids={"organization_id": organization.id}
94+
filter=snuba_filter,
95+
eap_conditions=eap_conditions,
96+
limit=1,
97+
tenant_ids={"organization_id": organization.id},
8198
)[0]
8299
except IndexError:
83100
raise ResourceDoesNotExist()

src/sentry/issues/endpoints/project_events.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from rest_framework.exceptions import ParseError
55
from rest_framework.request import Request
66
from rest_framework.response import Response
7+
from sentry_protos.snuba.v1.trace_item_filter_pb2 import TraceItemFilter
78

89
from sentry.api.api_owners import ApiOwner
910
from sentry.api.api_publish_status import ApiPublishStatus
@@ -72,8 +73,10 @@ def get(self, request: Request, project: Project) -> Response:
7273

7374
query = request.GET.get("query")
7475
conditions = []
76+
eap_conditions: TraceItemFilter | None = TraceItemFilter()
7577
if query:
7678
conditions.append([["positionCaseInsensitive", ["message", f"'{query}'"]], "!=", 0])
79+
eap_conditions = None # Function condition not representable in EAP
7780

7881
try:
7982
start, end = get_date_range_from_params(
@@ -95,6 +98,7 @@ def get(self, request: Request, project: Project) -> Response:
9598
data_fn = partial(
9699
eventstore.backend.get_events,
97100
filter=event_filter,
101+
eap_conditions=eap_conditions,
98102
referrer="api.project-events",
99103
tenant_ids={"organization_id": project.organization_id},
100104
)

src/sentry/models/group.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
from sentry.models.commit import Commit
4545
from sentry.models.grouphistory import record_group_history, record_group_history_from_activity_type
4646
from sentry.models.organization import Organization
47+
from sentry.search.eap.occurrences.query_utils import build_event_id_in_filter
4748
from sentry.services.eventstore.models import GroupEvent
4849
from sentry.snuba.dataset import Dataset
4950
from sentry.snuba.referrer import Referrer
@@ -435,6 +436,7 @@ def filter_by_event_id(self, project_ids, event_id, tenant_ids=None):
435436
project_ids=project_ids,
436437
conditions=[["group_id", "IS NOT NULL", None]],
437438
),
439+
eap_conditions=build_event_id_in_filter([event_id]), # IS NOT NULL is a no-op in EAP
438440
limit=max(len(project_ids), 100),
439441
referrer="Group.filter_by_event_id",
440442
tenant_ids=tenant_ids,

src/sentry/search/eap/occurrences/query_utils.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,18 @@
22
from datetime import datetime
33
from typing import Any
44

5+
from sentry_protos.snuba.v1.trace_item_attribute_pb2 import (
6+
AttributeKey,
7+
AttributeValue,
8+
IntArray,
9+
StrArray,
10+
)
11+
from sentry_protos.snuba.v1.trace_item_filter_pb2 import ComparisonFilter, TraceItemFilter
12+
513
from sentry.models.environment import Environment
614
from sentry.models.organization import Organization
715
from sentry.models.project import Project
16+
from sentry.search.eap.rpc_utils import and_trace_item_filters, or_trace_item_filters
817
from sentry.search.events.types import SnubaParams
918

1019

@@ -74,3 +83,56 @@ def _to_count_map(rows: Sequence[Mapping[str, Any]]) -> dict[Hashable, int]:
7483
return False
7584

7685
return all(exp_count <= control_map[key] for key, exp_count in experimental_map.items())
86+
87+
88+
def build_group_id_in_filter(group_ids: Sequence[int]) -> TraceItemFilter:
89+
return TraceItemFilter(
90+
comparison_filter=ComparisonFilter(
91+
key=AttributeKey(name="group_id", type=AttributeKey.TYPE_INT),
92+
op=ComparisonFilter.OP_IN,
93+
value=AttributeValue(val_int_array=IntArray(values=list(group_ids))),
94+
)
95+
)
96+
97+
98+
def build_event_id_in_filter(event_ids: Sequence[str]) -> TraceItemFilter:
99+
return TraceItemFilter(
100+
comparison_filter=ComparisonFilter(
101+
key=AttributeKey(name="sentry.item_id", type=AttributeKey.TYPE_STRING),
102+
op=ComparisonFilter.OP_IN,
103+
value=AttributeValue(val_str_array=StrArray(values=list(event_ids))),
104+
)
105+
)
106+
107+
108+
def build_keyset_pagination_filter(
109+
timestamp_value: str,
110+
event_id: str,
111+
) -> TraceItemFilter:
112+
ts_epoch = datetime.fromisoformat(timestamp_value).timestamp()
113+
timestamp_key = AttributeKey(name="sentry.timestamp", type=AttributeKey.TYPE_DOUBLE)
114+
event_id_key = AttributeKey(name="sentry.item_id", type=AttributeKey.TYPE_STRING)
115+
116+
ts_lte = TraceItemFilter(
117+
comparison_filter=ComparisonFilter(
118+
key=timestamp_key,
119+
op=ComparisonFilter.OP_LESS_THAN_OR_EQUALS,
120+
value=AttributeValue(val_double=ts_epoch),
121+
)
122+
)
123+
ts_lt = TraceItemFilter(
124+
comparison_filter=ComparisonFilter(
125+
key=timestamp_key,
126+
op=ComparisonFilter.OP_LESS_THAN,
127+
value=AttributeValue(val_double=ts_epoch),
128+
)
129+
)
130+
eid_lt = TraceItemFilter(
131+
comparison_filter=ComparisonFilter(
132+
key=event_id_key,
133+
op=ComparisonFilter.OP_LESS_THAN,
134+
value=AttributeValue(val_str=event_id),
135+
)
136+
)
137+
138+
return and_trace_item_filters(ts_lte, or_trace_item_filters(ts_lt, eid_lt))

src/sentry/seer/explorer/tools.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
from sentry.replays.post_process import process_raw_response
3333
from sentry.replays.query import query_replay_id_by_prefix, query_replay_instance
3434
from sentry.search.eap.constants import BOOLEAN, DOUBLE, INT, STRING
35+
from sentry.search.eap.occurrences.query_utils import build_event_id_in_filter
3536
from sentry.search.eap.resolver import SearchResolver
3637
from sentry.search.eap.types import SearchResolverConfig
3738
from sentry.search.events.constants import ISSUE_ID_ALIAS
@@ -1255,6 +1256,7 @@ def get_event_details(
12551256
organization_id=organization_id,
12561257
project_ids=project_ids,
12571258
),
1259+
eap_conditions=build_event_id_in_filter([event_id]),
12581260
limit=1,
12591261
tenant_ids={"organization_id": organization_id},
12601262
dataset=dataset,
@@ -1355,6 +1357,7 @@ def get_issue_and_event_details_v2(
13551357
organization_id=organization_id,
13561358
project_ids=project_ids,
13571359
),
1360+
eap_conditions=build_event_id_in_filter([event_id]),
13581361
limit=1,
13591362
tenant_ids={"organization_id": organization_id},
13601363
dataset=dataset,

src/sentry/services/eventstore/base.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from typing import Any, Literal, Self, overload
88

99
import sentry_sdk
10+
from sentry_protos.snuba.v1.trace_item_filter_pb2 import TraceItemFilter
1011
from snuba_sdk import Condition
1112

1213
from sentry import nodestore
@@ -166,6 +167,7 @@ class EventStorage(Service):
166167
def get_events(
167168
self,
168169
filter: Filter,
170+
eap_conditions: TraceItemFilter | None = None,
169171
orderby: Sequence[str] | None = None,
170172
limit: int = 100,
171173
offset: int = 0,
@@ -180,7 +182,8 @@ def get_events(
180182
transaction events. Returns an empty list if no events match the filter.
181183
182184
Arguments:
183-
snuba_filter (Filter): Filter
185+
filter (Filter): Snuba query filter
186+
eap_conditions (TraceItemFilter | None): EAP query conditions
184187
orderby (Sequence[str]): List of fields to order by - default ['-time', '-event_id']
185188
limit (int): Query limit - default 100
186189
offset (int): Query offset - default 0
@@ -208,6 +211,7 @@ def get_events_snql(
208211
def get_unfetched_events(
209212
self,
210213
filter: Filter,
214+
eap_conditions: TraceItemFilter | None = None,
211215
orderby: Sequence[str] | None = None,
212216
limit: int = 100,
213217
offset: int = 0,
@@ -216,16 +220,17 @@ def get_unfetched_events(
216220
tenant_ids: Mapping[str, Any] | None = None,
217221
) -> list[Event]:
218222
"""
219-
Same as get_events but returns events without their node datas loaded.
220-
Only the event ID, projectID, groupID and timestamp field will be present without
221-
an additional fetch to nodestore.
223+
Same as get_events but returns events without their node data loaded.
224+
Only the event ID, project ID, group ID, and timestamp fields will be present
225+
without an additional fetch to nodestore.
222226
223227
Used for fetching large volumes of events that do not need data loaded
224228
from nodestore. Currently this is just used for event data deletions where
225229
we just need the event IDs in order to process the deletions.
226230
227231
Arguments:
228-
snuba_filter (Filter): Filter
232+
filter (Filter): Snuba query filter
233+
eap_conditions (TraceItemFilter | None): EAP query conditions
229234
orderby (Sequence[str]): List of fields to order by - default ['-time', '-event_id']
230235
limit (int): Query limit - default 100
231236
offset (int): Query offset - default 0

0 commit comments

Comments
 (0)