@dbx-tools/postgres

中文文档
0.9.206发布于 2026/11/11Apache-2.0TypeScriptES Module构建来源已验证近 30 天下载 1.2万

连接正确的 PostgreSQL 咨询锁和 optEN/NOTIFY 节点主题总线

中文使用文档
文档目录15 节
  1. 快开始
  2. 能力
  3. 会议作用
  4. 与 AppKit Lakebase 一同使用
  5. 咨询锁
  6. 主题总线
  7. 信件信封
  8. 元数据优先
  9. 支持的信息值
  10. 连接、生命周期和失败
  11. 重放为 persist
  12. 信封或指针
  13. 命名频道
  14. 连接寿命周期
  15. 模块映射

@dbx-工具/邮递

使用连接正确的 PostgreSQL 咨询锁和结构化 LISTEN编号 :NOTIFY 主题总线来自Node.js. 软件包还提供了可选 消息持续性、发送者身份和连接帮助者.

都与平地为敌 pg.Pool 或任何结构上与一个, 包括 AppKit 的 Lakebase 插件导出, 因此一个 Databricks App 获得 它们没有第二个数据库客户端或连接池.

快开始

在持有锁的 PostgreSQL 连接上运行受保护的工作 :

import { postgresConnectionOptions, withAdvisoryLock } from "@dbx-tools/postgres";
import { Pool } from "pg";

const pool = new Pool(postgresConnectionOptions({}));
const invoiceId = 42;

await withAdvisoryLock(pool, ["invoice", invoiceId], async (client) => {
  await client.query("UPDATE invoices SET status = 'sent' WHERE id = $1", [invoiceId]);
});

能力

  • 转换任意结构键(字符串、阵列、对象、, Date,或一个 明确 bigint)成为稳定签名的64位咨询锁标识符.
  • 从任何识别频道中获取合法的 Postgres 频道名称,所以不 调用网站必须将应用程序名称或租户身份标识进行消毒.
  • 对整个召回的专用集合连接有一个会话锁定, 并且把连接到回调器上 所以保护的活在它上.
  • 持有解析释放的交易锁定 COMMIT 或者说 ROLLBACK,这是 一次性安装需要什么.
  • 广播a type编号 :metadata编号 :body 每个监听过程的封装 pg_notify,按正在处理的专题过滤.
  • 填充工程、机器、进程、部署和可选的 AppKit 发送器 上下文,调用元数据总是获胜.
  • 拒绝不会在JSON往返中幸存的有效载荷 电话站而不是电线上.
  • 重新连接一个已丢弃的收听器, 用户仍保留时, 设定了后退 .
  • 可选地坚持每条已发布的消息到一个软件包的表格,所以 未收听的订阅者可以通过光标重放历史, 默认 TTL和清理在插入.
  • 两种有效载荷样式:通知中的整个信封,或指向 听者读回. 指针抬起八千字节 NOTIFY 限制和保留 表格赠款背后的内容.

会议作用

设定 DBX_TOOLS_POSTGRES_ROLE 当每个仓库拥有的 PostgreSQL 连接时 认证后应承担一个角色. postgresConnectionOptions() 添加 作用 pg 和 AppKit Lakebase 池的启动选项,同时 postgresServerSettings() 返回等同点 asyncpg 服务器设置.

import { postgresConnectionOptions } from "@dbx-tools/postgres";
import { Pool } from "pg";

const pool = new Pool(postgresConnectionOptions({}));

明确的第二个参数压倒环境值。 帮助者保留 现有 options,包括语句超时,并附加角色设置. 使用 postgresRoleStatement() 仅用于设置工作,该工作必须在 连接已经打开 .

当这些选项配置 AppKit Lakebase 路由池时,它们适用于其 服务-主要和每个用户OBO集合。 每一个可以到达的登录身份 因此,人才库必须是所配置角色的成员,否则 权限 SET ROLE,在游泳池成功连接之前.

与 AppKit Lakebase 一同使用

使用 AppKit lakebase() 以获取和刷新 PostgreSQL 池。 这个包 不替换该插件。 当工作需要连接正确咨询时添加 锁或交叉连接 LISTEN编号 :NOTIFY 交付、能力 Lakebase 插件不提供 .

咨询锁

import { postgresConnectionOptions, withAdvisoryLock } from "@dbx-tools/postgres";
import { Pool } from "pg";

const pool = new Pool(postgresConnectionOptions({}));

