Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
2e6c743
ref(preprod): Remove snapshots from retention endpoint (#112096)
chromy Apr 2, 2026
58d978d
feat(github): Add frontend implementation for GitHub integration pipe…
evanpurkhiser Apr 2, 2026
c76ab6e
chore(org-tokens): Update scope help text for organization tokens (#1…
romtsn Apr 2, 2026
9da8f49
ref(lint): update boundaries config (#112106)
natemoo-re Apr 2, 2026
ee6bf90
fix(workflows): add detector group caching in ensure_association_with…
klochek Apr 2, 2026
9ee682f
fix(web_vitals): Add z-index to `PerformanceScoreRingTooltip` (#112111)
mjq Apr 2, 2026
6d8a90d
chore(search): Clean up old flow experiement (#112103)
nsdeschenes Apr 2, 2026
b1fb664
fix(cross-events): Enable raw-search replacement (#112099)
nsdeschenes Apr 2, 2026
3e9d94c
fix(preprod): Use recompare endpoint and add user-facing status check…
NicoHinderling Apr 2, 2026
2c44e5b
feat(scraps): indeterminate loader (#111369)
natemoo-re Apr 2, 2026
d4baa33
perf(vercel): Skip gzip pre-compression and cache hashed assets in de…
scttcper Apr 2, 2026
11c6c73
ref: bump sentry-arroyo to 2.38.7 (#112117)
getsentry-bot Apr 2, 2026
2d45906
perf(workflows): Unify AlertRuleDetectory querying in WorkflowEngineD…
kcons Apr 2, 2026
ce286c6
feat(ui): Port eslint-plugin-sentry to this repo (#112081)
scttcper Apr 2, 2026
54c9b67
chore(ACI): Update front end usage of detector create endpoint to pro…
ceorourke Apr 2, 2026
78f6d10
feat(billing): Select single project usage CSV (#112044)
brendanhsentry Apr 2, 2026
c439c84
chore(ACI): Combine issue alert rule flags into one (#112072)
ceorourke Apr 2, 2026
169b70a
ref(cells): Switch to SENTRY_LOCAL_CELL and SENTRY_CELLS (#111932)
lynnagara Apr 2, 2026
20d65df
feat(seer): Add issue summary experimental flag (#112115)
JoshFerge Apr 2, 2026
04c8c9f
ref(assisted-query): add new analytics events segmented by area (#112…
aliu39 Apr 2, 2026
53449b9
feat(seer): Send page_name in explorer chat requests (#112065)
Mihir-Mavalankar Apr 2, 2026
ce993dc
chore(flags): Migrate organizations:sdk-crash-detection to flagpole (…
wedamija Apr 2, 2026
59c60ad
chore(flags): Migrate projects:first-event-severity-calculation to fl…
wedamija Apr 2, 2026
787bdf4
Revert "feat(scraps): indeterminate loader (#111369)"
getsentry-bot Apr 2, 2026
cfd8adf
chore(ACI): Publish project based endpoint, unpublish org based one (…
ceorourke Apr 2, 2026
df00d46
ref(preprod): Call status check tasks synchronously (#112120)
NicoHinderling Apr 2, 2026
f124333
feat(aci): Add numbers to monitor and alert form sections (#111898)
malwilley Apr 2, 2026
c07d006
chore(ACI): Add custom metric and other aggregate options to API docs…
ceorourke Apr 2, 2026
d3035c2
fix(preprod): Use RPC service for cross-silo user lookup in snapshot …
NicoHinderling Apr 2, 2026
689fe1b
ref(issues): Remove streamline flag from archive/resolve (#112128)
scttcper Apr 2, 2026
9362bc4
feat(replay): Add a button to toggle the replay-details layout betwee…
ryan953 Apr 2, 2026
25e858f
feat(explore): Add issue post processor on occurrences dataset (#111724)
manessaraj Apr 2, 2026
6f250d5
chore(flags): Move hardcoded LA flags to options automator (#112131)
wedamija Apr 2, 2026
f2277e8
feat(seer): Send page_name in explorer chat requests from frontend (#…
Mihir-Mavalankar Apr 2, 2026
37b85ad
fix(ci): Bandaid fix flaky "Event loop was closed" in CI (#112133)
kenzoengineer Apr 2, 2026
fe209d6
feat(cells): Create the projectkey endpoint for synapse (#112047)
lynnagara Apr 2, 2026
91dffea
fix(txn_summary): Implement Web Vitals chart & sidebar for EAP (#112005)
mjq Apr 2, 2026
16c642c
fix(alerts): Incorporate EventsAnalyticsPlatform into our downgrade c…
kcons Apr 2, 2026
66a0fea
ref(cells): Remove region_name param from convert_to_async_slack_resp…
lynnagara Apr 2, 2026
f2ea09e
fix(github): Add accessibleOnly param to scope repo search to install…
jaydgoss Apr 2, 2026
d7407ed
Add additional label after attribute and tag fields in issue alert ed…
souredoutlook Apr 2, 2026
cef52cb
fix(onboarding): Pass accessibleOnly to SCM repo search (#111895)
jaydgoss Apr 2, 2026
ed9a114
feat(aci): Update new alert UI copy to make it a bit easier to unders…
malwilley Apr 2, 2026
0ee3413
fix(aci): Standardize monitor/alert form behavior on save (#111490)
malwilley Apr 2, 2026
7fd9ea6
feat(autofix): add root cause as valid stopping point under feature f…
srest2021 Apr 2, 2026
a81ef60
fix(hybrid-cloud): Add projectkey-cell-mappings to control silo URL p…
NicoHinderling Apr 2, 2026
8ae6c20
fix(pipeline): Add CSP nonce to trampoline inline script (#112149)
evanpurkhiser Apr 2, 2026
0db1d65
ref(preprod): Dispatch only taskbroker when rollout flag is enabled (…
NicoHinderling Apr 2, 2026
4e18c93
feat(seer): Add feature flag and register Dashboard in LLM context tr…
Mihir-Mavalankar Apr 2, 2026
4284918
ref(selective-testing): Add tests to always be executed in selective …
rbro112 Apr 2, 2026
8eac07f
style(preprod): Increase gap between approval tag and avatars (#112158)
NicoHinderling Apr 2, 2026
ccc79b5
feat(experiments): Add useExperiment hook for flagpole experiments (#…
evanpurkhiser Apr 2, 2026
1dc89ef
feat(flagpole): Register onboarding-scm-experiment feature flag (#112…
jaydgoss Apr 2, 2026
4b2f7a1
deps(ui): Upgrade to typescript 6, bump tsgo (#110151)
scttcper Apr 2, 2026
58e81b8
feat(preprod): Split Mobile Builds settings into tabbed interface (#1…
mtopo27 Apr 2, 2026
d935d89
ref(pipeline): Use `_pipeline_source` in tranpoline (#112155)
evanpurkhiser Apr 2, 2026
662bbbf
ref(cmdk) improve searching (#112007)
JonasBa Apr 2, 2026
94c4a90
feat(github): Add missing test for githubIntegrationPipeline + cleanu…
evanpurkhiser Apr 2, 2026
7dd4e50
fix(lint): Close S016 loophole for attribute-access ThreadPoolExecuto…
gricha Apr 2, 2026
9878833
ref(api): Rename organization serializers for clarity (#112159)
evanpurkhiser Apr 2, 2026
f18f901
fix(issues): Include exception header in copy-as-text for new stack t…
scttcper Apr 2, 2026
e047451
feat(notifications): Pre compute notification fields prior to calling…
Christinarlong Apr 2, 2026
6d6c13e
chore(codeowners): remove owners-api (#112167)
sentaur-athena Apr 2, 2026
1b357ce
ref(assisted-query): Remove old ai_query analytics events and analyti…
aliu39 Apr 2, 2026
f2ee677
fix(seer): Add dual-write when project preference doesn't exist, and …
srest2021 Apr 2, 2026
139fe19
fix(coding integrations): fix add integration button on safari (#112176)
sehr-m Apr 2, 2026
42cace6
ref(cells): REGION_NAME_LENGTH -> CELL_NAME_LENGTH (#112142)
lynnagara Apr 2, 2026
d8bef7e
ref(cells): Update agent skills with cells naming (#112144)
lynnagara Apr 2, 2026
41803b2
ref(cells): as_region_outbox -> as_cell_outbox (#112145)
lynnagara Apr 2, 2026
36ffdae
Revert "feat(notifications): Pre compute notification fields prior to…
getsentry-bot Apr 2, 2026
cb29880
feat: Add ViewerContext dataclass and contextvar module (#112156)
gricha Apr 2, 2026
1063b9d
ref(cells): DestinationType.SENTRY_REGION -> DestinationType.SENTRY_C…
lynnagara Apr 2, 2026
2dcd34c
chore(ACI): Remove unused org scoped detector creation endpoint (#112…
ceorourke Apr 2, 2026
1d47368
chore(ACI): Remove unused feature flags for API deprecation (#112121)
ceorourke Apr 2, 2026
c98cd86
ref(aq): add progress labels for currently supported tools (#112175)
aliu39 Apr 2, 2026
58bc634
ref(preprod): Add snapshots option for builds endpoint (#112180)
NicoHinderling Apr 2, 2026
9dd1872
ref(seer): Include project slug-id map for repo-project fetch (#112164)
kddubey Apr 2, 2026
18da239
feat(notification): Simplify renderer and data to be already processe…
Christinarlong Apr 2, 2026
303c904
Revert "ref(assisted-query): Remove old ai_query analytics events and…
aliu39 Apr 2, 2026
67bc454
feat: Add ViewerContext middleware for API requests (#112172)
gricha Apr 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
10 changes: 5 additions & 5 deletions .agents/skills/hybrid-cloud-outboxes/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ description: >-

Sentry uses a **transactional outbox pattern** for eventually consistent operations. When a model changes, an outbox row is written inside the same database transaction. After the transaction commits, the outbox is drained — firing a signal that triggers side effects such as RPC calls, tombstone propagation, or audit logging.

The most common use case is **cross-silo data replication**: a model saved in the Region silo produces a `RegionOutbox` that, when processed, replicates data to the Control silo (or vice versa via `ControlOutbox`). But the pattern is general — outboxes work for any operation that should happen reliably after a transaction commits, even within a single silo.
The most common use case is **cross-silo data replication**: a model saved in the Cell silo produces a `CellOutbox` that, when processed, replicates data to the Control silo (or vice versa via `ControlOutbox`). But the pattern is general — outboxes work for any operation that should happen reliably after a transaction commits, even within a single silo.

There are two outbox types corresponding to the two directions of flow:

- **`RegionOutbox`** — written in a Cell silo, processed in the Cell silo to push data toward Control (via RPC calls in signal receivers).
- **`CellOutbox`** — written in a Cell silo, processed in the Cell silo to push data toward Control (via RPC calls in signal receivers).
- **`ControlOutbox`** — written in the Control silo, processed in the Control silo to push data toward one or more Cell silos. Each `ControlOutbox` row targets a specific `cell_name`.

## Critical Constraints
Expand Down Expand Up @@ -66,7 +66,7 @@ There are two outbox types corresponding to the two directions of flow:

| Data lives in... | Replicates toward... | Mixin | Outbox type |
| ---------------- | -------------------- | ------------------------ | --------------- |
| Cell silo | Control silo | `ReplicatedCellModel` | `RegionOutbox` |
| Cell silo | Control silo | `ReplicatedCellModel` | `CellOutbox` |
| Control silo | Cell silo(s) | `ReplicatedControlModel` | `ControlOutbox` |

### 2.2 `ReplicatedCellModel` Template
Expand Down Expand Up @@ -148,7 +148,7 @@ class MyModel(ReplicatedCellModel):

### 2.3 `ReplicatedControlModel` Template

Use this when a Control model needs to replicate data to Region silo(s). The key difference: Control outboxes fan out to one or more cells, so the model must declare which cells to target.
Use this when a Control model needs to replicate data to Cell silo(s). The key difference: Control outboxes fan out to one or more cells, so the model must declare which cells to target.

```python
from sentry.db.models import control_silo_model
Expand Down Expand Up @@ -360,7 +360,7 @@ def test_idempotent_replication(self):

### 7.3 Silo Test Decorators

- Use **`@cell_silo_test`** for tests focused on `RegionOutbox` creation
- Use **`@cell_silo_test`** for tests focused on `CellOutbox` creation
- Use **`@control_silo_test`** for tests focused on `ControlOutbox` creation
- Use **`@all_silo_test`** for end-to-end replication tests that exercise both silos
- Only use **`TransactionTestCase`** for threading/concurrency tests (e.g., `threading.Barrier`), not for standard outbox drain tests
Expand Down
4 changes: 2 additions & 2 deletions .agents/skills/hybrid-cloud-outboxes/references/backfill.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ Each batch (via `process_outbox_backfill_batch`):

1. Calls `_chunk_processing_batch` to determine the ID range `(low, up)` for this batch
2. For each instance in `model.objects.filter(id__gte=low, id__lte=up)`:
- Region models: `inst.outbox_for_update().save()` inside `outbox_context(flush=False)`
- Cell models: `inst.outbox_for_update().save()` inside `outbox_context(flush=False)`
- Control models: saves all `inst.outboxes_for_update()` inside `outbox_context(flush=False)`
3. If no more rows: sets cursor to `(0, replication_version + 1)` (marks complete)
4. Otherwise: advances cursor to `(up + 1, version)`
Expand Down Expand Up @@ -141,7 +141,7 @@ options.get("outbox_replication.sentry_mymodel.replication_version")
### Check Outbox Queue Depth

```sql
-- Region outboxes for a specific category
-- Cell outboxes for a specific category
SELECT count(*) FROM sentry_regionoutbox
WHERE category = <category_value>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ When debugging stuck outboxes, you'll often need to generate SQL for a developer

| Direction | Model class | Table name |
| ------------------ | --------------- | ---------------------- |
| Cell -> Control | `RegionOutbox` | `sentry_regionoutbox` |
| Cell -> Control | `CellOutbox` | `sentry_regionoutbox` |
| Control -> Cell(s) | `ControlOutbox` | `sentry_controloutbox` |

**How to determine direction**: Look at the model that changed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ from .model import * # noqa
from .service import * # noqa
```

## `model.py` (REGION silo example)
## `model.py` (CELL silo example)

```python
# Please do not use
Expand Down Expand Up @@ -125,7 +125,7 @@ def serialize_my_thing(obj: MyThing) -> RpcMyThing:
)
```

## `service.py` (REGION silo)
## `service.py` (CELL silo)

```python
# Please do not use
Expand Down Expand Up @@ -223,7 +223,7 @@ class MyMappingService(RpcService):
my_mapping_service = MyMappingService.create_delegation()
```

## `impl.py` (REGION silo example)
## `impl.py` (CELL silo example)

```python
from __future__ import annotations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import pytest

from sentry.hybridcloud.models.outbox import (
ControlOutbox,
RegionOutbox,
CellOutbox,
outbox_context,
)
from sentry.hybridcloud.outbox.category import OutboxCategory, OutboxScope
Expand Down Expand Up @@ -181,7 +181,7 @@ class Test{Feature}OutboxProcessing(TestCase):
- **`assume_test_silo_mode_of(Model)`** is preferred for checking a specific model's state cross-silo. Auto-detects the model's silo.
- **`assume_test_silo_mode(SiloMode.X)`** for blocks accessing multiple models or non-model resources.
- **Factory calls** (`self.create_organization()`, etc.) must NEVER be wrapped in `assume_test_silo_mode`. Factories handle silo mode internally.
- **`@control_silo_test`** for tests focused on `ControlOutbox` records. **`@cell_silo_test`** for `RegionOutbox`.
- **`@control_silo_test`** for tests focused on `ControlOutbox` records. **`@cell_silo_test`** for `CellOutbox`.
- Only use **`TransactionTestCase`** for threading/concurrency tests (e.g., `threading.Barrier`), not for standard outbox drain tests.
- Outbox drain fixtures can clear state between tests:
```python
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The most common sources of stale IDs:
1. **Snuba/ClickHouse query results** -- Snuba stores issue IDs, project IDs, and group IDs that may be deleted from Postgres before Snuba data expires
2. **Workflow engine references** -- Detectors, subscriptions, and alert rules reference objects deleted asynchronously
3. **Integration state** -- SentryAppInstallation, ServiceHook, or ExternalActor deleted while alert rules still reference them
4. **Cross-silo references** -- Region silo holds IDs that reference control silo objects (or vice versa) that may be deleted asynchronously
4. **Cross-silo references** -- Cell silo holds IDs that reference control silo objects (or vice versa) that may be deleted asynchronously
5. **Cached foreign keys** -- A ProjectKey cached in Redis still references a `project_id` for a deleted project
6. **Monitor/cron references** -- Environment objects referenced by monitors that may be deleted

Expand Down Expand Up @@ -120,7 +120,7 @@ except Subscription.DoesNotExist:
| Environment deleted while monitors reference it | High | `Environment.objects.get(id=monitor.env_id)` |
| Integration uninstalled while rules active | High | Alert rules referencing deleted SentryApp |
| Cached FK target deleted | Medium | `get_from_cache(id=fk_id)` after parent deleted |
| Cross-silo object deleted asynchronously | Medium | Region silo references control silo object |
| Cross-silo object deleted asynchronously | Medium | Cell silo references control silo object |

## Fix Patterns

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ projects = self.get_projects(
| -------------- | ---------------------- | ------------------------------------ | ------------------------------------------- |
| Org-scoped | `OrganizationEndpoint` | `organization` in kwargs | `OrganizationPermission` (org:read for GET) |
| Project-scoped | `ProjectEndpoint` | `organization` + `project` in kwargs | `ProjectPermission` |
| Region silo | `RegionSiloEndpoint` | Nothing — must implement own auth | None |
| Cell silo | `CellSiloEndpoint` | Nothing — must implement own auth | None |
| Control silo | `ControlSiloEndpoint` | Nothing — must implement own auth | None |

If an endpoint inherits `RegionSiloEndpoint` or `Endpoint` directly instead of `OrganizationEndpoint`/`ProjectEndpoint`, verify it has its own authorization logic.
If an endpoint inherits `CellSiloEndpoint` or `Endpoint` directly instead of `OrganizationEndpoint`/`ProjectEndpoint`, verify it has its own authorization logic.
24 changes: 15 additions & 9 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -553,14 +553,14 @@ tests/sentry/api/endpoints/test_organization_attribute_mappings.py @get


## APIs
/src/sentry/apidocs/ @getsentry/owners-api
/src/sentry/api/urls.py @getsentry/owners-api
/api-docs/ @getsentry/owners-api
/tests/apidocs/ @getsentry/owners-api
/.github/workflows/openapi.yml @getsentry/owners-api
/.github/workflows/openapi-diff.yml @getsentry/owners-api
/src/sentry/conf/api_pagination_allowlist_do_not_modify.py @getsentry/owners-api
/tests/sentry/api/test_path_params.py @getsentry/owners-api
/src/sentry/apidocs/ @getsentry/docs
/src/sentry/api/urls.py @getsentry/enterprise
/api-docs/ @getsentry/docs
/tests/apidocs/ @getsentry/docs
/.github/workflows/openapi.yml @getsentry/docs
/.github/workflows/openapi-diff.yml @getsentry/docs
/src/sentry/conf/api_pagination_allowlist_do_not_modify.py @getsentry/enterprise
/tests/sentry/api/test_path_params.py @getsentry/enterprise
## End of APIs


Expand Down Expand Up @@ -622,6 +622,10 @@ tests/sentry/api/endpoints/test_organization_attribute_mappings.py @get
/tests/sentry/seer/fetch_issues/ @getsentry/machine-learning-ai @getsentry/coding-workflows-sentry-backend
/src/sentry/tasks/seer/ @getsentry/machine-learning-ai
/tests/sentry/tasks/seer/ @getsentry/machine-learning-ai
/src/sentry/viewer_context.py @getsentry/machine-learning-ai
/tests/sentry/test_viewer_context.py @getsentry/machine-learning-ai
/src/sentry/middleware/viewer_context.py @getsentry/machine-learning-ai
/tests/sentry/middleware/test_viewer_context.py @getsentry/machine-learning-ai
## End of ML & AI

## Issues
Expand Down Expand Up @@ -897,8 +901,10 @@ tests/sentry/api/endpoints/test_organization_attribute_mappings.py @get
# End of Conduit

# Cell architecture
/src/sentry/synapse/
/tests/sentry/synapse/
/.agents/skills/cell-architecture @getsentry/sre-infrastructure-engineering
tests/sentry/core/endpoints/test_organization_cell.py @getsentry/sre-infrastructure-engineering
/tests/sentry/core/endpoints/test_organization_cell.py @getsentry/sre-infrastructure-engineering
# End of cell architecture

# Foundational Storage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
".github/CODEOWNERS": ["tests/sentry/api/test_api_owners.py"],
}

# Tests that should always be run even if not explicitly selected.
ALWAYS_RUN_TESTS: set[str] = {
"tests/sentry/taskworker/test_config.py",
}


def _matches_trigger(file_path: str, trigger: str | re.Pattern[str]) -> bool:
if isinstance(trigger, re.Pattern):
Expand Down Expand Up @@ -164,6 +169,9 @@ def main() -> int:
print(f"Including {len(existing_changed_test_files)} directly changed test files")
affected_test_files.update(existing_changed_test_files)

# Include tests that should always be run
affected_test_files.update(ALWAYS_RUN_TESTS)

# Filter out any test files found via coverage lookup that no longer exist
# (e.g. a deleted test file that covered the same source as another changed file).
existing_files = {f for f in affected_test_files if Path(f).exists()}
Expand Down
Loading
Loading