错误: 必须在架构 "heroku_ext" 中安装 "btree_gist" 扩展名。

33

Heroku更改了postgressql扩展的安装方式

这导致在heroku上新建的rails review app出现以下错误。

ERROR: 扩展“btree_gist”必须安装在模式“heroku_ext”中

这让我需要删除现有扩展并重新启用heroku_ext模式。我使用bin/rails db:structure:load命令,在运行迁移之前。

此外,structure.sql将会分歧,因为heroku在review应用程序中添加了模式,并且我们需要在本地开发机器上手动运行创建。

有人遇到过这个问题吗?


1
不是RoR,但这个问题也在讨论Heroku PostgreSQL的更改:https://dev59.com/DFEG5IYBdhLWcg3wIUpb - Doug Harris
你联系了Heroku吗? - Frank Heikens
我们最近也遇到了同样的问题。已经提交了支持工单,但是Heroku还没有回复。 - sfate
我们已经联系了Heroku并正在等待他们的回复。 - Raghu Varma
提交了工单,让我们的企业支持代表介入,但他们的响应非常缓慢,而且没有提供详细信息。这对我们的 BI 流程产生了影响,我希望他们能提供一个选项来禁用数据库的这个新“功能”,直到他们解决这些问题。 - chrismo
8个回答

14

我开发了一个猴子补丁解决方案,虽然有些不规范,但比预先日期迁移或从schema.rb中删除行要好。

把这个放在config/initializers中。我把它叫做_enable_extension_hack.rb以确保它被早期加载。

module EnableExtensionHerokuMonkeypatch
  # Earl was here
  def self.apply_patch!
    adapter_const = begin
      Kernel.const_get('ActiveRecord::ConnectionAdapters::PostgreSQLAdapter')
    rescue NameError => ne
      puts "#{self.name} -- #{ne}"
    end

    if adapter_const
      patched_method = adapter_const.instance_method(:enable_extension)

      # Only patch this method if it's method signature matches what we're expecting
      if 1 == patched_method&.arity
        adapter_const.prepend InstanceMethods
      end
    end
  end

  module InstanceMethods
    def enable_extension(name)
      name_override = name

      if schema_exists?('heroku_ext')
        puts "enable_extension -- Adding SCHEMA heroku_ext"
        name_override = "#{name}\" SCHEMA heroku_ext -- Ignore trailing double quote"
      end

      super name_override
    end
  end
end

EnableExtensionHerokuMonkeypatch.apply_patch!

它做什么?它对在db/schema.rb中导致问题的enable_extension调用进行了猴子补丁。具体而言,如果它发现有一个名为"heroku_ext"的模式,则会“装饰”给定给enable_extension的参数,以便执行的SQL指定"heroku_ext"模式。就像这样:

CREATE EXTENSION IF NOT EXISTS "pg_stat_statements" SCHEMA heroku_ext -- Ignore trailing double quote"

如果没有这个技巧,生成的SQL会像这样:

CREATE EXTENSION IF NOT EXISTS "pg_stat_statements"

2
实际上,一种优雅的解决方案可以避免更改迁移文件,并防止需要在本地创建heroku_ext模式的需求。 - James Woodrow
2
对于任何因“ActiveRecord :: ConnectionAdapters :: PostgreSQLAdapter”名称错误而面临加载问题的人,您可以在补丁的顶部包含以下行:require“active_record/connection_adapters/postgresql_adapter” - Joel Biffin

12

这是一个更干净的修补schema.rb的方式。

# config/initializers/patch_enable_extension.rb

require 'active_record/connection_adapters/postgresql_adapter'

# NOTE: patch for https://devcenter.heroku.com/changelog-items/2446
module EnableExtensionHerokuPatch
  def enable_extension(name, **)
    return super unless schema_exists?("heroku_ext")

    exec_query("CREATE EXTENSION IF NOT EXISTS \"#{name}\" SCHEMA heroku_ext").tap { reload_type_map }
  end
end

module ActiveRecord
  module ConnectionAdapters
    class PostgreSQLAdapter
      prepend EnableExtensionHerokuPatch
    end
  end
end

在我看来,这是正确的答案。但是,在顶部我确实需要一个require 'active_record/connection_adapters/postgresql_adapter' - dzuelke
太棒了!简单直接!谢谢! - Pedro Augusto

3

编辑:这是官方的变更日志https://devcenter.heroku.com/changelog-items/2446

