Skip to content

Commit 27c2098

Browse files
committed
Merge branch 'master' into ref/em-simplify-signals
* master: (57 commits) ref(getting-started): Change code to always update loader script when products changes (#86583) ref(spans): Detect performance issues directly in segments consumer (#86595) ref(getting-started): Update copies, replacing 'Sentry dashboard' with 'Sentry Issues' (#86672) ref(product-selection): Update code to not strip url (#86582) ref(quick-start): Replace 'record' with 'create_or_update' in 'record_new_project' (#86663) ref(assemble): Remove old `find_missing_chunks` method (#86588) fix(views):Exclude newly added views from last visited update as well (#86653) feat(workflow_engine): Only execute enabled Detectors (#86652) fix(autofix): Github links, remove seer branding, and UI cleanup (#86640) fix(seer-issues-patch) More parsing of functions for Python (#86558) ref(ui): Remove sentry.eot (#86649) feat(alerts): Restrict uptime/crons overview buttons for alerts:write (#86436) chore(deps): bump axios from 1.7.7 to 1.8.2 (#86642) deps(ui): Upgrade prettier (#86634) deps(ui): Upgrade eslint, biome (#86630) fix(shared-views): Fix default view passing to last visited endpoint (#86632) feat(billing): update copy for payg disabled CTA (#86143) ref(ui): Remove usage of withOrganization from organizationAuthList (#86554) fix(crons): Fix disabled state of disable button (#86637) feat(ui): Replace OrganizationAuth DeprecatedAsyncComponent (#86556) ...
2 parents 76a53dd + 9efec47 commit 27c2098

File tree

364 files changed

+5645
-3108
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

364 files changed

+5645
-3108
lines changed

eslint.config.mjs

Lines changed: 26 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
*/
1212
import * as emotion from '@emotion/eslint-plugin';
1313
import eslint from '@eslint/js';
14+
import {globalIgnores} from 'eslint/config';
1415
import prettier from 'eslint-config-prettier';
1516
// @ts-expect-error TS(7016): Could not find a declaration file
1617
import importPlugin from 'eslint-plugin-import';
1718
import jest from 'eslint-plugin-jest';
1819
import jestDom from 'eslint-plugin-jest-dom';
1920
import react from 'eslint-plugin-react';
20-
// @ts-expect-error TS(7016): Could not find a declaration file
2121
import reactHooks from 'eslint-plugin-react-hooks';
2222
// @ts-expect-error TS(7016): Could not find a declaration file
2323
import sentry from 'eslint-plugin-sentry';
@@ -174,34 +174,31 @@ export default typescript.config([
174174
// https://eslint.org/docs/latest/use/configure/configuration-files#specifying-files-and-ignores
175175
files: ['**/*.js', '**/*.mjs', '**/*.ts', '**/*.jsx', '**/*.tsx'],
176176
},
177-
{
178-
name: 'eslint/global/ignores',
179-
// Global ignores
180-
// https://eslint.org/docs/latest/use/configure/configuration-files#globally-ignoring-files-with-ignores
181-
ignores: [
182-
'.devenv/**/*',
183-
'.github/**/*',
184-
'.mypy_cache/**/*',
185-
'.pytest_cache/**/*',
186-
'.venv/**/*',
187-
'**/*.benchmark.ts',
188-
'**/*.d.ts',
189-
'**/dist/**/*',
190-
'**/tests/**/fixtures/**/*',
191-
'**/vendor/**/*',
192-
'build-utils/**/*',
193-
'config/chartcuterie/config.js', // TODO: see if this file exists
194-
'fixtures/artifact_bundle/**/*',
195-
'fixtures/artifact_bundle_debug_ids/**/*',
196-
'fixtures/artifact_bundle_duplicated_debug_ids/**/*',
197-
'fixtures/profiles/embedded.js',
198-
'jest.config.ts',
199-
'api-docs/**/*',
200-
'src/sentry/static/sentry/js/**/*',
201-
'src/sentry/templates/sentry/**/*',
202-
'stylelint.config.js',
203-
],
204-
},
177+
// Global ignores
178+
// https://eslint.org/docs/latest/use/configure/configuration-files#globally-ignoring-files-with-ignores
179+
globalIgnores([
180+
'.devenv/**/*',
181+
'.github/**/*',
182+
'.mypy_cache/**/*',
183+
'.pytest_cache/**/*',
184+
'.venv/**/*',
185+
'**/*.benchmark.ts',
186+
'**/*.d.ts',
187+
'**/dist/**/*',
188+
'**/tests/**/fixtures/**/*',
189+
'**/vendor/**/*',
190+
'build-utils/**/*',
191+
'config/chartcuterie/config.js',
192+
'fixtures/artifact_bundle/**/*',
193+
'fixtures/artifact_bundle_debug_ids/**/*',
194+
'fixtures/artifact_bundle_duplicated_debug_ids/**/*',
195+
'fixtures/profiles/embedded.js',
196+
'jest.config.ts',
197+
'api-docs/**/*',
198+
'src/sentry/static/sentry/js/**/*',
199+
'src/sentry/templates/sentry/**/*',
200+
'stylelint.config.js',
201+
]),
205202
/**
206203
* Rules are grouped by plugin. If you want to override a specific rule inside
207204
* the recommended set, then it's recommended to spread the new rule on top

package.json

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@
140140
"peggy": "^4.1.1",
141141
"platformicons": "^8.0.0",
142142
"po-catalog-loader": "2.1.0",
143-
"prettier": "3.3.2",
143+
"prettier": "3.5.3",
144144
"prismjs": "^1.29.0",
145145
"process": "^0.11.10",
146146
"qrcode.react": "^3.1.0",
@@ -175,12 +175,12 @@
175175
"zxcvbn": "^4.4.2"
176176
},
177177
"devDependencies": {
178-
"@biomejs/biome": "^1.9.1",
178+
"@biomejs/biome": "^1.9.4",
179179
"@codecov/webpack-plugin": "^1.8.0",
180180
"@emotion/eslint-plugin": "^11.12.0",
181-
"@eslint/compat": "^1.2.4",
182-
"@eslint/eslintrc": "^3.2.0",
183-
"@eslint/js": "^9.17.0",
181+
"@eslint/compat": "^1.2.7",
182+
"@eslint/eslintrc": "^3.3.0",
183+
"@eslint/js": "^9.22.0",
184184
"@pmmmwh/react-refresh-webpack-plugin": "0.5.15",
185185
"@sentry/jest-environment": "6.0.0",
186186
"@sentry/profiling-node": "9.6.0-alpha.0",
@@ -194,19 +194,19 @@
194194
"babel-gettext-extractor": "^4.1.3",
195195
"babel-jest": "29.7.0",
196196
"benchmark": "^2.1.4",
197-
"eslint": "^9.17.0",
198-
"eslint-config-prettier": "^9.1.0",
199-
"eslint-import-resolver-typescript": "^3.7.0",
197+
"eslint": "^9.22.0",
198+
"eslint-config-prettier": "^10.1.1",
199+
"eslint-import-resolver-typescript": "^3.8.3",
200200
"eslint-plugin-import": "^2.31.0",
201-
"eslint-plugin-jest": "^28.10.0",
201+
"eslint-plugin-jest": "^28.11.0",
202202
"eslint-plugin-jest-dom": "^5.5.0",
203-
"eslint-plugin-react": "^7.37.3",
204-
"eslint-plugin-react-hooks": "5.0.0",
203+
"eslint-plugin-react": "^7.37.4",
204+
"eslint-plugin-react-hooks": "5.2.0",
205205
"eslint-plugin-sentry": "^2.10.0",
206206
"eslint-plugin-simple-import-sort": "^12.1.1",
207207
"eslint-plugin-testing-library": "^7.1.1",
208208
"eslint-plugin-typescript-sort-keys": "^3.3.0",
209-
"eslint-plugin-unicorn": "^56.0.1",
209+
"eslint-plugin-unicorn": "^57.0.0",
210210
"globals": "^15.14.0",
211211
"html-webpack-plugin": "^5.6.0",
212212
"jest": "29.7.0",
@@ -220,7 +220,7 @@
220220
"stylelint-config-recommended": "^14.0.1",
221221
"terser": "5.31.6",
222222
"tsconfig-paths": "^4.2.0",
223-
"typescript-eslint": "^8.18.2",
223+
"typescript-eslint": "^8.26.0",
224224
"webpack-dev-server": "5.2.0"
225225
},
226226
"resolutions": {
@@ -256,7 +256,7 @@
256256
"diff-docs": "yarn install-api-docs && ts-node api-docs/openapi-diff.ts",
257257
"deref-api-docs": "ts-node api-docs/index.ts tests/apidocs/openapi-spectacular.json tests/apidocs/openapi-derefed.json",
258258
"build-chartcuterie-config": "NODE_ENV=production webpack --config=config/webpack.chartcuterie.config.ts",
259-
"build-acceptance": "IS_ACCEPTANCE_TEST=1 NODE_ENV=production webpack",
259+
"build-acceptance": "CODECOV_ENABLE_BA=false IS_ACCEPTANCE_TEST=1 NODE_ENV=production webpack",
260260
"build-production": "NODE_ENV=production webpack --mode production",
261261
"build": "NODE_OPTIONS=--max-old-space-size=4096 webpack",
262262
"build-js-loader": "ts-node scripts/build-js-loader.ts",

src/sentry/api/endpoints/debug_files.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ def batch_assemble(project, files):
478478
checksums_to_check -= checksums_without_chunks
479479

480480
# 4. Find missing chunks and group them per checksum.
481-
all_missing_chunks = find_missing_chunks(project.organization, list(chunks_to_check.keys()))
481+
all_missing_chunks = find_missing_chunks(project.organization.id, set(chunks_to_check.keys()))
482482

483483
missing_chunks_per_checksum: dict[str, set[str]] = {}
484484
for chunk in all_missing_chunks:

src/sentry/api/endpoints/organization_artifactbundle_assemble.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,8 @@ def post(self, request: Request, organization) -> Response:
9292
chunks = data.get("chunks", [])
9393

9494
# We check if all requested chunks have been uploaded.
95-
with sentry_sdk.start_span(op="artifact_bundle.assemble.find_missing_chunks"):
96-
missing_chunks = find_missing_chunks(organization, chunks)
95+
missing_chunks = find_missing_chunks(organization.id, set(chunks))
96+
9797
# In case there are some missing chunks, we will tell the client which chunks we require.
9898
if missing_chunks:
9999
return Response(

src/sentry/api/endpoints/seer_rpc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
from sentry.hybridcloud.rpc.service import RpcAuthenticationSetupException, RpcResolutionException
2828
from sentry.hybridcloud.rpc.sig import SerializableFunctionValueException
2929
from sentry.models.organization import Organization
30-
from sentry.seer.fetch_issues_given_patches import get_issues_related_to_file_patches
30+
from sentry.seer.fetch_issues.fetch_issues_given_patches import get_issues_related_to_file_patches
3131
from sentry.silo.base import SiloMode
3232
from sentry.utils.env import in_test_environment
3333

src/sentry/debug_files/upload.py

Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,21 @@
1-
from collections.abc import Sequence
21
from datetime import timedelta
32

43
import sentry_sdk
54
from django.utils import timezone
65

7-
from sentry import features
8-
from sentry.models.files import FileBlob, FileBlobOwner
9-
from sentry.models.organization import Organization
6+
from sentry.models.files import FileBlob
107

118

12-
def find_missing_chunks(organization: Organization, chunks: Sequence[str]):
9+
def find_missing_chunks(organization_id: int, chunks: set[str]):
1310
"""Returns a list of chunks which are missing for an org."""
14-
if features.has("organizations:find-missing-chunks-new", organization):
15-
return _find_missing_chunks_new(organization.id, set(chunks))
16-
17-
return _find_missing_chunks_old(organization.id, set(chunks))
18-
19-
20-
def _find_missing_chunks_new(organization_id: int, chunks: set[str]):
21-
with sentry_sdk.start_span(op="find_missing_chunks_new") as span:
11+
with sentry_sdk.start_span(op="find_missing_chunks") as span:
2212
span.set_tag("organization_id", organization_id)
2313
span.set_data("chunks_size", len(chunks))
2414

2515
if not chunks:
2616
return []
2717

28-
with sentry_sdk.start_span(op="find_missing_chunks_new.fetch_owned_file_blobs"):
18+
with sentry_sdk.start_span(op="find_missing_chunks.fetch_owned_file_blobs"):
2919
owned_file_blobs = FileBlob.objects.filter(
3020
checksum__in=chunks, fileblobowner__organization_id=organization_id
3121
).values_list(
@@ -39,7 +29,7 @@ def _find_missing_chunks_new(organization_id: int, chunks: set[str]):
3929
owned_file_chunks = {checksum for _, checksum, _ in owned_file_blobs}
4030
unowned_file_chunks = chunks - owned_file_chunks
4131

42-
with sentry_sdk.start_span(op="find_missing_chunks_new.fetch_unowned_file_blobs"):
32+
with sentry_sdk.start_span(op="find_missing_chunks.fetch_unowned_file_blobs"):
4333
unowned_file_blobs = FileBlob.objects.filter(
4434
checksum__in=unowned_file_chunks,
4535
).values_list(
@@ -65,27 +55,3 @@ def _find_missing_chunks_new(organization_id: int, chunks: set[str]):
6555

6656
# We return all the file chunks that are not bound to the supply organization.
6757
return list(unowned_file_chunks)
68-
69-
70-
def _find_missing_chunks_old(organization_id: int, chunks: set[str]):
71-
with sentry_sdk.start_span(op="find_missing_chunks_old") as span:
72-
span.set_tag("organization_id", organization_id)
73-
span.set_data("chunks_size", len(chunks))
74-
75-
now = timezone.now()
76-
threshold = now - timedelta(hours=12)
77-
78-
with sentry_sdk.start_span(op="find_missing_chunks_old.update_timestamp"):
79-
FileBlob.objects.filter(checksum__in=chunks, timestamp__lte=threshold).update(
80-
timestamp=now
81-
)
82-
83-
# Compute the set of all existing chunks.
84-
with sentry_sdk.start_span(op="find_missing_chunks_old.get_owned_chunks"):
85-
owned = set(
86-
FileBlobOwner.objects.filter(
87-
blob__checksum__in=chunks, organization_id=organization_id
88-
).values_list("blob__checksum", flat=True)
89-
)
90-
91-
return list(chunks - owned)

src/sentry/event_manager.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2440,12 +2440,10 @@ def _calculate_span_grouping(jobs: Sequence[Job], projects: ProjectsMapping) ->
24402440

24412441

24422442
@sentry_sdk.tracing.trace
2443-
def _detect_performance_problems(
2444-
jobs: Sequence[Job], projects: ProjectsMapping, is_standalone_spans: bool = False
2445-
) -> None:
2443+
def _detect_performance_problems(jobs: Sequence[Job], projects: ProjectsMapping) -> None:
24462444
for job in jobs:
24472445
job["performance_problems"] = detect_performance_problems(
2448-
job["data"], projects[job["project_id"]], is_standalone_spans=is_standalone_spans
2446+
job["data"], projects[job["project_id"]]
24492447
)
24502448

24512449

src/sentry/features/temporary.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ def register_temporary_features(manager: FeatureManager):
251251
manager.add("organizations:performance-trace-details", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
252252
# Enable trace explorer features
253253
manager.add("organizations:performance-trace-explorer", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
254+
# Enable saved queries in trace explorer
255+
manager.add("organizations:performance-saved-queries", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
254256
# Enable querying spans fields stats from comparative workflows project
255257
manager.add("organizations:performance-spans-fields-stats", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
256258
# Enable linking to trace explorer from metrics
@@ -488,13 +490,6 @@ def register_temporary_features(manager: FeatureManager):
488490
manager.add("organizations:ourlogs-ingestion", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
489491
# Enable updated form for 3p publishing flow
490492
manager.add("organizations:streamlined-publishing-flow", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=True)
491-
# Enable new find missing chunks algorithm
492-
manager.add(
493-
"organizations:find-missing-chunks-new",
494-
OrganizationFeature,
495-
FeatureHandlerStrategy.FLAGPOLE,
496-
api_expose=False
497-
)
498493
# Enable per-project selection for Jira integration
499494
manager.add("organizations:jira-per-project-statuses", OrganizationFeature, FeatureHandlerStrategy.FLAGPOLE, api_expose=False)
500495
# Enable Relay extracting logs from breadcrumbs for a project.

src/sentry/hybridcloud/tasks/deliver_webhooks.py

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import orjson
77
import sentry_sdk
8-
from django.db.models import Min, Subquery
8+
from django.db.models import Case, CharField, Min, Subquery, Value, When
99
from django.utils import timezone
1010
from requests import Response
1111
from requests.models import HTTPError
@@ -58,6 +58,14 @@
5858
actions that have been made to the relevant resources.
5959
"""
6060

61+
# Define priorities for different webhook providers
62+
# Lower number means higher priority
63+
PROVIDER_PRIORITY = {
64+
"stripe": 1,
65+
}
66+
# Default priority for providers not explicitly listed above
67+
DEFAULT_PROVIDER_PRIORITY = 10
68+
6169

6270
class DeliveryFailed(Exception):
6371
"""
@@ -77,25 +85,47 @@ def schedule_webhook_delivery(**kwargs: Never) -> None:
7785
Find mailboxes that contain undelivered webhooks that were scheduled
7886
to be delivered now or in the past.
7987
88+
Prioritizes webhooks based on provider importance.
89+
8090
Triggered frequently by celery beat.
8191
"""
82-
# The double call to .values() ensures that the group by includes mailbox_nam
92+
# The double call to .values() ensures that the group by includes mailbox_name
8393
# but only id_min is selected
8494
head_of_line = (
8595
WebhookPayload.objects.all()
8696
.values("mailbox_name")
8797
.annotate(id_min=Min("id"))
8898
.values("id_min")
8999
)
100+
90101
# Get any heads that are scheduled to run
91-
scheduled_mailboxes = WebhookPayload.objects.filter(
92-
schedule_for__lte=timezone.now(),
93-
id__in=Subquery(head_of_line),
94-
).values("id", "mailbox_name")
102+
# Use provider field directly, with default priority for null values
103+
scheduled_mailboxes = (
104+
WebhookPayload.objects.filter(
105+
schedule_for__lte=timezone.now(),
106+
id__in=Subquery(head_of_line),
107+
)
108+
# Set priority value based on provider field
109+
.annotate(
110+
provider_priority=Case(
111+
# For providers that match our priority list
112+
*[
113+
When(provider=provider, then=Value(priority))
114+
for provider, priority in PROVIDER_PRIORITY.items()
115+
],
116+
# Default value for all other cases (including null providers)
117+
default=Value(DEFAULT_PROVIDER_PRIORITY),
118+
output_field=CharField(),
119+
)
120+
)
121+
# Order by priority first (lowest number = highest priority), then ID
122+
.order_by("provider_priority", "id").values("id", "mailbox_name")
123+
)
95124

96125
metrics.distribution(
97126
"hybridcloud.schedule_webhook_delivery.mailbox_count", scheduled_mailboxes.count()
98127
)
128+
99129
for record in scheduled_mailboxes[:BATCH_SIZE]:
100130
# Reschedule the records that we will attempt to deliver next.
101131
# We update schedule_for in an attempt to minimize races for potentially in-flight batches.

0 commit comments

Comments
 (0)