添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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
  • Elixir version (elixir -v): 1.6.1
  • Database and version (PostgreSQL 9.4, MongoDB 3.2, etc.): Postgres 10.1
  • Ecto version (mix deps): 2.2.8
  • Database adapter and version (mix deps): Postgrex 0.13.4
  • Operating system: MacOS High Sierra
  • Current behavior

    I'm trying to add indexes conditionally to a very large table and have created the following migration:

    defmodule PixelMetrics.Repo.Migrations.AddIndexesToForeignKeysOnActionsTable do
      use Ecto.Migration
      @disable_ddl_transaction true
      def change do
        create_if_not_exists(index("actions", [:app_id], concurrently: true))
    

    Running mix ecto.migrate throws because the migration is being run in a transaction, though @disable_ddl_transactionis set totrue`. Here's that stack trace:

    [info] == Running PixelMetrics.Repo.Migrations.AddIndexesToForeignKeysOnActionsTable.change/0 forward
    [info] create index if not exists actions_store_id_index
    ** (Postgrex.Error) ERROR 25001 (active_sql_transaction): CREATE INDEX CONCURRENTLY cannot run inside a transaction block
        (ecto) lib/ecto/adapters/sql.ex:200: Ecto.Adapters.SQL.query!/5
        (ecto) lib/ecto/adapters/postgres.ex:85: anonymous fn/4 in Ecto.Adapters.Postgres.execute_ddl/3
        (elixir) lib/enum.ex:1899: Enum."-reduce/3-lists^foldl/2-0-"/3
        (ecto) lib/ecto/adapters/postgres.ex:85: Ecto.Adapters.Postgres.execute_ddl/3
        (ecto) lib/ecto/migration/runner.ex:104: anonymous fn/2 in Ecto.Migration.Runner.flush/0
        (elixir) lib/enum.ex:1899: Enum."-reduce/3-lists^foldl/2-0-"/3
        (ecto) lib/ecto/migration/runner.ex:102: Ecto.Migration.Runner.flush/0
        (stdlib) timer.erl:181: :timer.tc/2
        (ecto) lib/ecto/migration/runner.ex:26: Ecto.Migration.Runner.run/6
        (ecto) lib/ecto/migrator.ex:128: Ecto.Migrator.attempt/6
        (ecto) lib/ecto/migrator.ex:73: anonymous fn/4 in Ecto.Migrator.do_up/4
        (ecto) lib/ecto/migrator.ex:261: anonymous fn/4 in Ecto.Migrator.migrate/4
        (elixir) lib/enum.ex:1294: Enum."-map/2-lists^map/1-0-"/2
        (ecto) lib/mix/tasks/ecto.migrate.ex:83: anonymous fn/4 in Mix.Tasks.Ecto.Migrate.run/2
        (elixir) lib/enum.ex:737: Enum."-each/2-lists^foreach/1-0-"/2
        (elixir) lib/enum.ex:737: Enum.each/2
        (mix) lib/mix/task.ex:314: Mix.Task.run_task/3
        (mix) lib/mix/cli.ex:80: Mix.CLI.run_task/2
    

    Expected behavior

    The migration runs outside a transaction. ;)

    @matthewlehner could you please investigate? According to the test we were supposed to detect it just fine: https://github.com/elixir-ecto/ecto/blob/v2.2.8/lib/ecto/migrator.ex#L114-L123

    So we are missing something somewhere.

    Here you go!
    https://github.com/matthewlehner/concurrent_indexes

    From my precursory trace through the logic, it looks like it does hit Ecto.Migrator.run_maybe_in_transaction/3 and runs fun.() under the true condition, not module.__migration__[:disable_ddl_transaction].

    Beyond that, I got lost… Also, maybe don't rely on what I've said above, I'm not familiar enough with how macros result in this code to be super confident in my diagnosis and got to this point by adding IEx.pry() in the function.

    Migration doesn't disable transactions create_if_not_exists with index doesn't work with concurrently Feb 10, 2018

    Good catch. This stack overflow post has more information about it: https://dba.stackexchange.com/questions/35616/create-index-if-it-does-not-exist

    The proper statement that works with CONCURRENTLY is only available on Postgres 9.5 which is quite recent, so I don't feel like we should impose it onto users.

    My suggestion is for you to run this particular command at the SQL level:

    execute "CREATE INDEX CONCURRENTLY IF NOT EXISTS ...."
              

    I am pretty sure this works now. The following (slightly doctored 😄) has shown to work locally using Postgres.

    defmodule MyApp.Repo.Migrations.CreateFooNumbersGinIndexes do
      use Ecto.Migration
      @disable_ddl_transaction true
      @disable_migration_lock true
      def change do
        execute "CREATE EXTENSION IF NOT EXISTS pg_trgm", "DROP EXTENSION pg_trgm"
        create_if_not_exists index(:foos, ["foo_number gin_trgm_ops"],
                               name: "foos_foo_number_gin_trgm_idx",
                               concurrently: true,
                               using: :gin