由于Heroku最近的更改,您需要做一些难以想象的事情,即修改过去的迁移文件,以便您的审核应用程序能够与新的Heroku扩展系统配合使用。

  1. 在第一个迁移文件的顶部添加预日期信息 connection.execute 'CREATE SCHEMA IF NOT EXISTS heroku_ext'
  2. 可能还要添加到 database.yml 中,包括 heroku_ext 的模式搜索路径(如果您没有自定义,则将其设置为 public,heroku_ext
  3. 搜索所有 enable_extension('extension_name')
  4. 将它们全部替换为 connection.execute('CREATE EXTENSION IF NOT EXISTS "extension_name" WITH SCHEMA "heroku_ext")
  5. 祈祷这就足够解决问题了

在进行上述更改之后,我们仍然需要联系Heroku支持,因为出现了以下问题:

  • 一个 pgaudit stack is not empty 错误
    • 解决方法是运行两次维护(因为预定的postgres添加已经安排好维护,并且早于架构/扩展更改)
  • 一个 ERROR: function pg_stat_statements_reset(oid, oid, bigint) does not exist 错误
    • 解决方法是Heroku在数据库上进行手动干预。这是由Heroku尝试每次创建模式时运行pg_stat_statements_reset引起的。

对于现有的数据库(例如在生产环境中),新的扩展应该安装在公共目录还是heroku_ext目录? - sguha
@sguha 所有现有数据库中的新扩展都应该在 heroku_ext 模式中创建。(我已经添加了一个链接到 heroku 更新日志,其中解释了所有这些) - James Woodrow
对于4,我们应该使用“CREATE EXTENSION IF NOT EXISTS”,因为enable_extension使用了该语句。 - Masafumi Okura

1
这个技巧对我来说似乎有效。以下脚本在app.json的postdeploy步骤中运行。
#!/bin/bash -xue
# Create extensions in the schema where Heroku requires them to be created
# The plpgsql extension has already been created before this script is run
heroku pg:psql -a $HEROKU_APP_NAME -c 'create extension if not exists citext schema heroku_ext'
heroku pg:psql -a $HEROKU_APP_NAME -c 'create extension if not exists pg_stat_statements schema heroku_ext'

# Remove enable_extension statements from schema.rb before loading it, since
# even 'create extension if not exists' fails when the schema is not heroku_ext
mv db/schema.rb{,.orig}
grep -v enable_extension db/schema.rb.orig > db/schema.rb
rails db:schema:load

0
dzirtusss 提供的答案大部分已经解决了我的问题,但是我在将此方法应用于我的 Rails 应用程序时还需要做最后一步操作,即编辑 config/database.yml 中的搜索路径,以包含 heroku_ext。这是我添加到默认配置的内容。
# config/database.yml
default:
  schema_search_path: public,heroku_ext

在添加了初始化脚本和database.yml编辑后,我成功地运行了heroku pg:copy命令,从一个应用程序复制到另一个应用程序,没有出现任何错误或意外行为。


0
Heroku最近推出了一个--extensions标志,可以作为解决方法。请参见FAQ
例如,如果您正在使用btree_gist扩展程序,则可以在运行迁移之前使用heroku run pg:reset --extensions 'btree_gist' --app YOUR_APP_NAME,然后它们应该可以在不更改代码库的情况下正常工作。

0
  1. 使用Heroku数据存储耐久性工具在源数据库上创建备份或者使用heroku pg:backups:capture -a <SOURCE_APP>命令。
  2. 确定数据库使用的pg扩展(可以通过psql命令\dx进行检查)
  3. 创建一个逗号分隔的扩展字符串(例如:fuzzystrmatch,pg_stat_statements,pg_trgm,pgcrypto,plpgsql,unaccent,uuid-ossp'
  4. 确保您的Heroku CLI已更新至至少7.63.0版本(使用heroku update命令进行更新)
  5. 运行以下命令:
    heroku pg:backups:restore $(heroku pg:backups public-url -a <SOURCE_APP>) DATABASE_URL --extensions '<EXTENSIONS>' -a <TARGET_APP>
    
  6. 重置目标应用程序上的dynos

0
截至2022年8月16日星期二,我们发现不再需要补丁,enable_extension似乎正常工作,无需显式指定模式。
当我在与Heroku支持的持续线程中提到这一点时,他们说,在解决https://status.heroku.com/incidents/2450时,他们围绕heroku_ext更改的一些修复可能已经发布,但仍有进一步的修复正在开发中尚未发布 - 所以我猜测这个特定问题是已经被解决的问题之一!

1
我今天尝试运行pg:copy,但仍然遇到相同的问题。 - dcporter7

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