Skip to content

Commit cfde4e6

Browse files
authored
Merge branch 'master' into scttcper/feat-prefetch-supergroups
2 parents b61e554 + 7218e1e commit cfde4e6

File tree

418 files changed

+7421
-4901
lines changed

Some content is hidden

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

418 files changed

+7421
-4901
lines changed

.agents/skills/hybrid-cloud-outboxes/references/debugging.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Understanding the pipeline helps locate where things break:
66

77
1. **Model save/delete** writes outbox row inside `outbox_context(transaction.atomic(...))`
88
2. **On commit**: if `flush=True`, `drain_shard()` runs synchronously for that shard
9-
3. **Periodic task**: `enqueue_outbox_jobs` (region) / `enqueue_outbox_jobs_control` (control) runs on a cron schedule
9+
3. **Periodic task**: `enqueue_outbox_jobs` (cell) / `enqueue_outbox_jobs_control` (control) runs on a cron schedule
1010
4. **`schedule_batch`** partitions the ID range into `CONCURRENCY=5` chunks and spawns `drain_outbox_shards` tasks
1111
5. **`drain_outbox_shards`** calls `process_outbox_batch` which:
1212
- Calls `find_scheduled_shards(lo, hi)` to find shards with `scheduled_for <= now`
@@ -71,9 +71,9 @@ Both tables share these columns:
7171

7272
`sentry_controloutbox` has one additional column:
7373

74-
| Column | Type | Description |
75-
| ------------- | ------- | ----------------------------- |
76-
| `region_name` | varchar | Target region for this outbox |
74+
| Column | Type | Description |
75+
| ------------- | ------- | --------------------------- |
76+
| `region_name` | varchar | Target cell for this outbox |
7777

7878
### Resolving Enum Values
7979

@@ -92,10 +92,10 @@ When generating SQL for a developer, **print the query to the terminal** so they
9292
2. Comments mapping integer values to their enum names
9393
3. Reasonable `LIMIT` clauses to avoid overwhelming output
9494

95-
#### Find stuck shards (region)
95+
#### Find stuck shards (cell)
9696

9797
```sql
98-
-- Find region outbox shards stuck in backoff
98+
-- Find cell outbox shards stuck in backoff
9999
-- shard_scope: 0 = ORGANIZATION_SCOPE, 1 = USER_SCOPE, etc.
100100
-- category: see OutboxCategory enum in category.py
101101
SELECT
@@ -199,7 +199,7 @@ LIMIT 10;
199199

200200
When a developer asks you to debug stuck outboxes:
201201

202-
1. **Determine the table**: Ask which model or direction is involved, or infer from context. Use `sentry_regionoutbox` for region models, `sentry_controloutbox` for control models.
202+
1. **Determine the table**: Ask which model or direction is involved, or infer from context. Use `sentry_regionoutbox` for cell models, `sentry_controloutbox` for control models.
203203
2. **Resolve enum values**: Read `src/sentry/hybridcloud/outbox/category.py` to get the integer values for the relevant `OutboxCategory` and `OutboxScope`.
204204
3. **Construct the query**: Use the templates above, substituting resolved values. Always add comments with the human-readable enum names.
205205
4. **Print to terminal**: Output the final SQL so the developer can copy it. Do NOT attempt to run it — you don't have production database access.
@@ -212,10 +212,10 @@ When a developer asks you to debug stuck outboxes:
212212
The `should_skip_shard()` method checks these options:
213213

214214
```python
215-
# Skip specific organization shards (region outboxes)
215+
# Skip specific organization shards (cell outboxes)
216216
"hybrid_cloud.authentication.disabled_organization_shards": [org_id_1, org_id_2]
217217

218-
# Skip specific user shards (region/control outboxes)
218+
# Skip specific user shards (cell/control outboxes)
219219
"hybrid_cloud.authentication.disabled_user_shards": [user_id_1, user_id_2]
220220
```
221221

@@ -252,7 +252,7 @@ For local debugging or in a Django shell:
252252
```python
253253
from sentry.hybridcloud.models.outbox import CellOutbox, ControlOutbox
254254

