You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -145,144 +145,276 @@ This is complicated due to our deploy process. When we deploy, we run migrations
145
145
146
146
To avoid this, follow these steps:
147
147
148
-
- Mark the column as nullable if it isn't, and create a migration. (ex. `BoundedIntegerField(null=True)`)
149
-
- Deploy.
150
-
- Remove the column from the model, but in the migration make sure we only mark the state as removed.
151
-
- Deploy.
152
-
- Finally, create a migration that deletes the column.
148
+
- Make a PR to remove all uses of the column in the codebase in a separate PR. This mostly helps with code cleanliness. This should be merged ahead of the migration prs, but we don't need to worry about whether it is deployed first.
149
+
- Make another PR that:
150
+
- Checks if the column is either not nullable, or doesn't have a `db_default` set. If either of these is true, then make it nullable via `null=True`.
151
+
- If the column is a foreign key, remove the database level foreign key constraint it by setting `db_constraint=False`.
152
+
- Remove the column and in the generated migration use `SafeRemoveField(..., deletion_action=DeletionAction.MOVE_TO_PENDING)` to replace `RemoveField(...)`. This only marks the state for the column as removed.
153
+
- Combine these migrations together to save making multiple deploys
154
+
- Deploy your migration changes. It's important that all previous pull requests are in production before we remove the actual column from the table.
155
+
- Make a pull request that create a new migration that has the same `SafeRemoveField` operation as before, but set `deletion_action=DeletionAction.DELETE` instead. This deletes the actual column from the table in Postgres.
156
+
- Deploy the drop column migration.
157
+
158
+
Here's an example of removing the `project` column from this model. It is both a foreign key and not null:
153
159
154
-
Here's an example of removing columns that were already nullable. First we remove the columns from the model, and then modify the migration to only update the state and make no database operations.
160
+
```python
161
+
@region_silo_model
162
+
classTestModel(Model):
163
+
__relocation_scope__ = RelocationScope.Excluded
164
+
165
+
project = FlexibleForeignKey("sentry.Project")
166
+
name = models.TextField()
167
+
168
+
classMeta:
169
+
app_label ="uptime"
170
+
db_table ="uptime_testmodel"
171
+
```
172
+
173
+
First, we remove all references to this field from the codebase. This is best done as a separate pr to keep things clean.
174
+
175
+
Next we produce two migrations, in individual prs that we will deploy separately.
Once this is deployed, we can then deploy the actual column deletion. This pr will have only a migration, since Django no longer knows about these fields. Note that the reverse SQL is only for dev, so it's fine to not assign a default or do any sort of backfill:
209
+
So once we have these two prs, we merge/deploy the first, and then the second and then the table is fully removed.
210
+
211
+
So to recap the steps here:
212
+
- Remove all references to the column in the code in a separate pull request and merge. Doesn't matter if this deploys before the next step or not.
213
+
- If the column has a foreign key constraint them remove it. If it's not null and has no `db_default` then mark it as nullable. Then delete the column using `SafeRemoveField(..., deletion_action=DeletionAction.MOVE_TO_PENDING)`. These operations can be in the same migration to save time.
214
+
- Deploy all previous before continuing.
215
+
- Remove the column from the table in from Postgres using `SafeRemoveField(..., deletion_action=DeletionAction.DELETE),`
216
+
217
+
If you're comfortable producing these prs and deploying them, then stop here. Otherwise, this next section covers how to produce them in more detail.
218
+
219
+
To produce the first migration, we need to remove the db level foreign key constraint, make the column nullable and remove the column from the codebase. To remove the db level foreign key constraints and mark the column nullable we add `db_constraint=False, null=True` to this column and generate a migration:
Django doesn't know about the `SafeRemoveField` operation, so we replace it with that instead. This allows us to remove all state related to the column, but defer deleting it until a later migration. So this becomes
To produce the second migration we generate an empty migration (`sentry django makemigrations <your_app> --empty`), then use the same `SafeRemoveField` command from the previous migration, but change the deletion_action to `DeletionAction.DELETE`. This operation will remove the column from the table in Postgres:
Extra care is needed here if the table is referenced as a foreign key in other tables. In that case, first remove the foreign key columns in the other tables, then come back to this step.
197
287
198
-
- Remove any database level foreign key constraints from this table to other tables by setting `db_constraint=False` on the columns.
199
-
- Deploy
200
-
- Remove the model and all references from the sentry codebase. Make sure that the migration only marks the state as removed.
201
-
- Deploy.
202
-
- Create a migrations that deletes the table.
288
+
- Make a pull request to remove all uses of the model in the codebase in a separate pull request. This mostly helps with code cleanliness. This should be merged ahead of the migration pull requests, but we don't need to worry about whether it is deployed first.
289
+
- Make another pull request to:
290
+
- Remove any database level foreign key constraints from this table to other tables by setting `db_constraint=False` on the columns.
291
+
- Remove the model and in the generated migration use `SafeDeleteModel(..., deletion_action=DeletionAction.MOVE_TO_PENDING)` to replace `DeleteModel(...)`. This only marks the state for the model as removed.
292
+
- Deploy. It's important that all previous pull requests are in production before we remove the actual table.
293
+
- Make a pull request that creates a new migration that has the same `SafeDeleteModel` operation as before, but set `deletion_action=DeletionAction.DELETE` instead. This deletes the actual table from Postgres.
First we checked that it's not referenced by any other models, and it's not. Next we need to remove and db level foreign key constraints. To do this, we change these two columns and generate a migration:
311
+
First, we remove all references to this model from the codebase, including making sure that it's not referenced by any other models. This is best done as a separate pr to keep things clean.
312
+
313
+
Next we produce two migrations, in individual prs that we will deploy separately.
So once we have these two prs, we merge/deploy the first, and then the second and then the table is fully removed.
347
+
348
+
So to recap the steps here:
349
+
- Remove all references to the model in the code in a separate pull request and merge. Doesn't matter if this deploys before the next step or not.
350
+
- Remove any foreign key constraints and delete the model using `SafeDeleteModel(..., deletion_action=DeletionAction.MOVE_TO_PENDING)`. These operations can be in the same migration to save time.
351
+
- Deploy all previous before continuing.
352
+
- Remove the table from Postgres using `SafeDeleteModel(..., deletion_action=DeletionAction.DELETE),`
353
+
354
+
If you're comfortable producing these prs and deploying them, then stop here. Otherwise, this next section covers how to produce them in more detail.
355
+
356
+
To produce the first migration, we need to remove any db level foreign key constraints and remove the table from the codebase. To remove the db level foreign key constraints we add `db_constraint=False` to this column and generate a migration:
So now we deploy this and move onto the next stage.
258
-
259
-
The next stage involves removing all references to the model from the codebase. So we do that, and then we generate a migration that removes the model from the migration state, but not the database. The operations in this migration look like
385
+
Django doesn't know about the `SafeDeleteModel` operation, so we replace it with that instead. This allows us to remove all state related to the model, but defer deleting it until a later migration. So this becomes
In this last step, we just want to manually write DDL to remove the table. So we use `sentry django makemigrations --empty` to produce an empty migration, and then modify the operations to be like:
409
+
To produce the second migration we generate an empty migration (`sentry django makemigrations <your_app> --empty`), then use the same `SafeDeleteModel` command from the previous migration, but change the deletion_action to `DeletionAction.DELETE`. This operation will remove the table from Postgres:
273
410
274
411
```python
275
412
operations = [
276
-
migrations.RunSQL(
277
-
"""
278
-
DROP TABLE "sentry_alertruletriggeraction";
279
-
""",
280
-
reverse_sql="CREATE TABLE sentry_alertruletriggeraction (fake_col int)", # We just create a fake table here so that the DROP will work if we roll back the migration.
0 commit comments