PAYATHON 2026

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 依赖的兼容性应当最先排查。

备注:内容仅供参考。