故障档案:PG安装扩展导致无法连接
今天遇到一个比较有趣的Case,客户报告说数据库连不上了。报这个错:
psql: FATAL: could not load library "/export/servers/pgsql/lib/pg_hint_plan.so": /export/servers/pgsql/lib/pg_hint_plan.so: undefined symbol: RINFO_IS_PUSHED_DOWN当然,这种错误一眼就知道是插件没编译好,报符号找不到。因此数据库后端进程在启动时尝试加载pg_hint_plan
插件时就GG了,报FATAL错误直接退出。
通常来说这个问题还是比较好解决的,这种额外的扩展通常都是在shared_preload_libraries
中指定的,只要把这个扩展名称去掉就好了。
结果……
客户说是通过ALTER ROLE|DATABASE SET session_preload_libraries = pg_hint_plan
的方式来启用扩展的。
这两条命令会在使用特定用户,或连接到特定数据库时覆盖系统默认参数,去加载pg_hint_plan
插件。
ALTER ROLE postgres SET session_preload_libraries = pg_hint_plan;
如果是这样的话,也是可以解决的,通常来说只要有其他的用户或者其他的数据库可以正常登陆,就可以通过ALTER TABLE
语句把这两行配置给去掉。
但坏事就坏在,所有的用户和数据库都配了这个参数,以至于没有任何一条连接能连到数据库了。
这种情况下,数据库就成了植物人状态,postmaster还活着,但任何新创建的后端服务器进程都会因为扩展失效自杀……。即使dropdb
这种外部自带的二进制命令也无法工作。
于是……
无法建立到数据库的连接,那么常规手段就都失效了……,只能Dirty hack了。
如果我们从二进制层面把用户和数据库级别的配置项给抹掉,那么就可以连接到数据库,把扩展给清理掉了。
DB与Role级别的配置存储在系统目录pg_db_role_setting
中,这个表有着固定的OID = 2964,存储在数据目录下global/2964
里。关闭数据库,使用二进制编辑器打开pg_db_role_setting
对应的文件
# 编辑完成后使用 :%!xxd -r转换回二进制,再用:wq保存
vi ${PGDATA}/global/2964
这里,将所有的pg_hint_plan
字符串都替换成等长的^@
二进制零字符即可。当然如果不在乎原来的配置,更省事的做法是直接把这个文件截断成零长文件。
重启数据库,终于又能连接上了。
复现
这个问题复现起来也非常简单,初始化一个新数据库实例
initdb -D /pg/test -U postgres && pg_ctl -D /pg/test start然后执行以下语句,就可以体会这种酸爽了。
psql postgres postgres -c 'ALTER ROLE postgres SET session_preload_libraries = pg_hint_plan;'教训
安装扩展后,一定要先验证扩展本身可以正常工作,再启用扩展
凡事留一线,日后好相见:一个紧急备用的纯洁的su,或者一个无污染的可连接数据库,都不至于这么麻烦。