Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Avoid a long table lock for the first_name migration #4816

Closed
wants to merge 1 commit into from
Closed
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
53 changes: 53 additions & 0 deletions api/tests/unit/users/test_migrations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import pytest
from django.conf import settings
from django_test_migrations.migrator import Migrator

pytestmark = pytest.mark.skipif(
settings.SKIP_MIGRATION_TESTS is True,
reason="Skip migration tests to speed up tests where necessary",
)


def test_0039_ffadminuser_first_name_v2__values_expected(migrator: Migrator) -> None:
# Given
old_state = migrator.apply_initial_migration(
("users", "0038_create_hubspot_tracker")
)
OldFFAdminUser = old_state.apps.get_model("users", "FFAdminUser")

user = OldFFAdminUser.objects.create(first_name="Testfirstname")

# When
new_state = migrator.apply_tested_migration(
("users", "0039_ffadminuser_first_name_v2")
)
NewFFAdminUser = new_state.apps.get_model("users", "FFAdminUser")

# Then
assert NewFFAdminUser.objects.get(id=user.id).first_name == user.first_name


def test_0039_ffadminuser_first_name_v2__reverse__values_expected(
migrator: Migrator,
) -> None:
# Given
old_state = migrator.apply_initial_migration(
("users", "0039_ffadminuser_first_name_v2")
)
NewFFAdminUser = old_state.apps.get_model("users", "FFAdminUser")

user = NewFFAdminUser.objects.create(
first_name="TestfirstnameTestfirstnameTestfirstnameTestfirstname"
)

# When
new_state = migrator.apply_tested_migration(
("users", "0038_create_hubspot_tracker")
)
OldFFAdminUser = new_state.apps.get_model("users", "FFAdminUser")

# Then
assert (
OldFFAdminUser.objects.get(id=user.id).first_name
== "TestfirstnameTestfirstnameTest"
)
18 changes: 0 additions & 18 deletions api/users/migrations/0039_alter_ffadminuser_first_name.py

This file was deleted.

55 changes: 55 additions & 0 deletions api/users/migrations/0039_ffadminuser_first_name_v2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Generated by Django 4.2.16 on 2024-11-05 10:49

from pathlib import Path
from django.db import migrations, models


def _get_ffadminuser_table_name() -> str:
from users.models import FFAdminUser

return FFAdminUser._meta.db_table


_ffadminuser_table_name = _get_ffadminuser_table_name()


parent_dir = Path(__file__).parent.resolve()

with open(parent_dir / "sql/0039_ffadminuser_first_name_v2.sql") as f:
sql_forwards = f.read().format(ffadminuser_table_name=_ffadminuser_table_name)

with open(parent_dir / "sql/0039_ffadminuser_first_name_v2_reverse.sql") as f:
sql_reverse = f.read().format(ffadminuser_table_name=_ffadminuser_table_name)


class Migration(migrations.Migration):

dependencies = [
("users", "0038_create_hubspot_tracker"),
]

operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.RemoveField(
model_name="ffadminuser",
name="first_name",
),
migrations.AddField(
model_name="ffadminuser",
name="first_name",
field=models.CharField(
db_column="first_name_v2",
max_length=150,
verbose_name="first name",
),
),
],
database_operations=[
migrations.RunSQL(
sql=sql_forwards,
reverse_sql=sql_reverse,
)
],
)
]
8 changes: 8 additions & 0 deletions api/users/migrations/sql/0039_ffadminuser_first_name_v2.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ALTER TABLE {ffadminuser_table_name}
ADD COLUMN first_name_v2 varchar(150) NULL;
UPDATE {ffadminuser_table_name}
SET first_name_v2 = {ffadminuser_table_name}.first_name;
ALTER TABLE {ffadminuser_table_name}
ALTER COLUMN first_name_v2 SET NOT NULL;
ALTER TABLE {ffadminuser_table_name}
DROP COLUMN first_name;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ALTER TABLE {ffadminuser_table_name}
ADD COLUMN first_name varchar(30) NULL;
UPDATE {ffadminuser_table_name}
SET first_name = left({ffadminuser_table_name}.first_name_v2, 30);
ALTER TABLE {ffadminuser_table_name}
ALTER COLUMN first_name SET NOT NULL;
ALTER TABLE {ffadminuser_table_name}
DROP COLUMN first_name_v2;
6 changes: 5 additions & 1 deletion api/users/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,11 @@ class FFAdminUser(LifecycleModel, AbstractUser):
email = models.EmailField(unique=True, null=False)
objects = UserManager()
username = models.CharField(unique=True, max_length=150, null=True, blank=True)
first_name = models.CharField("first name", max_length=150)
first_name = models.CharField(
"first name",
max_length=150,
db_column="first_name_v2",
)
last_name = models.CharField("last name", max_length=150)
google_user_id = models.CharField(max_length=50, null=True, blank=True)
github_user_id = models.CharField(max_length=50, null=True, blank=True)
Expand Down
Loading