Hel*_*iro 20 database migration postgresql transactions ruby-on-rails
我正在从OpenCongress运行一些奇怪的Postgres迁移代码,我收到此错误:
RuntimeError: ERROR C25001 MVACUUM cannot run inside a transaction block
Fxact.c L2649 RPreventTransactionChain: VACUUM FULL ANALYZE;
Run Code Online (Sandbox Code Playgroud)
所以我想尝试运行它而不会被事务包裹起来.
dav*_*000 77
现在有一种disable_ddl_transaction!允许这种方法的方法,例如:
class AddIndexesToTablesBasedOnUsage < ActiveRecord::Migration
disable_ddl_transaction!
def up
execute %{
CREATE INDEX CONCURRENTLY index_reservations_subscription_id ON reservations (subscription_id);
}
end
def down
execute %{DROP INDEX index_reservations_subscription_id}
end
end
Run Code Online (Sandbox Code Playgroud)
Pet*_*net 16
ActiveRecord::Migration 具有以下在运行迁移时调用的私有方法:
def ddl_transaction(&block)
if Base.connection.supports_ddl_transactions?
Base.transaction { block.call }
else
block.call
end
end
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,如果连接支持迁移,这将在事务中包装迁移.
在ActiveRecord::ConnectionAdapters::PostgreSQLAdapter你有:
def supports_ddl_transactions?
true
end
Run Code Online (Sandbox Code Playgroud)
SQLite 2.0及更高版本也支持迁移事务.在ActiveRecord::ConnectionAdapters::SQLiteAdapter你有:
def supports_ddl_transactions?
sqlite_version >= '2.0.0'
end
Run Code Online (Sandbox Code Playgroud)
那么,要跳过事务,你需要以某种方式绕过这个.这样的东西可能有用,虽然我还没有测试过:
class ActiveRecord::Migration
class << self
def no_transaction
@no_transaction = true
end
def no_transaction?
@no_transaction == true
end
end
private
def ddl_transaction(&block)
if Base.connection.supports_ddl_transactions? && !self.class.no_transaction?
Base.transaction { block.call }
else
block.call
end
end
end
Run Code Online (Sandbox Code Playgroud)
然后,您可以按如下方式设置迁移:
class SomeMigration < ActiveRecord::Migration
no_transaction
def self.up
# Do something
end
def self.down
# Do something
end
end
Run Code Online (Sandbox Code Playgroud)
pro*_*ons 11
一个非常简单的,与Rails版本无关(2.3,3.2,4.0,无关紧要)的方法就是简单地添加execute("commit;")到迁移的开头,然后编写SQL.
这会立即关闭Rails-started事务,并允许您编写可以创建自己的事务的原始SQL.在下面的示例中,我使用an .update_all和subselect LIMIT来处理更新庞大的数据库表.
举个例子,
class ChangeDefaultTabIdOfZeroToNilOnUsers < ActiveRecord::Migration
def self.up
execute("commit;")
while User.find_by_default_tab_id(0).present? do
User.update_all %{default_tab_id = NULL}, %{id IN (
SELECT id FROM users WHERE default_tab_id = 0 LIMIT 1000
)}.squish!
end
end
def self.down
raise ActiveRecord::IrreversibleMigration
end
end
Run Code Online (Sandbox Code Playgroud)
Rails 4 + 有一个方法 disable_ddl_transaction!,你可以在你的迁移文件中使用它,如下所示。
class AddIndexToTable < ActiveRecord::Migration
disable_ddl_transaction!
def change
add_index :table, :column, algorithm: :concurrently
end
end
Run Code Online (Sandbox Code Playgroud)
下轨 4
像上面的一些答案一样,有一个简单的技巧,您可以提交事务,然后在迁移完成后再次开始事务,如下所示
class AddIndexToTable < ActiveRecord::Migration
def change
execute "COMMIT;"
add_index :table, :column, algorithm: :concurrently
# start a new transaction after the migration finishes successfully
execute "BEGIN TRANSACTION;"
end
end
Run Code Online (Sandbox Code Playgroud)
这在我们不能同时创建/删除索引的情况下很有帮助,因为这些不能在事务中执行。如果您尝试,您将收到错误“PG::ActiveSqlTransaction: ERROR: DROP INDEX CONCURRENTLY cannot run inside a transaction block”。
| 归档时间: |
|
| 查看次数: |
9723 次 |
| 最近记录: |