如何在Heroku上使用Postgres数据库禁用预处理语句

11

我在本地修复了我的Rails项目(与PostgreSQL配置有关)。在database.yml中添加了以下语句:

test:
  prepared_statements: false

我修复的错误与这个问题有关:

 PG::ProtocolViolation: ERROR: bind message supplies 2 parameters, but prepared            statement "a24" requires 1 

现在,我想将它应用于我的托管在Heroku上的生产应用程序,并与Postgres数据库配合使用。由于database.yml是自动生成的,我不知道如何禁用预处理语句。我尝试添加以下内容:

现在,我希望将其固定在我的托管在Heroku上的生产应用程序上,并与postgres数据库配合使用。由于database.yml是自动生成的,我不知道如何禁用准备好的语句。我尝试追加:

/database?prepared_statements=false

我尝试连接到我的数据库URI,但结果得到了错误的DATABSE_URL,因此无法连接到我的数据库。

如何禁用prepared_statement的过程和正确语法是什么?


你更多是“绕过”了这个问题,而不是真正地解决它。如果ActiveRecord、Pg gem等确实存在真正的bug,最好有一个自包含的测试用例来证明它。(我无法帮助你在Heroku上应用workaround,因为我很少使用它的应用托管服务)。 - Craig Ringer
6个回答

17
截至2014年2月19日,Heroku已经不再覆盖database.yml,因此您可以按照最新文档的建议,在database.yml文件的productionstaging(或default)块中关闭预处理语句。no longer overrides database.yml latest docs
default: &default
  adapter: postgresql
  encoding: unicode
  pool: 5
  prepared_statements: false

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

production:
  <<: *default
  url:  <%= ENV['DATABASE_URL'] %>
  pool: <%= ENV['DB_POOL'] || ENV['MAX_THREADS'] || 5 %>

staging:
  <<: *default
  url:  <%= ENV['DATABASE_URL'] %>
  pool: <%= ENV['DB_POOL'] || ENV['MAX_THREADS'] || 5 %>

3
这个设置 prepared_statements 对我来说起作用了,你可以在 rails console 中运行 ActiveRecord::Base.configurations[Rails.env] 来验证它。 - Felix
1
在markquezada的答案中成功地描述了如何禁用预备语句,这里提供Heroku文章链接:https://devcenter.heroku.com/articles/postgres-logs-errors#out-of-memory-errors。 - Marklar

7
我们担心易碎性以及在staging/production(使用Heroku上的DATABASE_URL)与development/test(使用database.yml/database.example.yml)之间维持一致性。
受Rails测试的启发,我们将以下代码放置于config/initializers/disable_prepared_statements.rb中:(参见链接)
ActiveRecord::Base.establish_connection(
  ActiveRecord::Base.remove_connection.merge(
    :prepared_statements => false
  )
)

remove_connection返回被移除连接的连接参数哈希表。这将使得任何database.yml或DATABASE_URL继续工作。


1
这对我不起作用:ActiveRecord::Base.configurations 不包含 prepared_statements => false。 - Nico

3

您只需要在现有的数据库URL后面添加?prepared_statements=false,然后重新启动dynos即可。这对我们很有效。

heroku config:add DATABASE_URL=[old database url here]?prepared_statements=false

要检查是否在重新启动服务器后设置了,您可以打开控制台并查询 ActiveRecord::Base.connection_config


1
最近看起来Heroku已经禁用了使用heroku config:set DATABASE_URL=<blah>?prepared_statements=false设置DATABASE_URL的功能,会返回一个错误"▸ Cannot overwrite attachment values DATABASE_URL."。
为了解决这个问题,我们在config/中添加了一个disabled_prepared_statements.rb初始化器,其中包含:ActiveRecord::Base.configurations[Rails.env].merge!(prepared_statements: false)

1
你可以在初始化器中向 ActiveRecord::Base.establish_connection 传递一个配置哈希值。例如:
configure :production, :development, :test do
  db = URI.parse(ENV['DATABASE_URL']

  ActiveRecord::Base.establish_connection(
      :adapter => db.scheme == 'postgres' ? 'postgresql' : db.scheme,
      :host                => db.host,
      :username            => db.user,
      :password            => db.password,
      :database            => db.path[1..-1],
      :encoding            => 'utf8',
      :prepared_statements => false,
  )
end

http://apidock.com/rails/ActiveRecord/Base/establish_connection/class


-4

关闭预处理语句会降低性能,因为PostgreSQL在执行每个查询之前都必须重新计划,因此我不建议在生产服务器上关闭它 - 特别是当Rails在没有真正注意到急切加载所有内容时会执行许多小查询。相反,我建议找出如何在每次部署后在实时环境中进行重启而不影响您的服务可用性。虽然我不是Rails大师,但我知道这是可行的(我们公司就是这样做的)。这里有更多关于为什么会发生这种情况的见解 https://github.com/rails/rails/issues/12330


那个问题意味着这样。 - Todd Gardner
@kristok,您能否提供一些有关如何进行渐进式部署而不会遇到预处理语句问题的建议?我在那个问题中没有看到任何提供解决方案的内容,除了禁用它们。仅仅说这是可能的,而不给出任何见解并不是很有帮助。 - lobati
@lobati,这个问题没有简单的解决方案,这就是为什么还没有提供。通用的谷歌搜索关键词是“Rails零停机部署”,基本上每个人都在构建自己复杂的程序来克服Rails的缺点。 - kristok
@kristok 谷歌搜索“rails零停机部署”的结果往往解决如何在两个代码版本之间保持数据库和代码兼容的问题。我没有看到任何解决处理预编译语句时出现的Active Record问题的内容。我确实看到一些建议禁用它以便使用pg_bouncer扩展数据库的内容。 - lobati
@lobati 只要 Rails 迁移存在问题,就需要这样做,除非你打算深入挖掘并解决它。在我写 Rails 的九个月中,最糟糕的情况是迁移已经稳定了四年,但因为 Rails 在版本之间更改了 DDL 生成逻辑,某一天突然在开发人员的电脑上崩溃了。之后,就真的没有办法基于现有迁移结构来拥有可重现的生产结构,所以我们只能在迁移中插入一些东西,以使开发环境类似于生产环境。所以我的最佳答案是遵循这些代码兼容性规则。 - kristok

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接