PostgreSQL 导致 Sails DEV mode 加载 orm hook 超时
结论
本地安装的 Sails 0.12.14 与全局安装的 Sails 1.5.3 可能造成命令行入口混淆,但一般不会直接引发 ORM hook 超时。启动时只要明确使用项目内的 Sails,加载的就仍是本地依赖。
Node.js 版本应当优先排查。Sails 0.12.14 和 sails-postgresql 0.12.2 都属于较早的依赖体系,Node.js 21.1.0 则晚得多。旧版 Waterline、PostgreSQL 驱动及其传递依赖可能无法在 Node.js 21 上正常运行。有时底层只表现为初始化回调未执行,最后由 Sails 报出:
The hook 'orm' is taking too long to load.
数据库不可达、认证方式不兼容或 SSL 配置错误,也可能让 ORM 卡在连接阶段。增加 _hookTimeout 只会推迟报错,无法解决这些问题。
确认实际使用的 Sails 版本
先分别检查全局版本、项目内版本和相关依赖:
sails --version
./node_modules/.bin/sails --version
npm ls sails sails-postgresql waterline pg
启动时明确调用项目内的命令:
./node_modules/.bin/sails lift --verbose
也可以使用:
npx --no-install sails lift --verbose
如果两条版本命令的输出不同,直接执行 sails lift 时就可能调用了全局 CLI。先绕过全局版本,确认问题是否仍然存在,不必急着卸载全局 Sails。
同时检查 package.json 中的启动脚本。npm script 会优先调用 node_modules/.bin 下的本地命令,例如:
{
"scripts": {
"dev": "sails lift"
}
}
这种情况下可以运行:
npm run dev
不要只把同一项目中的 Sails 升级到 1.x,同时保留 sails-postgresql 0.12.x。Sails 1 使用新的 datastore 配置体系,需要完整迁移,不能只替换 CLI 或单个依赖。
使用旧版 Node.js 验证兼容性
先尝试用项目创建时期对应的 Node.js 版本复现。具体版本应根据项目的 package.json、锁文件、部署记录或原始文档确定,不能只凭 Sails 版本推断。
如果项目没有相关记录,可以先在隔离的开发环境中试用较早的 Node.js LTS 版本。例如通过 nvm 安装:
nvm install 8
nvm use 8
node --version
npm --version
切换 Node.js 后,需要重新安装原生依赖和传递依赖。操作前保留现有锁文件:
rm -rf node_modules
npm install
./node_modules/.bin/sails lift --verbose
Node.js 8 及同时期的 npm 已停止维护,不适合直接作为长期生产环境。这里使用旧版本,主要是为了确认超时是否由运行时不兼容引起。长期运行的项目仍应规划升级到受支持的 Node.js、Sails 和数据库适配器版本。
如果换到旧版 Node.js 后可以正常启动,问题通常出在运行时或传递依赖的兼容性上,model 定义和 PostgreSQL 响应速度的嫌疑相对较小。
绕过 Sails 测试 PostgreSQL 连接
先使用与 Sails 完全相同的地址和端口,确认 PostgreSQL 是否可达:
pg_isready -h 127.0.0.1 -p 5432
再使用相同的用户名和数据库名称实际登录:
psql -h 127.0.0.1 -p 5432 -U app_user -d app_database -W
建议先使用 127.0.0.1,避免使用 localhost。某些系统会把 localhost 解析为 IPv6 地址 ::1,而 PostgreSQL 可能只监听 IPv4。
如果数据库运行在 Docker、虚拟机或其他服务器上,还需要检查:
host是否指向正确的容器或服务器地址。- PostgreSQL 是否监听外部地址。
pg_hba.conf是否允许当前客户端地址和用户连接。- 5432 端口是否被防火墙或安全组拦截。
- 用户是否有目标数据库的连接权限。
- 连接数是否达到
max_connections。 - 服务端是否要求 SSL。
- 服务端是否采用旧版
pg驱动无法处理的认证方式。
SCRAM 认证尤其值得检查。PostgreSQL 12 支持 SCRAM,但旧版 pg 驱动未必支持。如果服务器或用户密码配置为 SCRAM,而项目依赖的 pg 较旧,认证可能失败。判断时应结合 npm ls pg 的结果和 PostgreSQL 日志,不能只看 Sails 报出的 hook 超时。
检查 Sails 0.12 的连接配置
Sails 0.12 通常在 config/connections.js 中配置连接。Sails 1 使用的 config/datastores.js 不适用于这一版本。
示例:
module.exports.connections = {
postgres: {
adapter: 'sails-postgresql',
host: '127.0.0.1',
port: 5432,
user: 'app_user',
password: process.env.DB_PASSWORD,
database: 'app_database'
}
};
config/models.js 中的连接名称必须与之对应:
module.exports.models = {
connection: 'postgres',
migrate: 'safe'
};
常见错误包括:
connection名称与connections.js中的键名不一致。- 在 Sails 0.12 中使用 Sails 1 的
datastore、default或config/datastores.js写法。 - 环境变量没有加载,导致
host、user或password实际为undefined。 - 开发环境配置覆盖了公共配置。
config/local.js或环境变量中仍保留旧的数据库地址。- SSL 参数与 PostgreSQL 服务端的要求不一致。
可以暂时打印脱敏后的配置,确认实际值是否符合预期。不要输出密码:
console.log({
host: sails.config.connections.postgres.host,
port: sails.config.connections.postgres.port,
user: sails.config.connections.postgres.user,
database: sails.config.connections.postgres.database
});
schema: false 不会关闭 ORM
如果修改的是 config/models.js 中的以下配置:
module.exports.models = {
schema: false
};
它只会改变 Waterline 处理 schema 的方式,不会阻止 ORM hook 初始化,也不会跳过 PostgreSQL 连接,所以无法解决连接阶段的超时。
如果要确认问题是否出在 ORM,可以临时禁用 ORM hook。不同 Sails 0.12 项目的配置方式可能略有差异,常见做法是在 .sailsrc 中加入:
{
"hooks": {
"orm": false
}
}
如果禁用后能够启动,可以确定阻塞发生在 ORM 初始化路径中。此时应用无法正常使用 models,因此该方法只用于诊断。
检查依赖树是否意外升级
老项目如果没有可靠的锁文件,重新执行 npm install 可能会安装与项目当年不同的传递依赖。先查看当前的依赖树:
npm ls sails sails-hook-orm waterline sails-postgresql pg
重点检查:
- 是否出现
invalid、extraneous或未满足的 peer dependency。 - 是否同时安装了多份 Waterline 或
pg。 package-lock.json是否由 npm 10 重新生成。- 版本声明是否使用了范围过宽的
^或*。 - 当前安装结果是否与正常运行的环境一致。
反复清理 npm 缓存无法有效判断兼容性。缓存通常不会改变已经解析出的依赖关系,更应关注锁文件、Node.js 版本和实际依赖树。
查看底层错误
hook timeout 往往是最后出现的错误,真正的连接异常可能更早就已写入日志。使用详细日志启动:
./node_modules/.bin/sails lift --verbose
同时查看 PostgreSQL 服务端日志。如果 Sails 发起过连接,服务端通常会记录认证失败、数据库不存在、SSL 不匹配或 pg_hba.conf 拒绝等信息。如果 PostgreSQL 完全没有收到连接,则需要检查地址解析、端口、网络和 Node.js 驱动。
也可以暂时缩小 model 范围,但连接测试应当先完成。确认 PostgreSQL 可以连接、运行时兼容且依赖树正常后,再逐个检查 model 的关联、属性类型和初始化代码。
_hookTimeout 只适合初始化确实较慢的情况
如需临时增加超时时间,可以在 config/orm.js 中设置:
module.exports.orm = {
_hookTimeout: 60000
};
只有在数据库可以正常连接,而初始化确实超过 20 秒时,这项设置才有意义。如果底层驱动没有执行 callback,改成 60 秒或更长也只是让相同的错误晚些出现。
结合当前版本,建议先强制使用本地 Sails,再切换到与项目年代相符的 Node.js 并重新安装依赖。随后用 psql 验证同一组连接参数,再检查认证方式、SSL、依赖树和 Sails 0.12 配置。Node.js 21 与这套旧版 Sails 依赖的兼容性应当最先排查。
备注:内容仅供参考。
