Repository: getsentry/sentry
Pull request: chore(projects): Increase project slug limit from 50 -> 100
Increase project slug limit from 50 -> 100. There are no FE changes needed b/c we don't validate length on frontend, but show the error received from the failed POST request.
<img width="679" alt="Screenshot 2024-08-07 at 10 59 23 AM" src="https://github.com/user-attachments/assets/72c21448-6e09-4474-94fb-882d4a370e0b">

Also switch to using a constant for default slug length.


Closes https://github.com/getsentry/sentry/issues/73032
Fixes [ALRT-195](https://getsentry.atlassian.net/browse/ALRT-195?atlOrigin=eyJpIjoiZDA1MzllNWQ5MDY5NDY5YWEwOGI1NTMyOWE1ZDc3NzciLCJwIjoiaiJ9)

[ALRT-195]: https://getsentry.atlassian.net/browse/ALRT-195?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

Problem statement:
Project slug length limit
### Problem Statement

Hey 👋 
Is there any opportunity to change max size of project's slug field?
This request is based on UX, 50 chars is not quite enough.
By project convention, used in our company, some project's names can be longer than 50 chars, so we face char limit in "slug" field.

Thanks

### Solution Brainstorm

_No response_

### Product Area

Settings - Projects

Hints from issue discussion:
Assigning to @getsentry/support for [routing](https://open.sentry.io/triage/#2-route) ⏲️
Routing to @getsentry/product-owners-settings-projects for [triage](https://develop.sentry.dev/processing-tickets/#3-triage) ⏲️
> By project convention, used in our company, some project's names can be longer than 50 chars, so we face char limit in "slug" field.

How long do project names get?
> How long do project names get?

It seems that a length of 100 characters will cover all of our needs

Hi
Any news?
@beloniki-flant I've scheduled it but there are competing priorities at the moment.

Commit message: chore(projects): Increase project slug limit from 50 -> 100

Patch to review:
diff --git a/migrations_lockfile.txt b/migrations_lockfile.txt
index 7bfad071c772e2..449f066d8a0ac0 100644
--- a/migrations_lockfile.txt
+++ b/migrations_lockfile.txt
@@ -10,6 +10,6 @@ hybridcloud: 0016_add_control_cacheversion
 nodestore: 0002_nodestore_no_dictfield
 remote_subscriptions: 0003_drop_remote_subscription
 replays: 0004_index_together
-sentry: 0747_create_datasecrecywaiver_table
+sentry: 0748_increase_project_slug_max_length
 social_auth: 0002_default_auto_field
 uptime: 0006_projectuptimesubscription_name_owner
diff --git a/src/sentry/api/endpoints/team_projects.py b/src/sentry/api/endpoints/team_projects.py
index f76b699fac6c9a..4e808614e626a3 100644
--- a/src/sentry/api/endpoints/team_projects.py
+++ b/src/sentry/api/endpoints/team_projects.py
@@ -22,7 +22,7 @@
 from sentry.apidocs.parameters import CursorQueryParam, GlobalParams
 from sentry.apidocs.utils import inline_sentry_response_serializer
 from sentry.constants import RESERVED_PROJECT_SLUGS, ObjectStatus
-from sentry.models.project import Project
+from sentry.models.project import PROJECT_SLUG_MAX_LENGTH, Project
 from sentry.seer.similarity.utils import SEER_ELIGIBLE_PLATFORMS
 from sentry.signals import project_created
 from sentry.utils.snowflake import MaxSnowflakeRetryError
@@ -37,7 +37,7 @@ class ProjectPostSerializer(serializers.Serializer):
     slug = SentrySerializerSlugField(
         help_text="""Uniquely identifies a project and is used for the interface.
         If not provided, it is automatically generated from the name.""",
-        max_length=50,
+        max_length=PROJECT_SLUG_MAX_LENGTH,
         required=False,
         allow_null=True,
     )
diff --git a/src/sentry/migrations/0748_increase_project_slug_max_length.py b/src/sentry/migrations/0748_increase_project_slug_max_length.py
new file mode 100644
index 00000000000000..6cb30e8db1f0a6
--- /dev/null
+++ b/src/sentry/migrations/0748_increase_project_slug_max_length.py
@@ -0,0 +1,34 @@
+# Generated by Django 5.0.7 on 2024-08-07 17:15
+
+from django.db import migrations
+
+import sentry.db.models.fields.slug
+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", "0747_create_datasecrecywaiver_table"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="project",
+            name="slug",
+            field=sentry.db.models.fields.slug.SentrySlugField(max_length=100, null=True),
+        ),
+    ]
diff --git a/src/sentry/models/project.py b/src/sentry/models/project.py
index bd47c746c2dfc3..76734a4a0905b6 100644
--- a/src/sentry/models/project.py
+++ b/src/sentry/models/project.py
@@ -53,6 +53,7 @@
     from sentry.models.user import User
 
 SENTRY_USE_SNOWFLAKE = getattr(settings, "SENTRY_USE_SNOWFLAKE", False)
+PROJECT_SLUG_MAX_LENGTH = 100
 
 # NOTE:
 # - When you modify this list, ensure that the platform IDs listed in "sentry/static/app/data/platforms.tsx" match.
@@ -228,7 +229,7 @@ class Project(Model, PendingDeletionMixin):
 
     __relocation_scope__ = RelocationScope.Organization
 
-    slug = SentrySlugField(null=True)
+    slug = SentrySlugField(null=True, max_length=PROJECT_SLUG_MAX_LENGTH)
     # DEPRECATED do not use, prefer slug
     name = models.CharField(max_length=200)
     forced_color = models.CharField(max_length=6, null=True, blank=True)


Code review comments:
diff_hunk: @@ -0,0 +1,34 @@
+# Generated by Django 5.0.7 on 2024-08-07 17:15
+
+from django.db import migrations
+
+import sentry.db.models.fields.slug
+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
path: src/sentry/migrations/0748_increase_project_slug_max_length.py
text: @user1:
should we make this a post deployment migration? not quite sure how many projects there are or the scale of this migration.

@user2:
No, post deployment is for backfills and large indexes only, not things that modify the schema.

Increasing the size of this column should be fast. If it's not, we have a statement timeout that will cause the migration to abort within a few seconds.

I'd say that just make sure you're around to monitor this and revert it if it won't run - it should be ok though.

@user3:
We might get hit with an index rebuild which could throw off query performance for slug lookups. The [postgres docs](https://www.postgresql.org/docs/current/sql-altertable.html#SQL-ALTERTABLE-DESC-SET-DATA-TYPE) state

> When this form is used, the column's statistics are removed, so running [ANALYZE](https://www.postgresql.org/docs/current/sql-analyze.html) on the table afterwards is recommended.

We have had [an incident](https://www.notion.so/sentry/INC-654-Database-migration-on-ApiTokenReplica-resulted-in-down-Sentry-7188774d88a848ffb05d7e092e0234f5) where a column resize invalided stats and caused an outage. We could have a similar outcome here.

@author:
How do you run ANALYZE, the [example in docs](https://www.notion.so/sentry/PostgreSQL-d2be3f78723d4b6fad12001f3522a5d2?pvs=4#701c3bab6cc74ee58309f773d361de1f) is a little unclear

@user2:
Good call @user3, I'd forgotten about that. Maybe in that case, we should make this a post_deploy migration and trigger the analyze as part of it, or even have ops run the entire thing manually for us.

I doubt we can run the ANALYZE as part of the deploy, it'll very likely time out.

@author:
I switched it to post deploy, do I have to include the ANALYZE as part of the migration code?