Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion migrations_lockfile.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ replays: 0007_organizationmember_replay_access

seer: 0002_add_default_coding_agent

sentry: 1048_organizationmapping_cell_name_idx
sentry: 1049_rename_slugreservation_region_name_to_cell_name

social_auth: 0003_social_auth_json_field

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ def provision_organization(
slug=slug,
organization_id=org_id,
user_id=org_provision_args.provision_options.owning_user_id,
region_name=region_name,
cell_name=region_name,
)

org_slug_res.save(unsafe_write=True)
Expand Down Expand Up @@ -221,7 +221,7 @@ def update_organization_slug(
slug=slug_base,
organization_id=organization_id,
user_id=-1,
region_name=region_name,
cell_name=region_name,
reservation_type=OrganizationSlugReservationType.TEMPORARY_RENAME_ALIAS.value,
).save(unsafe_write=True)

Expand Down Expand Up @@ -302,7 +302,7 @@ def bulk_create_organization_slug_reservations(
organization_id=org_id,
reservation_type=OrganizationSlugReservationType.TEMPORARY_RENAME_ALIAS.value,
user_id=-1,
region_name=region_name,
cell_name=region_name,
)
slug_reservation.save(unsafe_write=True)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def serialize_slug_reservation(
id=slug_reservation.id,
organization_id=slug_reservation.organization_id,
slug=slug_reservation.slug,
region_name=slug_reservation.region_name,
region_name=slug_reservation.cell_name,
user_id=slug_reservation.user_id,
reservation_type=slug_reservation.reservation_type,
)
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def _check_organization_mapping_integrity(
)
return False

