Heroku的Postgres PostGIS - Django发布失败:关系“spatial_ref_sys”不存在。

31

Heroku于2022年8月1日更改了他们的PostgreSQL扩展架构管理。 (https://devcenter.heroku.com/changelog-items/2446)

自那时以来,我们现有的django 4.0应用程序每次部署到Heroku时都会在发布阶段失败,但是构建成功。

是否有人遇到了同样的问题?有没有解决方法可以推送新版本到Heroku,而不必重新安装postgis扩展?

如果我理解正确,Heroku为新创建的扩展添加了一个名为“heroku_ext”的架构。在我们的情况下,由于该扩展已经存在,因此不应受到影响。

所有当前安装的扩展将继续按预期工作。

以下是通过git push进行发布的完整日志:

git push staging develop:master
Gesamt 0 (Delta 0), Wiederverwendet 0 (Delta 0), Pack wiederverwendet 0
remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> Building on the Heroku-20 stack
remote: -----> Using buildpacks:
remote:        1. https://github.com/heroku/heroku-geo-buildpack.git
remote:        2. heroku/python
remote: -----> Geo Packages (GDAL/GEOS/PROJ) app detected
remote: -----> Installing GDAL-2.4.0
remote: -----> Installing GEOS-3.7.2
remote: -----> Installing PROJ-5.2.0
remote: -----> Python app detected
remote: -----> Using Python version specified in runtime.txt
remote: -----> No change in requirements detected, installing from cache
remote: -----> Using cached install of python-3.9.13
remote: -----> Installing pip 22.1.2, setuptools 60.10.0 and wheel 0.37.1
remote: -----> Installing SQLite3
remote: -----> Installing requirements with pip
remote: -----> Skipping Django collectstatic since the env var DISABLE_COLLECTSTATIC is set.
remote: -----> Discovering process types
remote:        Procfile declares types -> release, web, worker
remote: 
remote: -----> Compressing...
remote:        Done: 156.1M
remote: -----> Launching...
remote:  !     Release command declared: this new release will not be available until the command succeeds.
remote:        Released v123
remote:        https://myherokuapp.herokuapp.com/ deployed to Heroku
remote: 
remote: This app is using the Heroku-20 stack, however a newer stack is available.
remote: To upgrade to Heroku-22, see:
remote: https://devcenter.heroku.com/articles/upgrading-to-the-latest-stack
remote: 
remote: Verifying deploy... done.
remote: Running release command...
remote: 
remote: Traceback (most recent call last):
remote:   File "/app/.heroku/python/lib/python3.9/site-packages/django/db/backends/utils.py", line 87, in _execute
remote:     return self.cursor.execute(sql)
remote: psycopg2.errors.UndefinedTable: relation "spatial_ref_sys" does not exist
remote: 
remote: 
remote: The above exception was the direct cause of the following exception:
remote: 
remote: Traceback (most recent call last):
remote:   File "/app/manage.py", line 22, in <module>
remote:     main()
remote:   File "/app/manage.py", line 18, in main
remote:     execute_from_command_line(sys.argv)
remote:   File "/app/.heroku/python/lib/python3.9/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
remote:     utility.execute()
remote:   File "/app/.heroku/python/lib/python3.9/site-packages/django/core/management/__init__.py", line 440, in execute
remote:     self.fetch_command(subcommand).run_from_argv(self.argv)
remote:   File "/app/.heroku/python/lib/python3.9/site-packages/django/core/management/base.py", line 414, in run_from_argv
remote:     self.execute(*args, **cmd_options)
remote:   File "/app/.heroku/python/lib/python3.9/site-packages/django/core/management/base.py", line 460, in execute
remote:     output = self.handle(*args, **options)
remote:   File "/app/.heroku/python/lib/python3.9/site-packages/django/core/management/base.py", line 98, in wrapped
remote:     res = handle_func(*args, **kwargs)
remote:   File "/app/.heroku/python/lib/python3.9/site-packages/django/core/management/commands/migrate.py", line 106, in handle
remote:     connection.prepare_database()
remote:   File "/app/.heroku/python/lib/python3.9/site-packages/django/contrib/gis/db/backends/postgis/base.py", line 26, in prepare_database
remote:     cursor.execute("CREATE EXTENSION IF NOT EXISTS postgis")
remote:   File "/app/.heroku/python/lib/python3.9/site-packages/sentry_sdk/integrations/django/__init__.py", line 544, in execute
remote:     return real_execute(self, sql, params)
remote:   File "/app/.heroku/python/lib/python3.9/site-packages/django/db/backends/utils.py", line 67, in execute
remote:     return self._execute_with_wrappers(
remote:   File "/app/.heroku/python/lib/python3.9/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
remote:     return executor(sql, params, many, context)
remote:   File "/app/.heroku/python/lib/python3.9/site-packages/django/db/backends/utils.py", line 89, in _execute
remote:     return self.cursor.execute(sql, params)
remote:   File "/app/.heroku/python/lib/python3.9/site-packages/django/db/utils.py", line 91, in __exit__
remote:     raise dj_exc_value.with_traceback(traceback) from exc_value
remote:   File "/app/.heroku/python/lib/python3.9/site-packages/django/db/backends/utils.py", line 87, in _execute
remote:     return self.cursor.execute(sql)
remote: django.db.utils.ProgrammingError: relation "spatial_ref_sys" does not exist
remote: 
remote: Sentry is attempting to send 2 pending error messages
remote: Waiting up to 2 seconds
remote: Press Ctrl-C to quit
remote: Waiting for release.... failed.
To https://git.heroku.com/myherokuapp
8个回答

4
下面是我想到的可行方案,用于处理使用 pg:backups:restore 进行数据库恢复的评论应用程序(如果您正在操作生产数据库,则可能需要启用维护):
  1. 从本地复制您的评论应用程序数据库(最近通过 pg:backups:restore 恢复的一个,以便获取所有数据):heroku pg:pull [Database URL] localdb -a [app-name]

  2. 将应用程序数据库配置设置为使用 localdb ,然后连接到 psql 并执行:ALTER EXTENSION "hstore" SET SCHEMA heroku_ext;。 对于所有现有扩展都要运行此命令。

要列出您已提取的所有可用扩展名,请运行 \dx。您不必更改 plpgsql,因为它是 PostgreSQL 的本机扩展。

这将有效,因为在本地您拥有所有特权。

  1. 将此版本推回评论应用程序:heroku pg:push mylocaldb [Database URL] -a [app-name] ==> 执行此操作时您的数据库需要为空。您可以尝试在具有空数据的新评论应用程序上执行此操作。这样可以成为所有评论应用程序的新基础。

  2. 确保一切正常(数据已正确还原)。然后,您可以通过 pg:backups:capture 生成新的数据库转储,并将其用作所有新评论应用程序的新备份数据库。

来源:https://devcenter.heroku.com/articles/managing-heroku-postgres-using-cli

我也不得不这样做,因为例如删除 hstore 扩展并重新启用它对我们的情况来说不是可行的选项。


3
我通过重写postgis/base.py引擎来解决了这个问题,我将以下内容放在我的应用程序下的db/base.py中。
from django.contrib.gis.db.backends.postgis.base import (
     DatabaseWrapper as PostGISDatabaseWrapper,
)

class DatabaseWrapper(PostGISDatabaseWrapper):
    def prepare_database(self):
        # This is the overwrite - we don't want to call the
        # super() because of a faulty extension creation
     pass

那么在我的设置中,我只需将 DATABASES["engine"] = "app.db"指向即可。

虽然它不能帮助备份,但至少我可以再次发布。


你确定这对你有效吗?我尝试过相同的操作,部署后是可行的,但我无法访问任何postgis数据。每次我在管理员中访问具有PointField的模型时,都会出现以下错误: “AttributeError:'DatabaseOperations'对象没有属性'select'”这可能会增加你的麻烦,因为如果没有这个解决方法,你将无法发布版本,因为它在部署的发布阶段失败。 - Simon
如果您没有与PointField相关的任何迁移,则现在这是最好的解决方案。只需添加上述内容,部署并运行迁移,然后再将引擎更改为django.contrib.gis.db.backends.postgis并重新部署即可。 - elenag
@Simon,我在从数据库中读取数据时没有遇到任何问题,你确定你的数据库实际上安装了Postgis吗?在我们的情况下,Heroku提供了Postgis,这可能解释了为什么它可以正常工作。 - marcinowski
@marcinowski 是的,错误是我的问题。我在这里写成了DATABASES['default']['ENGINE']: 'my.settings.patched_postgis',而不是DATABASES['default']['ENGINE'] = 'my.settings.patched_postgis'。(从其他地方复制字典时出现了误差)。这是一个很好的解决方法,谢谢! - Simon

3

我们在使用 heroku pg:backups:restoreheroku pg:copy 时也遇到了这个问题——如果备份快照中包含已安装的扩展,则无法恢复现有备份快照。


3

我们采用了@chedli提供的解决方案 https://dev59.com/DFEG5IYBdhLWcg3wIUpb#73219273

但是在我们的情况下,当尝试使用ALTER EXTENSION postgis SET SCHEMA heroku_ext解决方法时,postgis会出现"不允许重定位模式"的错误,因此我们最终需要执行这个额外的步骤。

UPDATE pg_extension
  SET extrelocatable = true
    WHERE extname = 'postgis';

ALTER EXTENSION "postgis" SET SCHEMA "heroku_ext";

UPDATE pg_extension
  SET extrelocatable = false
    WHERE extname = 'postgis';

1
我尝试了这个操作,但是出现了“对于表pg_extension的权限被拒绝”的错误提示。 - Doug Harris
目前唯一可用的方法由@chedli描述-https://dev59.com/DFEG5IYBdhLWcg3wIUpb#73219273 - sfate
@sfate,你可以帮忙点赞,这样它就更容易被看到了,谢谢! - chedli
@DougHarris,你不能直接在Heroku数据库实例上运行此命令,你必须在拥有权限的服务器上清理你的模式。 - MrMaz
@chedli,我更新了我的答案,引用了你的原始解决方法,并澄清了我的答案是一个额外的步骤。 - MrMaz

2

我的团队也遇到了这个问题。我们不能等待Heroku修复,所以我们进行了一些冒险的手术。这种方法可能不适用于每个人,但由于我们对PostGIS的使用相当少,所以并不太糟糕。

以下是我为每个受影响的数据库制定的检查清单:

  • 备份数据库
  • 开启维护模式
  • 在所有使用geography类型存储原始经纬度数据的表上创建新的基本float
  • geography字段的值写入新列中
  • 删除postgis扩展:DROP EXTENSION postgis CASCADE;
  • 重新创建扩展:CREATE EXTENSION IF NOT EXISTS postgis;
  • 重新创建我们的geography字段并从新列中填充它们
  • 删除新的临时列
  • 重新创建适当的索引

目前为止还不错。


2
我有一个解决方案,不需要更改代码库,可以完全通过Heroku CLI完成。
  1. Use the Heroku datastore durability tool to create a backup on the source database or heroku pg:backups:capture -a <SOURCE_APP>.
  2. Determine which pg extensions the database uses (can check from psql with \dx)
  3. create a comma-separated string of the extensions (e.g.:fuzzystrmatch,pg_stat_statements,pg_trgm,pgcrypto,plpgsql,unaccent,uuid-ossp')
  4. Make sure your Heroku CLI is updated to at least version 7.63.0 (use heroku update to update)
  5. Run this:
    heroku pg:backups:restore $(heroku pg:backups public-url -a <SOURCE_APP>) DATABASE_URL --extensions '<EXTENSIONS>' -a <TARGET_APP>
    
  6. Reset dynos on the TARGET_APP

1

我正在跟踪一个非常类似的问题。我已经联系了Heroku。我的先前错误提到了pgaudit扩展,现在我看到了和你一样的错误。如果我找到解决方案,我会在这里更新。

System check identified no issues (5 silenced).
Traceback (most recent call last):
File "/app/.heroku/python/lib/python3.10/site-packages/django/db/backends/utils.py", line 87, in _execute
return self.cursor.execute(sql)
psycopg2.errors.UndefinedTable: relation "spatial_ref_sys" does not exist
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/app/manage.py", line 40, in
main()
File "/app/manage.py", line 36, in main
execute_from_command_line(sys.argv)
File "/app/.heroku/python/lib/python3.10/site-packages/django/core/management/init.py", line 446, in execute_from_command_line
utility.execute()
File "/app/.heroku/python/lib/python3.10/site-packages/django/core/management/init.py", line 440, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "/app/.heroku/python/lib/python3.10/site-packages/django/core/management/base.py", line 414, in run_from_argv
self.execute(*args, **cmd_options)
File "/app/.heroku/python/lib/python3.10/site-packages/django/core/management/base.py", line 460, in execute
output = self.handle(*args, **options)
File "/app/.heroku/python/lib/python3.10/site-packages/django/core/management/base.py", line 98, in wrapped
res = handle_func(*args, **kwargs)
File "/app/.heroku/python/lib/python3.10/site-packages/django/core/management/commands/migrate.py", line 106, in handle
connection.prepare_database()
File "/app/.heroku/python/lib/python3.10/site-packages/psqlextra/backend/base.py", line 32, in prepare_database
super().prepare_database()
File "/app/.heroku/python/lib/python3.10/site-packages/django/contrib/gis/db/backends/postgis/base.py", line 26, in prepare_database
cursor.execute("CREATE EXTENSION IF NOT EXISTS postgis")
File "/app/.heroku/python/lib/python3.10/site-packages/sentry_sdk/integrations/django/init.py", line 544, in execute
return real_execute(self, sql, params)
File "/app/.heroku/python/lib/python3.10/site-packages/django/db/backends/utils.py", line 67, in execute
return self._execute_with_wrappers(
File "/app/.heroku/python/lib/python3.10/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
return executor(sql, params, many, context)
File "/app/.heroku/python/lib/python3.10/site-packages/django_read_only/init.py", line 74, in blocker
return execute(sql, params, many, context)
File "/app/.heroku/python/lib/python3.10/site-packages/django/db/backends/utils.py", line 84, in _execute
with self.db.wrap_database_errors:
File "/app/.heroku/python/lib/python3.10/site-packages/django/db/utils.py", line 91, in exit
raise dj_exc_value.with_traceback(traceback) from exc_value
File "/app/.heroku/python/lib/python3.10/site-packages/django/db/backends/utils.py", line 87, in _execute
return self.cursor.execute(sql)
django.db.utils.ProgrammingError: relation "spatial_ref_sys" does not exist
Sentry is attempting to send 2 pending error messages
Waiting up to 2 seconds
Press Ctrl-C to quit

6
更新:这是我从Heroku代表那里收到的最新回复--感谢您的回复。看起来这个新错误是一个已知问题,我们的数据工程团队正在积极地解决它;他们预计修复将在今天晚些时候推出。我会关注他们的进展,并在修复实施后通知您。 - Rory-R-Reyes
谢谢@Rory-R-Reyes,很高兴听到我不是唯一一个遇到这个问题的人。 - Simon
谢谢@Rory-R-Reyes,我们今天也遇到了完全相同的应用程序问题。 - Chris B.
同时破坏了我们的备份恢复流程。 - seigel
出了很多问题,包括我们的特性环境。即使成功安装后,新建的环境也无法识别扩展名。 - John Cartwright
我在中央标准时间7:30左右收到了这个更新。自那以后我没有听到任何更新 - 我想回复您确认我们的团队仍在解决此问题。我们已经找到了问题的原因,并正在尽快部署修复。请放心,这是我们目前最重要的任务,我们将继续努力减轻影响并尽快找到解决方案。 - Rory-R-Reyes

1

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