await withAdvisoryLock(pool, ["invoice", invoiceId], async (client) => {
  await client.query("UPDATE invoices SET status = 'sent' WHERE id = $1", [invoiceId]);
});

运行保护工程 client 调用接收。 A 类 pool.query() 内召回可能降落 在不同的连接上, 这不是一个 拿着锁.

使用交易范围帮助一次性数据库工作 :

import { withAdvisoryTransactionLock } from "@dbx-tools/postgres";

await withAdvisoryTransactionLock(pool, { schema: "my_feature", version: 1 }, async (client) => {
  await client.query("CREATE TABLE IF NOT EXISTS my_feature.events (id bigint primary key)");
});

identity.advisoryLockId(key) 暴露与 JSON 安全小数相同的减少 绳子。 A 类 bigint 键为 直接使用( 缩到64比特) 而不是散列, 因此锁可以交互操作 和另一个发布其数字锁标识的应用程序,例如 pgmq是因为 例如安装器锁. 任何其它东西都用 object.toStableKey 从 @dbx-tools/shared-core {\fn黑体\fs22\bord1\shad0\3ahbe\4ah00\fscx67\fscy66\2chffffff\3ch808080}那么关键顺序 对象在 a 时无关紧要 1 (单位:千美元) "1" 保持不同的锁。 一个阵列是 读作若干部分,所以 ["invoice", 7] 财务报告和审定财务报表 "invoice_7" 是不同的锁。 A 类 非无限数字或循环键抛出 TypeError 而不是产生一个 两个打电话的人可能不同意.

主题总线

import { PostgresTopicBus } from "@dbx-tools/postgres";

const bus = new PostgresTopicBus(pool, {
  metadata: async () => ({ publicIp: await resolvePublicIp() }),
  onError: (cause) => logger.error("bus", { cause }),
});

const unsubscribe = await bus.listen("orders", ({ type, metadata, body }) => {
  console.log(type, metadata.project, body);
});

await bus.broadcast("orders", {
  type: "order.updated",
  metadata: { traceId: "abc-123" },
  body: { orderId: "123", status: "ready" },
});

await unsubscribe();
await bus.close();

在频道上监听的每个过程都会收到每个消息. 没有 竞争的消费者。 巴士适合每个浏览器的SSE流等现场播放 选项卡,缓存无效或存在 ping。 它不分配工作. 默认只提供现场直播。 打开 persist 当 a 订阅者需要重放,需要时使用真正的队列.

信件信封

外地来源页:1
id公共汽车每个消息在实例中都独一无二。 用来去除或作为 id:。 。 。 .
topic公共汽车这个 broadcast 主题。 其他话题的听众从不看.
type呼叫器例如事件名称 order.updated。必须的,没有空白.
metadata合并呼叫器下的自动上下文 。 见下文.
body呼叫器顺利通过.
publishedAt公共汽车ISO-8601,取自出版商的时钟,不是数据库的.

publishedAt 财务报告和审定财务报表 id 用于显示和调试。 也没有命令信件 不同的情况可靠.

元数据优先

最弱到最强:自动机器上下文,然后是AppKit发送器身份,然后是 公交车 metadata 选项,然后是每个消息 metadata键入 更坚固的地层从不被覆盖 所以通过 project 明确替换 推断出一个 明确 null 也保存并阻止自动值 从用于那个键.

自动键, 当解析为无时, 每个键都会省略 : project (从) DATABRICKS_APP_NAME, (中文(简体) ), DATABRICKS_BUNDLE_NAME, (中文(简体) ), PROJECT_NAME,或 npm_package_name, (中文(简体) ), hostname, (中文(简体) ), platform, (中文(简体) ), environment, (中文(简体) ), appName, (中文(简体) ), deploymentId,以及 databricksHost. 公共/客户IP、进程ID、CPU 架构、运行时名称和运行时版本被故意忽略: 皮条很吵(而且往往很敏感),其余的都是每次部署的常数,以及 每条信息都会按有效载荷限制支付 通过 a metadata 函数,当一个进程需要其中一个这些值时.

上面的例子将一个发现的公用IP附以这种方式. 函数运行于 每一个广播, 所以记住任何昂贵的.

与可选 @databricks/appkit 已安装端点和用户执行上下文 活动, senderId, (中文(简体) ), senderName,以及 senderEmail 还加上了。 AppKit 为 进口懒惰且不需要。 在请求之外,或者没有包裹 公交车跳过那些键.

