|
@@ -7,6 +7,25 @@ from django_zero_downtime_migrations.backends.postgres.schema import UnsafeOpera
|
|
|
from sentry.testutils.cases import TestCase
|
|
|
|
|
|
|
|
|
+def one_line_sql(sql: str) -> str:
|
|
|
+ return (
|
|
|
+ sql.replace(" ", "")
|
|
|
+ .replace("\n", " ")
|
|
|
+ .replace("( ", "(")
|
|
|
+ .replace(" )", ")")
|
|
|
+ .replace(" ", " ")
|
|
|
+ .strip()
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+def split_sql_queries(sql: str) -> list[str]:
|
|
|
+ return [
|
|
|
+ line
|
|
|
+ for line in [one_line_sql(line) for line in sql.splitlines()]
|
|
|
+ if line and not line.startswith("--")
|
|
|
+ ]
|
|
|
+
|
|
|
+
|
|
|
class BaseSafeMigrationTest(TestCase):
|
|
|
BASE_PATH = "fixtures.safe_migrations_apps"
|
|
|
# abstract
|
|
@@ -15,22 +34,28 @@ class BaseSafeMigrationTest(TestCase):
|
|
|
migrate_to: str
|
|
|
|
|
|
def run_migration(self):
|
|
|
+ self._run_migration(self.app, self.migrate_from)
|
|
|
+ self._run_migration(self.app, self.migrate_to)
|
|
|
+
|
|
|
+ def _run_migration(self, app, migration):
|
|
|
with override_settings(
|
|
|
INSTALLED_APPS=(f"{self.BASE_PATH}.{self.app}",), MIGRATION_MODULES={}
|
|
|
):
|
|
|
- migrate_from = [(self.app, self.migrate_from)]
|
|
|
- migrate_to = [(self.app, self.migrate_to)]
|
|
|
executor = MigrationExecutor(connection)
|
|
|
- executor.loader.project_state(migrate_from).apps
|
|
|
-
|
|
|
- # Reverse to the original migration
|
|
|
- executor.migrate(migrate_from)
|
|
|
+ executor.loader.build_graph()
|
|
|
+ target = [(app, migration)]
|
|
|
+ executor.loader.project_state(target).apps
|
|
|
+ executor.migrate(target)
|
|
|
|
|
|
- # Run the migration to test
|
|
|
+ def sql_migrate(self, app_label, migration_name):
|
|
|
+ with override_settings(
|
|
|
+ INSTALLED_APPS=(f"{self.BASE_PATH}.{self.app}",), MIGRATION_MODULES={}
|
|
|
+ ):
|
|
|
+ target = (app_label, migration_name)
|
|
|
executor = MigrationExecutor(connection)
|
|
|
- executor.loader.build_graph() # reload.
|
|
|
- executor.migrate(migrate_to)
|
|
|
- executor.loader.project_state(migrate_to).apps
|
|
|
+ plan = [(executor.loader.graph.nodes[target], None)]
|
|
|
+ sql_statements = executor.loader.collect_sql(plan) # type: ignore[attr-defined]
|
|
|
+ return "\n".join(sql_statements)
|
|
|
|
|
|
|
|
|
class AddColWithDefaultTest(BaseSafeMigrationTest):
|
|
@@ -155,3 +180,42 @@ class DeleteModelCorrectTest(BaseSafeMigrationTest):
|
|
|
|
|
|
def test(self):
|
|
|
self.run_migration()
|
|
|
+
|
|
|
+
|
|
|
+class LockTimeoutTest(BaseSafeMigrationTest):
|
|
|
+ app = "run_sql_app"
|
|
|
+
|
|
|
+ def test(self):
|
|
|
+ with override_settings(
|
|
|
+ ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT="5s",
|
|
|
+ ZERO_DOWNTIME_MIGRATIONS_STATEMENT_TIMEOUT="5s",
|
|
|
+ ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT_FORCE=True,
|
|
|
+ ):
|
|
|
+ migration_sql = self.sql_migrate(self.app, "0001_initial")
|
|
|
+ # We'd never block while attempting to acquire a lock when creating a table, but since we set
|
|
|
+ # `ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT_FORCE` we should add a lock_timeout here anyway
|
|
|
+ assert split_sql_queries(migration_sql) == [
|
|
|
+ "SET lock_timeout TO '5s';",
|
|
|
+ 'CREATE TABLE "run_sql_app_testtable" ("id" integer NOT NULL PRIMARY KEY '
|
|
|
+ 'GENERATED BY DEFAULT AS IDENTITY, "field" integer NULL);',
|
|
|
+ "SET lock_timeout TO '0ms';",
|
|
|
+ ]
|
|
|
+ self._run_migration(self.app, "0001_initial")
|
|
|
+ migration_sql = self.sql_migrate(self.app, "0002_run_sql")
|
|
|
+ # The runsql operation should just have the lock timeout set, since it's relying on
|
|
|
+ # `ZERO_DOWNTIME_MIGRATIONS_LOCK_TIMEOUT_FORCE`
|
|
|
+ assert split_sql_queries(migration_sql) == [
|
|
|
+ "SET lock_timeout TO '5s';",
|
|
|
+ 'ALTER TABLE "run_sql_app_testtable" DROP COLUMN "field";',
|
|
|
+ "SET lock_timeout TO '0ms';",
|
|
|
+ ]
|
|
|
+ self._run_migration(self.app, "0002_run_sql")
|
|
|
+ migration_sql = self.sql_migrate(self.app, "0003_add_col")
|
|
|
+ # This should set the statement timeout since it's an operation that dzdm handles
|
|
|
+ assert split_sql_queries(migration_sql) == [
|
|
|
+ "SET statement_timeout TO '5s';",
|
|
|
+ "SET lock_timeout TO '5s';",
|
|
|
+ 'ALTER TABLE "run_sql_app_testtable" ADD COLUMN "field" integer NULL;',
|
|
|
+ "SET statement_timeout TO '0ms';",
|
|
|
+ "SET lock_timeout TO '0ms';",
|
|
|
+ ]
|