1
+ ###################
1
2
Database migrations
2
- ===================
3
+ ###################
3
4
4
5
Modifying database schemata will need database migrations (even for adding and
5
6
removing tables). To autogenerate migrations::
@@ -9,8 +10,9 @@ removing tables). To autogenerate migrations::
9
10
Verify your migration was generated by looking at the output from the command
10
11
above:
11
12
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
14
16
15
17
Then migrate and test your migration::
16
18
@@ -24,6 +26,11 @@ This makes it more difficult to make breaking changes, since you must phase
24
26
them in over time. See :ref: `destructive-migrations ` for tips on doing
25
27
migrations that involve column deletions or renames.
26
28
29
+ .. _migration-timeouts :
30
+
31
+ Migration Timeouts
32
+ ==================
33
+
27
34
To help protect against an accidentally long running migration from taking down
28
35
PyPI, by default a migration will timeout if it is waiting more than 4s to
29
36
acquire a lock, or if any individual statement takes more than 5s.
@@ -56,7 +63,7 @@ environment like PyPI, there is related reading available at:
56
63
.. _destructive-migrations :
57
64
58
65
Destructive migrations
59
- ----------------------
66
+ ======================
60
67
61
68
.. warning ::
62
69
@@ -104,3 +111,63 @@ a data migration. To rename a column:
104
111
105
112
In total, this requires three separate migrations: one to add the new column,
106
113
one 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