支持的信息值

身体和元数据必须活下来 JSON.parse(JSON.stringify(x)) 不变,哪些 更为严格 JSON.stringify 不扔东西。 A 类 Date 成为弦, NaN 财务报告和审定财务报表 Infinity 变成 null页:1 Map 变成 {},以及 undefined 失踪了. 每一个都到达听众的身边, 与所发送的东西不同, 所以所有他们 掷出 TypeError 在电话中代替。 周期、功能、符号、比值和类 由于同样的原因,拒绝提交案件.

规矩在 @dbx-tools/shared-core 作为 object.isSerializableValue 财务报告和审定财务报表 object.SerializableValue,因此,一个路由可以对不可信 请求正文并回答400而不是500,浏览器侧调用器可以共享 类型.

编码后的信封被封为 7900 字节, 在 PostgreSQL 的 8000 字节下 NOTIFY 有限度的,有大势的 RangeError自动元数据计数 反之,所以广播一个参考 并让接收器获取任何重大的东西.

连接、生命周期和失败

出版每通电话借用集合连接. 听力无法 : LISTEN 实值 会话状态,所以总线会检查一个专用客户端,并保持它多久 因为它有订阅者,但许多专题和听众都登记在册。 大小 带着那长生不老的检查表去游泳.

第一次之前没有联系 listen,或明示 start() 你何时会 最好找出一个连接问题 在启动. close() 释放客户端 并且是必需的。 用主机的停机钩登记 封闭的公交车停放 关闭并投掷,而不是静默地重新连接.

当通知连接中断时,总线会自行重新连接:立即 首先,将上限从250米翻一番,无限期地翻一番 订户仍然存在。 Postgres 重启或旋转 Lakebase 证书回收 没有干预。 空隙期间发布的消息会丢失 。 每一个失败 尝试,每个中断的连接, 和每个播放者 丢弃 结束 onError,它默认吞下它们。 配置登录器 任何长期运行。 一个失败的听众从来不影响出版商或其他人 听众.

一个频道包含许多话题,所以增加一个话题并不花费什么. 给一个 不相关的高音量流 自己的频道,因为每次听 会话解码频道上的每个消息.

重放为 persist

NOTIFY 没有重放: 一个不听的过程永远不会知道它是什么 错过了。 设定 persist 并且每个已发布的消息都是相同的 交易作为其通知,因此光标可以读取历史.

const bus = new PostgresTopicBus(pool, { persist: true });

await bus.broadcast("orders", { type: "order.updated", body: { id: 7 } });

let cursor: string | undefined;
do {
  const page = await bus.history("orders", { after: cursor, limit: 100 });
  for (const { message, expiresAt } of page.messages) handle(message, expiresAt);
  cursor = page.nextCursor;
} while (cursor);

该行和通知共同承诺,这就是整个财产 设计的基础是: NOTIFY 是在承诺时而不是在发言时交付的,所以 永远不能告诉听众它还不能读的一行.

选项默认行为
schemadbx_message_busSchema用于信件的持久性.
scopeopen哪一级的表格发布,历史默认读取.
tables*_messages覆盖管理下的安装的分级表格名称.
ttl24 hours万事通过期. false 保留信件直到删除.
payloadenvelopepointer 仅以路由方式通知。 见下文.
cleanupBatchSize1000已过期的行被删除, 每一份持续出版 .
provisiontrue创建首先使用的图、表和索引.

消息 EXPIRE 默认。 24小时TTL是默认的,正因为 未绑定的消息表是此特性否则会引入的失败 . 已过期的行被删除,在每份持续出版的限定批次 cleanupExpired() 按要求进行同样的扫荡. ttl: false 选择一个总线(或 { ttl: false } 单曲 broadcast)出为已出. 虽然有,但.. 这不是默认.

两层, open 财务报告和审定财务报表 restricted,所以一个信道可以携带信息 不同的读者群:它们是单独的表格,所以一个角色被赋予了开放性 表格不能读取限制性历史. messageBusGrantStatements(roles, scope, options) 发放的补助额为: history 光标已绑定 至其所出之阶. 在限定的表格中恢复开放页面 舍去掉别人的行 发布到分级 每个消息 broadcast(topic, { scope: "restricted", ... })。 。 。 .

设定 provision: false 当移民拥有这个计划时 声明是: 在咨询交易锁下,否则适用一次,因此同时适用 在启动时安装的例不比赛 .

信封或指针