255-
# Top 10 deepest region shards
255+
# Top 10 deepest cell shards
256256
for shard in CellOutbox.get_shard_depths_descending(limit=10):
257257
print(f"Scope={shard['shard_scope']} ID={shard['shard_identifier']} Depth={shard['depth']}")
258258
```

.github/CODEOWNERS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,11 @@ tests/sentry/api/endpoints/test_organization_attribute_mappings.py @get
489489
*sentryapp*.py @getsentry/product-owners-settings-integrations @getsentry/ecosystem
490490
*doc_integration*.py @getsentry/ecosystem
491491

492+
/src/sentry/runner/commands/createproject.py @getsentry/ecosystem
493+
/src/sentry/runner/commands/createorg.py @getsentry/ecosystem
494+
/tests/sentry/runner/commands/test_createproject.py @getsentry/ecosystem
495+
/tests/sentry/runner/commands/test_createorg.py @getsentry/ecosystem
496+
492497
## End of Integrations
493498

494499

@@ -680,6 +685,7 @@ tests/sentry/api/endpoints/test_organization_attribute_mappings.py @get
680685
/static/app/views/issueList/supergroups/ @getsentry/issue-detection-frontend
681686
/static/app/views/issueDetails/ @getsentry/issue-workflow
682687
/static/app/views/nav/secondary/sections/issues/ @getsentry/issue-workflow
688+
/static/app/views/sharedGroupDetails/ @getsentry/issue-workflow
683689
/static/app/components/events/interfaces/performance/spanEvidenceKeyValueList.tsx @getsentry/issue-detection-frontend
684690
/static/app/components/events/interfaces/crashContent/exception/actionableItems.tsx @getsentry/issue-workflow
685691
/tests/sentry/deletions/test_group.py @getsentry/issue-detection-backend
@@ -807,6 +813,7 @@ tests/sentry/api/endpoints/test_organization_attribute_mappings.py @get
807813
/static/app/utils/theme/ @getsentry/design-engineering
808814
/static/app/components/commandPalette/ @getsentry/design-engineering
809815
/static/app/components/core/ @getsentry/design-engineering
816+
/static/app/components/dnd/ @getsentry/design-engineering
810817
/static/app/components/pageFilters/ @getsentry/design-engineering
811818
/static/app/icons/ @getsentry/design-engineering
812819
/static/app/stories/ @getsentry/design-engineering

.github/codeowners-coverage-baseline.txt

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ src/sentry/runner/commands/cleanup.py
184184
src/sentry/runner/commands/config.py
185185
src/sentry/runner/commands/configoptions.py
186186
src/sentry/runner/commands/createflag.py
187+
src/sentry/runner/commands/createorg.py
187188
src/sentry/runner/commands/createuser.py
188189
src/sentry/runner/commands/devserver.py
189190
src/sentry/runner/commands/devservices.py
@@ -225,9 +226,6 @@ src/sentry/scripts/quotas/is_rate_limited.lua
225226
src/sentry/scripts/ratelimits/api_limiter.lua
226227
src/sentry/scripts/ratelimits/leaky_bucket.lua
227228
src/sentry/scripts/similarity/index.lua
228-
src/sentry/scripts/spans/add-buffer.lua
229-
src/sentry/scripts/spans/done-flush-segment-data.lua
230-
src/sentry/scripts/spans/done-flush-segment.lua
231229
src/sentry/scripts/tsdb/cmsketch.lua
232230
src/sentry/scripts/utils/locking/delete_lock.lua
233231
src/sentry/scripts/utils/sadd_capped.lua
@@ -604,14 +602,9 @@ static/app/components/events/eventAttachmentActions.tsx
604602
static/app/components/events/eventAttachments.spec.tsx
605603
static/app/components/events/eventAttachments.tsx
606604
static/app/components/events/eventAttachmentsCrashReportsNotice.tsx
607-
static/app/components/events/eventBreadcrumbsSection.spec.tsx
608-
static/app/components/events/eventBreadcrumbsSection.tsx
609605
static/app/components/events/eventCustomPerformanceMetrics.tsx
610606
static/app/components/events/eventDataSection.tsx
611607
static/app/components/events/eventDrawer.tsx
612-
static/app/components/events/eventEntries.spec.tsx
613-
static/app/components/events/eventEntries.tsx
614-
static/app/components/events/eventEntry.tsx
615608
static/app/components/events/eventEvidence.spec.tsx
616609
static/app/components/events/eventEvidence.tsx
617610
static/app/components/events/eventExtraData/getEventExtraDataKnownDataDetails.tsx
@@ -1022,18 +1015,6 @@ static/app/components/replaysOnboarding/replayConfigToggle.tsx
10221015
static/app/components/replaysOnboarding/replayOnboardingLayout.tsx
10231016
static/app/components/replaysOnboarding/sidebar.tsx
10241017
static/app/components/replaysOnboarding/utils.tsx
1025-
static/app/components/repositories/getRepoStatusLabel.tsx
1026-
static/app/components/repositories/repoProviderIcon.tsx
1027-
static/app/components/repositories/scmIntegrationTree/providerConfigLink.tsx
1028-
static/app/components/repositories/scmIntegrationTree/scmIntegrationTree.tsx
1029-
static/app/components/repositories/scmIntegrationTree/scmIntegrationTreeNodes.ts
1030-
static/app/components/repositories/scmIntegrationTree/scmIntegrationTreeRow.tsx
1031-
static/app/components/repositories/scmIntegrationTree/scmTreeFilters.tsx
1032-
static/app/components/repositories/scmIntegrationTree/types.ts
1033-
static/app/components/repositories/scmIntegrationTree/useScmIntegrationTreeData.spec.tsx
1034-
static/app/components/repositories/scmIntegrationTree/useScmIntegrationTreeData.ts
1035-
static/app/components/repositories/scmIntegrationTree/useScmTreeFilters.tsx
1036-
static/app/components/repositories/scmRepoTreeModal.tsx
10371018
static/app/components/repositoryRow.spec.tsx
10381019
static/app/components/repositoryRow.tsx
10391020
static/app/components/resolutionBox.spec.tsx
@@ -1488,9 +1469,6 @@ static/app/views/setupWizard/utils/useOrganizationsWithRegion.tsx
14881469
static/app/views/setupWizard/utils/useUpdateWizardCache.tsx
14891470
static/app/views/setupWizard/waitingForWizardToConnect.tsx
14901471
static/app/views/setupWizard/wizardProjectSelection.tsx
1491-
static/app/views/sharedGroupDetails/index.spec.tsx
1492-
static/app/views/sharedGroupDetails/index.tsx
1493-
static/app/views/sharedGroupDetails/sharedGroupHeader.tsx
14941472
static/app/views/unsubscribe/issue.spec.tsx
14951473
static/app/views/unsubscribe/issue.tsx
14961474
static/app/views/unsubscribe/project.spec.tsx
@@ -2317,6 +2295,7 @@ tests/sentry/runner/commands/test_cleanup.py
23172295
tests/sentry/runner/commands/test_config.py
23182296
tests/sentry/runner/commands/test_configoptions.py
23192297
tests/sentry/runner/commands/test_createflag.py
2298+
tests/sentry/runner/commands/test_createorg.py
23202299
tests/sentry/runner/commands/test_createuser.py
23212300
tests/sentry/runner/commands/test_init.py
23222301
tests/sentry/runner/commands/test_migrations.py

.github/workflows/backend-selective.yml

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ jobs:
179179
env:
180180
MATRIX_INSTANCE_TOTAL: ${{ needs.calculate-shards.outputs.shard-count }}
181181
TEST_GROUP_STRATEGY: roundrobin
182+
PYTHONHASHSEED: '0'
183+
XDIST_PER_WORKER_SNUBA: '1'
184+
XDIST_WORKERS: '2'
182185
steps:
183186
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
184187

@@ -195,6 +198,58 @@ jobs:
195198
sudo install -m 755 odiff-linux-x64 /usr/local/bin/odiff
196199
rm odiff-linux-x64
197200
201+
- name: Bootstrap per-worker Snuba instances
202+
run: |
203+
set -eo pipefail
204+
SNUBA_IMAGE=$(docker inspect snuba-snuba-1 --format '{{.Config.Image}}')
205+
SNUBA_NETWORK=$(docker inspect snuba-snuba-1 --format '{{range $k, $v := .NetworkSettings.Networks}}{{$k}}{{end}}')
206+
if [ -z "$SNUBA_IMAGE" ] || [ -z "$SNUBA_NETWORK" ]; then
207+
echo "ERROR: Could not inspect snuba-snuba-1 container. Is devservices running?"
208+
exit 1
209+
fi
210+
211+
docker stop snuba-snuba-1 || true
212+
213+
PIDS=()
214+
for i in $(seq 0 $(( ${XDIST_WORKERS} - 1 ))); do
215+
(
216+
WORKER_DB="default_gw${i}"
217+
WORKER_PORT=$((1230 + i))
218+
curl -sf 'http://localhost:8123/' --data-binary "CREATE DATABASE IF NOT EXISTS ${WORKER_DB}"
219+
docker run --rm --network "$SNUBA_NETWORK" \
220+
-e "CLICKHOUSE_DATABASE=${WORKER_DB}" -e "CLICKHOUSE_HOST=clickhouse" \
221+
-e "CLICKHOUSE_PORT=9000" -e "CLICKHOUSE_HTTP_PORT=8123" \
222+
-e "DEFAULT_BROKERS=kafka:9093" -e "REDIS_HOST=redis" \
223+
-e "REDIS_PORT=6379" -e "REDIS_DB=1" -e "SNUBA_SETTINGS=docker" \
224+
"$SNUBA_IMAGE" bootstrap --force 2>&1 | tail -3
225+
docker run -d --name "snuba-gw${i}" --network "$SNUBA_NETWORK" \
226+
-p "${WORKER_PORT}:1218" \
227+
-e "CLICKHOUSE_DATABASE=${WORKER_DB}" -e "CLICKHOUSE_HOST=clickhouse" \
228+
-e "CLICKHOUSE_PORT=9000" -e "CLICKHOUSE_HTTP_PORT=8123" \
229+
-e "DEFAULT_BROKERS=kafka:9093" -e "REDIS_HOST=redis" \
230+
-e "REDIS_PORT=6379" -e "REDIS_DB=1" -e "SNUBA_SETTINGS=docker" \
231+
-e "DEBUG=1" "$SNUBA_IMAGE" api
232+
233+
for attempt in $(seq 1 30); do
234+
if curl -sf "http://127.0.0.1:${WORKER_PORT}/health" > /dev/null 2>&1; then
235+
echo "snuba-gw${i} healthy on port ${WORKER_PORT}"
236+
break
237+
fi
238+
if [ "$attempt" -eq 30 ]; then
239+
echo "ERROR: snuba-gw${i} failed health check after 30 attempts"
240+
docker logs "snuba-gw${i}" 2>&1 | tail -20 || true
241+
exit 1
242+
fi
243+
sleep 2
244+
done
245+
) &
246+
PIDS+=($!)
247+
done
248+
249+
for pid in "${PIDS[@]}"; do
250+
wait "$pid" || { echo "ERROR: Snuba bootstrap subshell (PID $pid) failed"; exit 1; }
251+
done
252+
198253
- name: Download selected tests artifact
199254
if: needs.select-tests.outputs.has-selected-tests == 'true'
200255
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
@@ -206,6 +261,7 @@ jobs:
206261
env:
207262
SELECTED_TESTS_FILE: ${{ needs.select-tests.outputs.has-selected-tests == 'true' && '.artifacts/selected-tests.txt' || '' }}
208263
run: |
264+
export PYTEST_ADDOPTS="$PYTEST_ADDOPTS -n ${XDIST_WORKERS} --dist=loadfile"
209265
make test-python-ci
210266
211267
- name: Inspect failure
@@ -214,3 +270,8 @@ jobs:
214270
if command -v devservices; then
215271
devservices logs
216272
fi
273+
274+
for i in $(seq 0 $(( ${XDIST_WORKERS} - 1 ))); do
275+
echo "--- snuba-gw${i} logs ---"
276+
docker logs "snuba-gw${i}" 2>&1 | tail -30 || true
277+
done

.github/workflows/backend.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,10 +272,9 @@ jobs:
272272
# Dynamic total from calculate-shards
273273
MATRIX_INSTANCE_TOTAL: ${{ needs.calculate-shards.outputs.shard-count }}
274274
TEST_GROUP_STRATEGY: roundrobin
275-
# xdist: enabled on master pushes and workflow_dispatch, disabled on PRs
276-
PYTHONHASHSEED: ${{ github.event_name != 'pull_request' && '0' || 'random' }}
277-
XDIST_PER_WORKER_SNUBA: ${{ github.event_name != 'pull_request' && '1' || '' }}
278-
XDIST_WORKERS: ${{ github.event_name != 'pull_request' && '3' || '' }}
275+
PYTHONHASHSEED: '0'
276+
XDIST_PER_WORKER_SNUBA: '1'
277+
XDIST_WORKERS: ${{ github.event_name == 'pull_request' && '2' || '3' }}
279278

280279
steps:
281280
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7

.github/workflows/frontend-optional.yml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,11 @@ jobs:
2525
testable_rules_changed: ${{ steps.changes.outputs.testable_rules_changed }}
2626
typecheckable_rules_changed: ${{ steps.changes.outputs.typecheckable_rules_changed }}
2727
frontend_all: ${{ steps.changes.outputs.frontend_all }}
28+
merge_base: ${{ steps.merge_base.outputs.merge_base }}
2829
steps:
2930
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
31+
with:
32+
fetch-depth: 100
3033

3134
- name: Check for frontend file changes
3235
uses: dorny/paths-filter@0bc4621a3135347011ad047f9ecf449bf72ce2bd # v3.0.0
@@ -36,6 +39,88 @@ jobs:
3639
filters: .github/file-filters.yml
3740
list-files: shell
3841

42+
# On PRs, HEAD is the merge commit; its parents (HEAD^1, HEAD^2) are base and head.
43+
# Merge base of those two is what Jest --changedSince needs.
44+
# If merge base can't be computed or non-frontend files changed, output is empty
45+
# and the optional Jest job will be skipped entirely.
46+
- name: Get merge base for changedSince
47+
id: merge_base
48+
run: |
49+
MERGE_BASE=$(git merge-base HEAD^1 HEAD^2 2>/dev/null) || true
50+
if [ -n "$MERGE_BASE" ]; then
51+
CHANGED=$(git diff --name-only "$MERGE_BASE" HEAD^2)
52+
if echo "$CHANGED" | grep -qvE '^static/'; then
53+
echo "Non-frontend file changed — skipping optional Jest"
54+
MERGE_BASE=""
55+
else
56+
echo "Merge base: $MERGE_BASE (Jest will use --changedSince)"
57+
fi
58+
else
59+
echo "Could not compute merge base — skipping optional Jest"
60+
fi
61+
echo "merge_base=${MERGE_BASE:-}" >> "$GITHUB_OUTPUT"
62+
63+
# This job intentionally mirrors `frontend-jest-tests` in frontend.yml.
64+
# Our intent is to try it out for a few weeks and see if it's stable.
65+
frontend-jest-tests-changed-only:
66+
if: >-
67+
needs.files-changed.outputs.merge_base != '' &&
68+
(needs.files-changed.outputs.testable_rules_changed == 'true' || needs.files-changed.outputs.testable_modified == 'true')
69+
needs: [files-changed]
70+
name: Jest
71+
# If you change the runs-on image, you must also change the runner in jest-balance.yml
72+
# so that the balancer runs in the same environment as the tests.
73+
runs-on: ubuntu-24.04
74+
timeout-minutes: 30
75+
strategy:
76+
# This helps not having to run multiple jobs because one fails, thus, reducing resource usage
77+
# and reducing the risk that one of many runs would turn red again (read: intermittent tests)
78+
fail-fast: false
79+
matrix:
80+
# XXX: When updating this, make sure you also update CI_NODE_TOTAL.
81+
instance: [0, 1, 2, 3]
82+
83+
steps:
84+
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
85+
name: Checkout sentry
86+
with:
87+
# PRs need history so we can compute merge base for Jest --changedSince.
88+
# 100 is an arbitrary depth that will get most reasonable PRs' commits.
89+
fetch-depth: ${{ github.event_name == 'pull_request' && '100' || '1' }}
90+
91+
- uses: ./.github/actions/setup-node-pnpm
92+
93+
- name: Download jest-balance.json
94+
id: download-artifact
95+
uses: dawidd6/action-download-artifact@ac66b43f0e6a346234dd65d4d0c8fbb31cb316e5 # v11
96+
with:
97+
workflow: 38531594 # jest-balancer.yml
98+
workflow_conclusion: success # The conclusion of the workflow we're looking for
99+
branch: master # The branch we're looking for
100+
name: jest-balance.json # Artifact name
101+
name_is_regexp: false
102+
path: tests/js/test-balancer/ # Directory where to extract artifact(s), defaults to the current directory
103+
search_artifacts: true # Search for the last workflow run whose stored the artifact we're looking for
104+
if_no_artifact_found: warn # Can be one of: "fail", "warn", "ignore"
105+
106+
- name: jest
107+
env:
108+
GITHUB_PR_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
109+
GITHUB_PR_REF: ${{ github.event.pull_request.head.ref || github.ref }}
110+
# XXX: CI_NODE_TOTAL must be hardcoded to the length of strategy.matrix.instance.
111+
# Otherwise, if there are other things in the matrix, using strategy.job-total
112+
# wouldn't be correct.
113+
CI_NODE_TOTAL: 4
114+
CI_NODE_INDEX: ${{ matrix.instance }}
115+
# Disable testing-library from printing out any of of the DOM to
116+
# stdout. No one actually looks through this in CI, they're just
117+
# going to run it locally.
118+
#
119+
# This quiets up the logs quite a bit.
120+
DEBUG_PRINT_LIMIT: 0
121+
MERGE_BASE: ${{ needs.files-changed.outputs.merge_base }}
122+
run: pnpm run test-ci --forceExit
123+
39124
typescript-native:
40125
if: needs.files-changed.outputs.frontend_all == 'true'
41126
needs: files-changed

eslint.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,7 @@ export default typescript.config([
928928
name: 'files/jest related',
929929
files: [
930930
'tests/js/jest-pegjs-transform.js',
931+
'tests/js/sentry-test/jest-environment.js',
931932
'tests/js/sentry-test/mocks/*',
932933
'tests/js/sentry-test/loadFixtures.ts',
933934
'tests/js/setup.ts',

0 commit comments

Comments
 (0)