1+ ###################
12Database migrations
2- ===================
3+ ###################
34
45Modifying database schemata will need database migrations (even for adding and
56removing tables). To autogenerate migrations::
@@ -9,8 +10,9 @@ removing tables). To autogenerate migrations::
910Verify your migration was generated by looking at the output from the command
1011above:
1112
12- ``Generating /opt/warehouse/src/warehouse/migrations/versions/390811c1c\
13- dbe_.py ... done ``
13+ .. code-block :: console
14+
15+ Generating /opt/warehouse/src/warehouse/migrations/versions/390811c1cdbe_.py ... done
1416
1517 Then migrate and test your migration::
1618
@@ -24,6 +26,11 @@ This makes it more difficult to make breaking changes, since you must phase
2426them in over time. See :ref: `destructive-migrations ` for tips on doing
2527migrations that involve column deletions or renames.
2628
29+ .. _migration-timeouts :
30+
31+ Migration Timeouts
32+ ==================
33+
2734To help protect against an accidentally long running migration from taking down
2835PyPI, by default a migration will timeout if it is waiting more than 4s to
2936acquire a lock, or if any individual statement takes more than 5s.
@@ -56,7 +63,7 @@ environment like PyPI, there is related reading available at:
5663.. _destructive-migrations :
5764
5865Destructive migrations
59- ----------------------
66+ ======================
6067
6168.. warning ::
6269
@@ -104,3 +111,63 @@ a data migration. To rename a column:
104111
105112In total, this requires three separate migrations: one to add the new column,
106113one to backfill to it, and a third to remove the old column.
114+
115+ Creating Indexes
116+ ================
117+
118+ Indexes should be declared as part of SQLAlchemy Table definitions,
119+ often under the ``__table_args__ `` attribute.
120+ Once declared, auto-generating a migration will create the index for alembic.
121+
122+ See more in index definition in
123+ `SQLAlchemy documentation <https://docs.sqlalchemy.org/en/20/core/constraints.html#schema-indexes >`_.
124+
125+ Since index creation will often take longer than a few seconds
126+ for tables that are large or active,
127+ it is recommended to create indexes concurrently.
128+
129+ To create an index concurrently, adding ``postgresql_concurrently=True ``
130+ to the index definition is incompatible with our deployment migration process
131+ as it runs in a transaction, and concurrent index creation requires a separate transaction.
132+
133+ Instead, manually update the migration to start a separate transaction.
134+
135+ After auto-generating the new migration, update the migration to create the index concurrently.
136+ Here's an example of an migration for a definition of ``Index("tbl1_column1_idx", "column1") ``
137+ that was auto-generated, and manually updated to create the index concurrently:
138+
139+ .. code-block :: diff
140+
141+ def upgrade():
142+ - op.create_index(
143+ - "tbl1_column1_idx",
144+ - "tbl1",
145+ - ["column1"],
146+ - unique=False,
147+ - )
148+ + # CREATE INDEX CONCURRENTLY cannot happen inside a transaction. We'll close
149+ + # our transaction here and issue the statement.
150+ + op.get_bind().commit()
151+ + with op.get_context().autocommit_block():
152+ + op.create_index(
153+ + "tbl1_column1_idx",
154+ + "tbl1",
155+ + ["column1"],
156+ + unique=False,
157+ + if_not_exists=True
158+ + postgresql_concurrently=True,
159+ + )
160+
161+ The original ``op.create_index() `` call is indented under a context manager,
162+ and the keyword args ``if_not_exists=True `` and ``postgresql_concurrently=True ``
163+ are added to the call.
164+
165+ Leave the generated ``downgrade() `` function as normal.
166+
167+ If the index creation is likely to continue to take longer than a few seconds,
168+ and most indexes on existing tables in use are likely to take longer than a few seconds,
169+ it is recommended to modify the migration to increase the statement timeout
170+ as described in :ref: `migration-timeouts `.
171+
172+ Another option is to share the SQL statement to create the index concurrently
173+ on the Pull Request, and have a maintainer run the statement manually.
0 commit comments