ankane / strong_migrations

Catch unsafe migrations in development

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

2 ideas for (optional?) rules

jjb opened this issue · comments

  1. do not allow mixing transaction-allowed and transaction-not-allowed changes in one migration
  2. do not allow more than one transaction-not-allowed change in one migration

This helps keep retries more robust and various problem-solving scenarios easier to deal with.

Hey @jjb, thanks for the suggestion. I don't want to support more checks that don't affect safety right now, so I think these are better as custom checks (maybe someone can create a gist for it).

Gotcha - sounds good - I didn't know about custom checks! Can a check access info about the other invoked methods in a migration? I didn't see anything in checks.rb but maybe I'm missing it. If not possible, will be a fun metaprogramming challenge 😈

You can keep track of previous method calls with:

method_calls = {}
StrongMigrations.add_check do |method, args|
  # your check code ...

  (method_calls[version] ||= []) << [method, args]
end

Edit: you can use version to differentiate between migrations

🤯

@ankane can't figure this out: how does one introspect if disable_ddl_transaction! is present?

add_check executes inside the migration context, so it'll be the same as inside the migration (self.class.disable_ddl_transaction).

Edit: That's also how/why version works, fwiw

came up with this 🎉 which I'll put in an initializer

thanks to having replaced myself with a (very small) script, i no longer have to bug my colleagues about this.

# this scenario can still happen with add_reference, we could inhibit that too
# https://github.com/ankane/strong_migrations/issues/202
strong_migrations_method_calls = {}
StrongMigrations.add_check do |method, args|
  next unless 20221104000000 < version

  host = connection.raw_connection.host
  db = connection.current_database
  strong_migrations_method_calls[host] ||= {}
  strong_migrations_method_calls[host][db] ||= {}
  strong_migrations_method_calls[host][db][version] ||= []
  strong_migrations_method_calls[host][db][version] << [method, args]

  if strong_migrations_method_calls[host][db][version].size > 1 && disable_ddl_transaction
    stop! <<~MESSAGE
      If a migration uses disable_ddl_transaction!, it must have only 1 operation.
      Put another way: if an operation requires disable_ddl_transaction!, then it must
      be in its own migration by itself.
      Note: because this is a disable_ddl_transaction! migration, the first operation in
      the migration probably succeeded, so if you try to run this again after fixing it you will get a
      "something already exists" error. So, you'll need to manually undo the first operation
      in this migration to get your dev environment back in good shape.
    MESSAGE
  end
end

Thanks for sharing