如何在 Rails 中不启动事务的情况下运行迁移?
我正在从 OpenCongress 运行一些奇怪的 Postgres 迁移代码,并且收到此错误:
RuntimeError: ERROR C25001 MVACUUM cannot run inside a transaction block
Fxact.c L2649 RPreventTransactionChain: VACUUM FULL ANALYZE;
所以我我想尝试在不被事务包裹的情况下运行它。
I'm running some bizarre Postgres migration code from OpenCongress and I'm getting this error:
RuntimeError: ERROR C25001 MVACUUM cannot run inside a transaction block
Fxact.c L2649 RPreventTransactionChain: VACUUM FULL ANALYZE;
So I'd like to try running it without getting wrapped by a transaction.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
现在有一个方法
disable_ddl_transaction!
允许这样做,例如:There's now a method
disable_ddl_transaction!
that allows this, e.g.:ActiveRecord::Migration
具有以下私有方法,在运行迁移时会调用该方法:如您所见,如果连接支持,这会将迁移包装在事务中。
在
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
中,您有:SQLite 2.0 版及更高版本也支持迁移事务。
在 ActiveRecord::ConnectionAdapters::SQLiteAdapter 中,您有:
因此,要跳过事务,您需要以某种方式规避此问题。
像这样的东西可能会起作用,尽管我还没有测试过:
然后您可以按如下方式设置迁移:
ActiveRecord::Migration
has the following private method that gets called when running migrations:As you can see this will wrap the migration in a transaction if the connection supports it.
In
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
you have:SQLite version 2.0 and beyond also support migration transactions.
In
ActiveRecord::ConnectionAdapters::SQLiteAdapter
you have:So then, to skip transactions, you need to somehow circumvent this.
Something like this might work, though I haven't tested it:
You could then set up your migration as follows:
一个非常简单的、与 Rails 版本无关(2.3、3.2、4.0,无关紧要)的方法是简单地将
execute("commit;")
添加到迁移的开头,然后然后编写SQL。这会立即关闭 Rails 启动的事务,并允许您编写可以创建自己的事务的原始 SQL。在下面的示例中,我使用
.update_all
和子选择LIMIT
来处理更新大型数据库表。举个例子,
An extremely simple, Rails-version-independent (2.3, 3.2, 4.0, doesn't matter) way about this is to simply add
execute("commit;")
to the beginning of your migration, and then write SQL.This immediately closes the Rails-started transaction, and allows you to write raw SQL that can create its own transactions. In the below example, I use an
.update_all
and a subselectLIMIT
to handle updating a huge database table.As an example,
轨道 4 +
有一个方法disable_ddl_transaction!,您可以在迁移文件中使用它,如下所示。
Rails 4 下面
像上面的一些答案一样,有一个简单的技巧,您可以提交事务,然后在迁移完成后再次开始事务,如下所示,
这在我们无法同时创建/删除索引的情况下会很有帮助,因为这些不能在事务中执行。
如果您尝试,您将收到错误“PG::ActiveSqlTransaction:错误:DROP INDEX CONCURRENTLY 无法在事务块内运行。”
Rails 4 +
There is a method disable_ddl_transaction!, you can use it in your migration file like below.
Below Rails 4
Like some of answers above, there is a simple hack, you can commit the transaction and then after your migration has completed you again the begin the transaction, like below
This can be helpful in case where we cant create/drop index concurrently, as these cannot be executed in a transaction.
If you try you will get error "PG::ActiveSqlTransaction: ERROR: DROP INDEX CONCURRENTLY cannot run inside a transaction block."
对于 Rails 3,上述答案已被破坏,因为 ddl_transaction 已移至 ActiveRecord::Migrator 中。我无法找到一种方法来猴子修补该类,所以这里有一个替代解决方案:
我在 lib/ 下添加了一个文件
然后您在迁移中要做的就是:
这所做的是在加载迁移类时禁用事务(希望在加载所有先前的事务之后以及加载任何未来的事务之前),然后在迁移之后,恢复现有的任何旧事务功能。
The above answer is broken for Rails 3 as ddl_transaction was moved into ActiveRecord::Migrator. I could not figure out a way to monkey patch that class, so here is an alternate solution:
I added a file under lib/
Then all you have to do in your migration is:
What this does is disable transactions when the migration class is loaded (hopefully after all previous ones were loaded and before any future ones are loaded), then after the migration, restore whatever old transaction capabilities there were.
我并不是说这是“正确的方法”,但对我有用的是单独运行一次迁移。
其中 20120801151807 是迁移的时间戳。
显然,当您运行单个迁移时,它不使用事务。
I'm not saying this is the "right way" to do it, but what worked for me was to run just that one migration in isolation.
where 20120801151807 is the timestamp of the migration.
Apparently, it doesn't use a transaction when you run a single migration.
添加“commit;”就像黑客一样到我的sql的开头对我有用,但那是针对SQL Server的,不确定这是否适用于postgres...
示例:
CREATE FULLTEXT INDEX ...
在sql-server用户事务中是非法的。所以...:
效果很好...我们会看看我以后是否会后悔。
As hacky as this is adding 'commit;' to the beginning of my sql worked for me, but that's for SQL Server, not sure if this works for postgres...
Example:
CREATE FULLTEXT INDEX ...
is illegal inside a sql-server user transaction.so...:
works fine... We'll see if I regret it later.