Skip to content

Commit 1766e91

Browse files
authored
perf: Experimentally use orjson in Slack integration (#69409)
This adds an option `"integrations.slack.enable-orjson"` to enable `orjson` for json parsing (ETA: and dumping) in the slack integration. Ref: #68903
1 parent 9b50b8c commit 1766e91

File tree

16 files changed

+134
-39
lines changed

16 files changed

+134
-39
lines changed

src/sentry/integrations/slack/actions/notification.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ def send_notification(event: GroupEvent, futures: Sequence[RuleFuture]) -> None:
9797
if payload_blocks := blocks.get("blocks"):
9898
payload = {
9999
"text": blocks.get("text"),
100-
"blocks": json.dumps(payload_blocks),
100+
"blocks": json.dumps_experimental(
101+
"integrations.slack.enable-orjson", payload_blocks
102+
),
101103
"channel": channel,
102104
"unfurl_links": False,
103105
"unfurl_media": False,
@@ -180,7 +182,9 @@ def send_notification(event: GroupEvent, futures: Sequence[RuleFuture]) -> None:
180182
"channel_name": self.get_option("channel"),
181183
}
182184
# temporarily log the payload so we can debug message failures
183-
log_params["payload"] = json.dumps(payload)
185+
log_params["payload"] = json.dumps_experimental(
186+
"integrations.slack.enable-orjson", payload
187+
)
184188

185189
self.logger.info(
186190
"rule.fail.slack_post",
@@ -250,7 +254,9 @@ def send_confirmation_notification(
250254
blocks = SlackRuleSaveEditMessageBuilder(rule=rule, new=new, changed=changed).build()
251255
payload = {
252256
"text": blocks.get("text"),
253-
"blocks": json.dumps(blocks.get("blocks")),
257+
"blocks": json.dumps_experimental(
258+
"integrations.slack.enable-orjson", blocks.get("blocks")
259+
),
254260
"channel": channel,
255261
"unfurl_links": False,
256262
"unfurl_media": False,

src/sentry/integrations/slack/message_builder/issues.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -687,6 +687,6 @@ def build(self, notification_uuid: str | None = None) -> SlackBlock:
687687
return self._build_blocks(
688688
*blocks,
689689
fallback_text=self.build_fallback_text(obj, project.slug),
690-
block_id=json.dumps(block_id),
690+
block_id=json.dumps_experimental("integrations.slack.enable-orjson", block_id),
691691
skip_fallback=self.skip_fallback,
692692
)

src/sentry/integrations/slack/message_builder/notifications/base.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ def build(self) -> SlackBlock:
3333
self.recipient, ExternalProviders.SLACK
3434
)
3535
actions = self.notification.get_message_actions(self.recipient, ExternalProviders.SLACK)
36-
callback_id = json.dumps(callback_id_raw) if callback_id_raw else None
36+
callback_id = (
37+
json.dumps_experimental("integrations.slack.enable-orjson", callback_id_raw)
38+
if callback_id_raw
39+
else None
40+
)
3741

3842
first_block_text = ""
3943
if title_link:

src/sentry/integrations/slack/message_builder/notifications/daily_summary.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,12 @@ def build(self) -> SlackBlock:
189189

190190
text = subject
191191
callback_id_raw = self.notification.get_callback_data()
192-
callback_id = json.dumps(callback_id_raw) if callback_id_raw else None
192+
callback_id = (
193+
json.dumps_experimental("integrations.slack.enable-orjson", callback_id_raw)
194+
if callback_id_raw
195+
else None
196+
)
197+
193198
footer = self.notification.build_notification_footer(
194199
self.recipient, ExternalProviders.SLACK
195200
)

src/sentry/integrations/slack/message_builder/prompt.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ def __init__(self, url: str) -> None:
1313

1414
def build(self) -> SlackBody:
1515
return {
16-
"blocks": json.dumps(
16+
"blocks": json.dumps_experimental(
17+
"integrations.slack.enable-orjson",
1718
[
1819
self.get_markdown_block(LINK_IDENTITY_MESSAGE),
1920
self.get_action_block([("Link", self.url, "link"), ("Cancel", None, "ignore")]),
20-
]
21+
],
2122
)
2223
}

src/sentry/integrations/slack/notifications.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,17 @@ def _notify_recipient(
9494
"unfurl_links": False,
9595
"unfurl_media": False,
9696
"text": text if text else "",
97-
"blocks": json.dumps(blocks),
97+
"blocks": json.dumps_experimental("integrations.slack.enable-orjson", blocks),
9898
}
9999
callback_id = local_attachments.get("callback_id")
100100
if callback_id:
101101
# callback_id is now at the same level as blocks, rather than within attachments
102102
if isinstance(callback_id, str):
103103
payload["callback_id"] = callback_id
104104
else:
105-
payload["callback_id"] = json.dumps(local_attachments.get("callback_id"))
105+
payload["callback_id"] = json.dumps_experimental(
106+
"integrations.slack.enable-orjson", local_attachments.get("callback_id")
107+
)
106108

107109
post_message_task = post_message
108110
if SiloMode.get_current_mode() == SiloMode.CONTROL:

src/sentry/integrations/slack/requests/action.py

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,27 +37,39 @@ def callback_data(self) -> JSONData:
3737
- is_message: did the original message have a 'message' type
3838
"""
3939
if self.data.get("callback_id"):
40-
return json.loads(self.data["callback_id"])
40+
return json.loads_experimental(
41+
"integrations.slack.enable-orjson", self.data["callback_id"]
42+
)
4143

4244
# XXX(CEO): can't really feature flag this but the block kit data is very different
4345

4446
# slack sends us a response when a modal is opened and when an option is selected
4547
# we don't do anything with it until the user hits "Submit" but we need to handle it anyway
4648
if self.data["type"] == "block_actions":
4749
if self.data.get("view"):
48-
return json.loads(self.data["view"]["private_metadata"])
50+
return json.loads_experimental(
51+
"integrations.slack.enable-orjson", self.data["view"]["private_metadata"]
52+
)
53+
4954
elif self.data.get("container", {}).get(
5055
"is_app_unfurl"
5156
): # for actions taken on interactive unfurls
52-
return json.loads(self.data["app_unfurl"]["blocks"][0]["block_id"])
53-
return json.loads(self.data["message"]["blocks"][0]["block_id"])
57+
return json.loads_experimental(
58+
"integrations.slack.enable-orjson",
59+
self.data["app_unfurl"]["blocks"][0]["block_id"],
60+
)
61+
return json.loads_experimental(
62+
"integrations.slack.enable-orjson", self.data["message"]["blocks"][0]["block_id"]
63+
)
5464

5565
if self.data["type"] == "view_submission":
56-
return json.loads(self.data["view"]["private_metadata"])
66+
return json.loads_experimental(
67+
"integrations.slack.enable-orjson", self.data["view"]["private_metadata"]
68+
)
5769

5870
for data in self.data["message"]["blocks"]:
5971
if data["type"] == "section" and len(data["block_id"]) > 5:
60-
return json.loads(data["block_id"])
72+
return json.loads_experimental("integrations.slack.enable-orjson", data["block_id"])
6173
# a bit hacky, you can only provide a block ID per block (not per entire message),
6274
# and if not provided slack generates a 5 char long one. our provided block_id is at least '{issue: <issue_id>}'
6375
# so we know it's longer than 5 chars
@@ -74,7 +86,9 @@ def _validate_data(self) -> None:
7486
raise SlackRequestError(status=status.HTTP_400_BAD_REQUEST)
7587

7688
try:
77-
self._data = json.loads(self.data["payload"])
89+
self._data = json.loads_experimental(
90+
"integrations.slack.enable-orjson", self.data["payload"]
91+
)
7892
except (KeyError, IndexError, TypeError, ValueError):
7993
raise SlackRequestError(status=status.HTTP_400_BAD_REQUEST)
8094

src/sentry/integrations/slack/requests/options_load.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,17 @@ class SlackOptionsLoadRequest(SlackRequest):
1919
@property
2020
def group_id(self) -> int:
2121
if self.data.get("container", {}).get("is_app_unfurl"):
22-
return int(json.loads(self.data["app_unfurl"]["blocks"][0]["block_id"])["issue"])
23-
return int(json.loads(self.data["message"]["blocks"][0]["block_id"])["issue"])
22+
return int(
23+
json.loads_experimental(
24+
"integrations.slack.enable-orjson",
25+
self.data["app_unfurl"]["blocks"][0]["block_id"],
26+
)["issue"]
27+
)
28+
return int(
29+
json.loads_experimental(
30+
"integrations.slack.enable-orjson", self.data["message"]["blocks"][0]["block_id"]
31+
)["issue"]
32+
)
2433

2534
@property
2635
def substring(self) -> str:
@@ -33,7 +42,9 @@ def _validate_data(self) -> None:
3342
raise SlackRequestError(status=status.HTTP_400_BAD_REQUEST)
3443

3544
try:
36-
self._data = json.loads(self.data["payload"])
45+
self._data = json.loads_experimental(
46+
"integrations.slack.enable-orjson", self.data["payload"]
47+
)
3748
except (KeyError, IndexError, TypeError, ValueError):
3849
raise SlackRequestError(status=status.HTTP_400_BAD_REQUEST)
3950

src/sentry/integrations/slack/service.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,9 @@ def _handle_parent_notification(
201201
)
202202
payload.update(slack_payload)
203203
# TODO (Yash): Users should not have to remember to do this, interface should handle serializing the field
204-
payload["blocks"] = json.dumps(payload.get("blocks"))
204+
payload["blocks"] = json.dumps_experimental(
205+
"integrations.slack.enable-orjson", payload.get("blocks")
206+
)
205207
try:
206208
client.post("/chat.postMessage", data=payload, timeout=5)
207209
except Exception as err:

src/sentry/integrations/slack/utils/notifications.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def send_incident_alert_notification(
6363
payload = {
6464
"channel": channel,
6565
"text": text,
66-
"attachments": json.dumps([blocks]),
66+
"attachments": json.dumps_experimental("integrations.slack.enable-orjson", [blocks]),
6767
# Prevent duplicate unfurl
6868
# https://api.slack.com/reference/messaging/link-unfurling#no_unfurling_please
6969
"unfurl_links": False,

src/sentry/integrations/slack/utils/rule_status.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ def set_value(
3838
def get_value(self) -> JSONData:
3939
key = self._get_redis_key()
4040
value = self.client.get(key)
41-
return json.loads(cast(Union[str, bytes], value))
41+
return json.loads_experimental(
42+
"integrations.slack.enable-orjson", cast(Union[str, bytes], value)
43+
)
4244

4345
def _generate_uuid(self) -> str:
4446
return uuid4().hex
4547

4648
def _set_initial_value(self) -> None:
47-
value = json.dumps({"status": "pending"})
49+
value = json.dumps_experimental("integrations.slack.enable-orjson", {"status": "pending"})
4850
self.client.set(self._get_redis_key(), f"{value}", ex=60 * 60, nx=True)
4951

5052
def _get_redis_key(self) -> str:
@@ -64,4 +66,4 @@ def _format_value(
6466
elif status == "failed":
6567
value["error"] = SLACK_FAILED_MESSAGE
6668

67-
return json.dumps(value)
69+
return json.dumps_experimental("integrations.slack.enable-orjson", value)

src/sentry/integrations/slack/webhooks/action.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,9 @@ def api_error(
197197
if view:
198198
private_metadata = view.get("private_metadata")
199199
if private_metadata:
200-
data = json.loads(private_metadata)
200+
data = json.loads_experimental(
201+
"integrations.slack.enable-orjson", private_metadata
202+
)
201203
channel_id = data.get("channel_id")
202204
response_url = data.get("orig_response_url")
203205

@@ -398,7 +400,7 @@ def open_resolve_dialog(self, slack_request: SlackActionRequest, group: Group) -
398400
if use_block_kit and slack_request.data.get("channel"):
399401
callback_id["channel_id"] = slack_request.data["channel"]["id"]
400402
callback_id["rule"] = slack_request.callback_data.get("rule")
401-
callback_id = json.dumps(callback_id)
403+
callback_id = json.dumps_experimental("integrations.slack.enable-orjson", callback_id)
402404

403405
dialog = {
404406
"callback_id": callback_id,
@@ -408,7 +410,7 @@ def open_resolve_dialog(self, slack_request: SlackActionRequest, group: Group) -
408410
}
409411

410412
payload = {
411-
"dialog": json.dumps(dialog),
413+
"dialog": json.dumps_experimental("integrations.slack.enable-orjson", dialog),
412414
"trigger_id": slack_request.data["trigger_id"],
413415
}
414416
slack_client = SlackClient(integration_id=slack_request.integration.id)
@@ -418,11 +420,17 @@ def open_resolve_dialog(self, slack_request: SlackActionRequest, group: Group) -
418420
modal_payload = self.build_resolve_modal_payload(callback_id)
419421
try:
420422
payload = {
421-
"view": json.dumps(modal_payload),
423+
"view": json.dumps_experimental(
424+
"integrations.slack.enable-orjson", modal_payload
425+
),
422426
"trigger_id": slack_request.data["trigger_id"],
423427
}
424428
headers = {"content-type": "application/json; charset=utf-8"}
425-
slack_client.post("/views.open", data=json.dumps(payload), headers=headers)
429+
slack_client.post(
430+
"/views.open",
431+
data=json.dumps_experimental("integrations.slack.enable-orjson", payload),
432+
headers=headers,
433+
)
426434
except ApiError as e:
427435
logger.exception(
428436
"slack.action.response-error",
@@ -460,17 +468,21 @@ def open_archive_dialog(self, slack_request: SlackActionRequest, group: Group) -
460468

461469
if slack_request.data.get("channel"):
462470
callback_id["channel_id"] = slack_request.data["channel"]["id"]
463-
callback_id = json.dumps(callback_id)
471+
callback_id = json.dumps_experimental("integrations.slack.enable-orjson", callback_id)
464472

465473
slack_client = SlackClient(integration_id=slack_request.integration.id)
466474
modal_payload = self.build_archive_modal_payload(callback_id)
467475
try:
468476
payload = {
469-
"view": json.dumps(modal_payload),
477+
"view": json.dumps_experimental("integrations.slack.enable-orjson", modal_payload),
470478
"trigger_id": slack_request.data["trigger_id"],
471479
}
472480
headers = {"content-type": "application/json; charset=utf-8"}
473-
slack_client.post("/views.open", data=json.dumps(payload), headers=headers)
481+
slack_client.post(
482+
"/views.open",
483+
data=json.dumps_experimental("integrations.slack.enable-orjson", payload),
484+
headers=headers,
485+
)
474486
except ApiError as e:
475487
logger.exception(
476488
"slack.action.response-error",
@@ -571,7 +583,10 @@ def _handle_group_actions(
571583
# use the original response_url to update the link attachment
572584
slack_client = SlackClient(integration_id=slack_request.integration.id)
573585
try:
574-
private_metadata = json.loads(slack_request.data["view"]["private_metadata"])
586+
private_metadata = json.loads_experimental(
587+
"integrations.slack.enable-orjson",
588+
slack_request.data["view"]["private_metadata"],
589+
)
575590
slack_client.post(private_metadata["orig_response_url"], data=body, json=True)
576591
except ApiError as e:
577592
logger.error("slack.action.response-error", extra={"error": str(e)})

src/sentry/integrations/slack/webhooks/event.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,9 @@ def on_link_shared(self, request: Request, slack_request: SlackDMRequest) -> boo
156156
return True
157157

158158
# Don't unfurl the same thing multiple times
159-
seen_marker = hash(json.dumps((link_type, args)))
159+
seen_marker = hash(
160+
json.dumps_experimental("integrations.slack.enable-orjson", (link_type, args))
161+
)
160162
if seen_marker in links_seen:
161163
continue
162164

@@ -192,7 +194,7 @@ def on_link_shared(self, request: Request, slack_request: SlackDMRequest) -> boo
192194
payload = {
193195
"channel": data["channel"],
194196
"ts": data["message_ts"],
195-
"unfurls": json.dumps(results),
197+
"unfurls": json.dumps_experimental("integrations.slack.enable-orjson", results),
196198
}
197199

198200
client = SlackClient(integration_id=slack_request.integration.id)

src/sentry/integrations/slack/webhooks/options_load.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,9 @@ def post(self, request: Request) -> Response:
9999
extra={
100100
"group_id": group.id if group else None,
101101
"organization_id": group.project.organization.id if group else None,
102-
"request_data": json.dumps(slack_request.data),
102+
"request_data": json.dumps_experimental(
103+
"integrations.slack.enable-orjson", slack_request.data
104+
),
103105
},
104106
)
105107
return self.respond(status=status.HTTP_400_BAD_REQUEST)

src/sentry/options/defaults.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,7 @@
637637
)
638638

639639
register("snuba.snql.enable-orjson", default=0.0, flags=FLAG_AUTOMATOR_MODIFIABLE)
640+
register("integrations.slack.enable-orjson", default=0.0, flags=FLAG_AUTOMATOR_MODIFIABLE)
640641

641642
# Kafka Publisher
642643
register("kafka-publisher.raw-event-sample-rate", default=0.0, flags=FLAG_AUTOMATOR_MODIFIABLE)

0 commit comments

Comments
 (0)