org_slug_regions_set = {org_slug.region_name for org_slug in org_slugs}
org_slug_regions_set = {org_slug.cell_name for org_slug in org_slugs}
if update.region_name not in org_slug_regions_set:
capture_exception(
OrganizationMappingConsistencyException(
Expand Down Expand Up @@ -112,7 +112,7 @@ def _upsert_organization_slug_reservation_for_monolith(
).first()
if org_slug_reservation is None:
OrganizationSlugReservation(
region_name=mapping_update.region_name,
cell_name=mapping_update.region_name,
slug=mapping_update.slug,
organization_id=organization_id,
user_id=-1,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Generated by Django 5.2.11 on 2026-03-06

from django.db import migrations, models

from sentry.new_migrations.migrations import CheckedMigration


class Migration(CheckedMigration):
# This flag is used to mark that a migration shouldn't be automatically run in production.
# This should only be used for operations where it's safe to run the migration after your
# code has deployed. So this should not be used for most operations that alter the schema
# of a table.
# Here are some things that make sense to mark as post deployment:
# - Large data migrations. Typically we want these to be run manually so that they can be
# monitored and not block the deploy for a long period of time while they run.
# - Adding indexes to large tables. Since this can take a long time, we'd generally prefer to
# run this outside deployments so that we don't block them. Note that while adding an index
# is a schema change, it's completely safe to run the operation after the code has deployed.
# Once deployed, run these manually via: https://develop.sentry.dev/database-migrations/#migration-deployment

is_post_deployment = False

dependencies = [
("sentry", "1048_organizationmapping_cell_name_idx"),
]

operations = [
migrations.AlterField(
model_name="organizationslugreservation",
name="region_name",
field=models.CharField(db_column="region_name", max_length=48, null=False),
),
migrations.RenameField(
model_name="organizationslugreservation",
old_name="region_name",
new_name="cell_name",
),
]
7 changes: 3 additions & 4 deletions src/sentry/models/organizationslugreservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ class OrganizationSlugReservation(ReplicatedControlModel):
slug = models.SlugField(unique=True, null=False)
organization_id = HybridCloudForeignKey("sentry.organization", null=False, on_delete="CASCADE")
user_id = BoundedBigIntegerField(db_index=True, null=True)
# TODO(cells): rename to cell_name
region_name = models.CharField(max_length=REGION_NAME_LENGTH, null=False)
cell_name = models.CharField(max_length=REGION_NAME_LENGTH, null=False, db_column="region_name")
reservation_type = BoundedBigIntegerField(
choices=OrganizationSlugReservationType.as_choices(),
null=False,
Comment on lines 35 to 41
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: An OrganizationSlugReservation instantiation uses the old region_name field, which has been renamed to cell_name, and will cause a TypeError.
Severity: HIGH

Suggested Fix

In src/sentry/services/organization/provisioning.py, update the OrganizationSlugReservation instantiation inside the handle_possible_organization_slug_swap function to use the new field name. Change region_name=region_name to cell_name=region_name.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: src/sentry/models/organizationslugreservation.py#L35-L41

Potential issue: The `OrganizationSlugReservation` model's `region_name` field was
renamed to `cell_name`. However, an instantiation of this model within the `else` block
of the `handle_possible_organization_slug_swap` function was not updated to reflect this
change. It still attempts to pass a `region_name` keyword argument. When this code path
is executed—specifically, when an organization is missing a primary slug reservation
during a slug swap—it will raise a `TypeError` because `region_name` is not a recognized
field on the model, causing the operation to fail.

Expand Down Expand Up @@ -68,7 +67,7 @@ def update(self, *args: Any, **kwds: Any):
return super().update(*args, **kwds)

def outbox_region_names(self) -> Collection[str]:
return [self.region_name]
return [self.cell_name]

def handle_async_replication(self, region_name: str, shard_identifier: int) -> None:
from sentry.hybridcloud.services.control_organization_provisioning.serial import (
Expand All @@ -78,7 +77,7 @@ def handle_async_replication(self, region_name: str, shard_identifier: int) -> N

serialized = serialize_slug_reservation(self)
region_replica_service.upsert_replicated_org_slug_reservation(
slug_reservation=serialized, region_name=self.region_name
slug_reservation=serialized, region_name=self.cell_name
)

@classmethod
Expand Down
2 changes: 1 addition & 1 deletion src/sentry/services/organization/provisioning.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ def handle_possible_organization_slug_swap(*, region_name: str, org_slug_reserva
organization_id=org_slug_reservation.organization_id,
reservation_type=OrganizationSlugReservationType.PRIMARY,
user_id=org_slug_reservation.user_id,
region_name=region_name,
cell_name=region_name,
).save(unsafe_write=True)


Expand Down
2 changes: 1 addition & 1 deletion src/sentry/testutils/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ def create_organization(name=None, owner=None, region: Cell | str | None = None,
# Organization mapping creation relies on having a matching org slug reservation
OrganizationSlugReservation(
organization_id=org.id,
region_name=region_name,
cell_name=region_name,
user_id=owner.id if owner else -1,
slug=org.slug,
).save(unsafe_write=True)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ def create_temporary_slug_res(self, organization: Organization, slug: str, regio
reservation_type=OrganizationSlugReservationType.TEMPORARY_RENAME_ALIAS,
slug=slug,
organization_id=organization.id,
region_name=region,
cell_name=region,
user_id=-1,
).save(unsafe_write=True)

Expand Down
2 changes: 1 addition & 1 deletion tests/sentry/hybridcloud/test_organizationmapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ def test_upsert__update_when_slug_matches_temporary_alias(self) -> None:
slug=temporary_slug,
organization_id=self.organization.id,
reservation_type=OrganizationSlugReservationType.TEMPORARY_RENAME_ALIAS,
region_name=primary_slug_res.region_name,
cell_name=primary_slug_res.cell_name,
user_id=user.id,
).save(unsafe_write=True)

Expand Down
22 changes: 11 additions & 11 deletions tests/sentry/models/test_organizationslugreservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def does_replica_match_original_reservation(
slug_replica: OrganizationSlugReservationReplica,
):
matches = slug_replica.organization_id == slug_reservation.organization_id
matches = matches and slug_replica.region_name == slug_reservation.region_name
matches = matches and slug_replica.region_name == slug_reservation.cell_name
matches = matches and slug_replica.reservation_type == slug_reservation.reservation_type

return matches
Expand Down Expand Up @@ -92,7 +92,7 @@ def test_standard_replica(self) -> None:
slug="santry",
user_id=self.user.id,
organization_id=42,
region_name="us",
cell_name="us",
reservation_type=OrganizationSlugReservationType.PRIMARY,
)

Expand All @@ -101,7 +101,7 @@ def test_replica_deletion(self) -> None:
slug="santry",
user_id=self.user.id,
organization_id=42,
region_name="us",
cell_name="us",
reservation_type=OrganizationSlugReservationType.PRIMARY,
)

Expand All @@ -117,7 +117,7 @@ def test_replica_deletion_with_pending_changes(self) -> None:
slug="santry",
user_id=self.user.id,
organization_id=42,
region_name="us",
cell_name="us",
reservation_type=OrganizationSlugReservationType.PRIMARY,
)

Expand All @@ -137,7 +137,7 @@ def test_replica_update(self) -> None:
slug="santry",
user_id=self.user.id,
organization_id=42,
region_name="us",
cell_name="us",
reservation_type=OrganizationSlugReservationType.PRIMARY,
)

Expand All @@ -156,7 +156,7 @@ def test_slug_update_only(self) -> None:
slug="santry",
user_id=self.user.id,
organization_id=42,
region_name="us",
cell_name="us",
reservation_type=OrganizationSlugReservationType.PRIMARY,
)

Expand All @@ -173,15 +173,15 @@ def test_delete_and_slug_change(self) -> None:
slug="santry",
user_id=self.user.id,
organization_id=42,
region_name="us",
cell_name="us",
reservation_type=OrganizationSlugReservationType.PRIMARY,
)

org_slug_res_b = self.create_org_slug_reservation(
slug="acme",
user_id=self.user.id,
organization_id=43,
region_name="us",
cell_name="us",
reservation_type=OrganizationSlugReservationType.PRIMARY,
)

Expand All @@ -199,23 +199,23 @@ def test_multi_rename_collision(self) -> None:
slug="santry",
user_id=self.user.id,
organization_id=42,
region_name="us",
cell_name="us",
reservation_type=OrganizationSlugReservationType.PRIMARY,
)

org_slug_res_b = self.create_org_slug_reservation(
slug="acme",
user_id=self.user.id,
organization_id=43,
region_name="us",
cell_name="us",
reservation_type=OrganizationSlugReservationType.PRIMARY,
)

org_slug_res_c = self.create_org_slug_reservation(
slug="foobar",
user_id=self.user.id,
organization_id=44,
region_name="us",
cell_name="us",
reservation_type=OrganizationSlugReservationType.PRIMARY,
)

Expand Down
Loading