payload: "envelope" (缺省)将整个消息放入通知. 一次往返,不读放大,以PostgreSQL的有效载荷限制为界.

payload: "pointer" 仅以路线和身份通知 (单位:千美元){ v, scope, topic, id, sequence }),收听者读回信封. 这样就大了 完全限制(一个大于7900字节的身体只能以这种方式公布)和 意思是没有级表补助的订户得知出了事 没有学会什么。 费用为每份通知读取一次。 听众收到 同样的 TopicMessage 无论是哪种方式。 上面的区别是看不见的 listen。 。 。 .

已经走光的指针( TTL 清理赢得比赛) 或无法读取 提高不会产生任何效果, 使用 decodePointer 如果非公共汽车消费者需要阅读原始通知.

命名频道

channel 使用任何识别通道, 而不是一个预先保密的标识 : 姓名、房客身份证、 [env, feature] 组合,或配置对象。 一个或多个值: 一个阵列被读取为多个部分,其他任何部分被读取为一体.

new PostgresTopicBus(pool, { channel: "billing-events" }).channelName;
// "billing_events_3x55ck"
new PostgresTopicBus(pool, { channel: ["billing", "production"] }).channelName;
// "billing_production_008jgf"

部分被标记成可读的半个和短的散列 窗体( e)object.toStableKey页:1 大麻是制图的原因 信得过: 孤独的象征是迷失的,所以 my-app, (中文(简体) ), my_app,以及 myApp 将 崩溃到一个信道上,并且名称长度足以击中 Postgres 的 63 个字符 限制会与任何分享主要标志的东西相撞。 用后缀 可读部分保持可读性,不同投入保持差异,包括 ["billing", "prod"] 对 "billing_prod",结构不同.

衍生是决定 跨过程和运行, 所以每个参与者 同一航道上降落时没有协调 对象密钥 秩序无关紧要。 不同的拼写方式有 读取 bus.channelName 来看 一组部分是否同意或确认两个进程。 默认为 a 共享 dbx_tools_topic_bus 频道.

identity.channelName(parts) 不搭公交车就暴露出原因, 用于配置检查和交叉运行的合同测试.

连接寿命周期

咨询锁是连接范围,不正确的池使用可能隐蔽地失败: 带着锁 pool.query() 与他人一起从事受保护的工作 pool.query() 可以用两个不同的连接, 所以锁保护什么 代码看起来是正确的。 这同样适用于 LISTEN,即会话状态 a 集合查询无法维持 。 这些帮手将两个生命周期都保持专注 连接和仅依赖 pg。 。 。 .

AppKit供应了"湖基"游泳池. 这些帮助者添加了锁和交叉连接 在它上面发送消息.

模块映射

模块目的
advisoryLock稳定锁ID+会话-和交易范围锁帮助器.
identity跨运行时的咨询锁和频道身份政策.
topicBus专题公交和信息合同.
persistence存储信息计划 赠款 TTL清理 历史 指针线.

两个模块也被平整到软件包根上,所以 import { withAdvisoryLock, PostgresTopicBus } from "@dbx-tools/postgres" 工作内容 没有命名空间.

版本与发布记录

@dbx-tools/postgres 共发布 251 个版本,首次发布于 2026/08/03;过去 12 个月发布了 251 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.9.206latest2026/11/11今天
0.9.2052026/11/111 天前
0.9.2042026/11/101 天前
0.9.2032026/11/074 天前
0.9.2022026/11/065 天前
0.9.2012026/11/056 天前

关于 @dbx-tools/postgres 的常见问题

@dbx-tools/postgres 是什么?

连接正确的 PostgreSQL 咨询锁和 optEN/NOTIFY 节点主题总线

如何安装 @dbx-tools/postgres?

在项目目录运行 npm install @dbx-tools/postgres,或使用 pnpm add / yarn add / bun add。

@dbx-tools/postgres 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@dbx-tools/postgres 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@dbx-tools/postgres 可以商用吗?

@dbx-tools/postgres 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@dbx-tools/postgres 的下载量有多少?

根据 npm 官方下载统计,@dbx-tools/postgres 在 2026/10/12 – 2026/11/10 这 30 天内被下载 12,098 次,日均约 403 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@dbx-tools/postgres 还在维护吗?

最新版本 0.9.206 发布于 2026/11/11(今天);至今共发布 251 个版本,其中过去 12 个月发布了 251 个;首次发布于 2026/08/03。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。