@@ -108,21 +108,12 @@ defmodule Ecto.Migrator do
108
108
end
109
109
110
110
defp do_up ( repo , version , module , opts ) do
111
- run_maybe_in_transaction ( repo , module , fn ->
111
+ async_migrate_maybe_in_transaction ( repo , version , module , :up , opts , fn ->
112
112
attempt ( repo , version , module , :forward , :up , :up , opts )
113
113
|| attempt ( repo , version , module , :forward , :change , :up , opts )
114
114
|| { :error , Ecto.MigrationError . exception (
115
115
"#{ inspect module } does not implement a `up/0` or `change/0` function" ) }
116
116
end )
117
- |> case do
118
- :ok ->
119
- verbose_schema_migration repo , "update schema migrations" , fn ->
120
- SchemaMigration . up ( repo , version , opts [ :prefix ] )
121
- end
122
- :ok
123
- error ->
124
- error
125
- end
126
117
end
127
118
128
119
@ doc """
@@ -153,41 +144,65 @@ defmodule Ecto.Migrator do
153
144
end
154
145
155
146
defp do_down ( repo , version , module , opts ) do
156
- run_maybe_in_transaction ( repo , module , fn ->
147
+ async_migrate_maybe_in_transaction ( repo , version , module , :down , opts , fn ->
157
148
attempt ( repo , version , module , :forward , :down , :down , opts )
158
149
|| attempt ( repo , version , module , :backward , :change , :down , opts )
159
150
|| { :error , Ecto.MigrationError . exception (
160
151
"#{ inspect module } does not implement a `down/0` or `change/0` function" ) }
161
152
end )
162
- |> case do
163
- :ok ->
153
+ end
154
+
155
+ defp async_migrate_maybe_in_transaction ( repo , version , module , direction , opts , fun ) do
156
+ parent = self ( )
157
+ ref = make_ref ( )
158
+ task = Task . async ( fn -> run_maybe_in_transaction ( parent , ref , repo , module , fun ) end )
159
+
160
+ if migrated_successfully? ( ref , task . pid ) do
161
+ try do
162
+ # The table with schema migrations can only be updated from
163
+ # the parent process because it has a lock on the table
164
164
verbose_schema_migration repo , "update schema migrations" , fn ->
165
- SchemaMigration . down ( repo , version , opts [ :prefix ] )
165
+ apply ( SchemaMigration , direction , [ repo , version , opts [ :prefix ] ] )
166
166
end
167
- :ok
168
- error ->
169
- error
167
+ catch
168
+ kind , error ->
169
+ Task . shutdown ( task , :brutal_kill )
170
+ :erlang . raise ( kind , error , System . stacktrace ( ) )
171
+ end
170
172
end
173
+
174
+ send ( task . pid , ref )
175
+ Task . await ( task , :infinity )
171
176
end
172
177
173
- defp run_maybe_in_transaction ( repo , module , fun ) do
174
- fn -> do_run_maybe_in_transaction ( repo , module , fun ) end
175
- |> Task . async ( )
176
- |> Task . await ( :infinity )
178
+ defp migrated_successfully? ( ref , pid ) do
179
+ receive do
180
+ { ^ ref , :ok } -> true
181
+ { ^ ref , _ } -> false
182
+ { :EXIT , ^ pid , _ } -> false
183
+ end
177
184
end
178
185
179
- defp do_run_maybe_in_transaction ( repo , module , fun ) do
180
- cond do
181
- module . __migration__ [ :disable_ddl_transaction ] ->
182
- fun . ( )
183
- repo . __adapter__ . supports_ddl_transaction? ->
184
- { :ok , result } = repo . transaction ( fun , log: false , timeout: :infinity )
185
- result
186
- true ->
187
- fun . ( )
186
+ defp run_maybe_in_transaction ( parent , ref , repo , module , fun ) do
187
+ if module . __migration__ [ :disable_ddl_transaction ] ||
188
+ not repo . __adapter__ . supports_ddl_transaction? do
189
+ send_and_receive ( parent , ref , fun . ( ) )
190
+ else
191
+ { :ok , result } =
192
+ repo . transaction (
193
+ fn -> send_and_receive ( parent , ref , fun . ( ) ) end ,
194
+ log: false , timeout: :infinity
195
+ )
196
+
197
+ result
188
198
end
189
199
catch kind , reason ->
190
- { kind , reason , System . stacktrace }
200
+ send_and_receive ( parent , ref , { kind , reason , System . stacktrace } )
201
+ end
202
+
203
+ defp send_and_receive ( parent , ref , value ) do
204
+ send parent , { ref , value }
205
+ receive do: ( ^ ref -> value )
191
206
end
192
207
193
208
defp attempt ( repo , version , module , direction , operation , reference , opts ) do
0 commit comments