CDC
订阅来自 db.cdc.postgres 和 db.cdc.sqlite 源的变更数据捕获(Change Data Capture)流。列出已配置的源、打开一个流,并通过通道接收行级变更事件。该 API 与驱动无关:两种 kind 返回相同的源信息和相同的变更事件,仅在它们发布的能力上有所不同。
加载
local cdc = require("cdc")
list_sources
列出调用者被允许看到的已配置 CDC 源:
local sources, err = cdc.list_sources()
for _, s in ipairs(sources) do
print(s.id, s.kind, s.state, s.capabilities.before_images)
end
调用者不具备 cdc.source 权限的源会被省略,而不是作为错误报告。
返回: table, error
source
按名称(其条目 ID)获取单个源:
local info, err = cdc.source("app:pg_cdc")
if info == nil then
-- 没有这样的源
end
返回: table, error(源信息,未找到时为 nil)
stream
在某个源上打开变更流。返回一个 cdc.Stream,其通道传递变更事件:
local stream, err = cdc.stream("app:pg_cdc", {
tables = { "public.users", "public.orders" },
ops = { "insert", "update" },
buffer = 128,
})
| 参数 | 类型 | 默认值 | 描述 |
|---|---|---|---|
name |
string | 必填 | 源名称(条目 ID) |
opts.tables |
[]string | - | 过滤为这些表(省略则为所有被捕获的表) |
opts.ops |
[]string | - | 过滤为这些操作:insert、update、delete、truncate |
opts.buffer |
int | 64 | 积压条目容量(1-65536) |
opts.max_bytes |
int | 1048576 | 该订阅者的积压字节预算(1 MiB) |
opts.snapshot |
bool | 条目默认值 | 为该流请求快照/实时交接 |
opts.after |
string | - | 来自前一个事件 cursor 的不透明恢复游标 |
未知的选项键会以 errors.INVALID 被拒绝。表名会不区分大小写地同时与限定关系名和裸表名匹配。快照行仅按 tables 过滤;ops 适用于实时变更。
当 opts.snapshot 为 true 或源条目的 snapshot 字段被设置时,流会收到一个快照;快照行先以 op = "snapshot" 到达,随后流无缝地继续进入实时变更。opts.after 保留给能够从游标恢复的驱动——目前发布的每个驱动对它都返回 errors.INVALID("cdc operation is not supported by this source"),包括报告了 capture_resume 的 db.cdc.postgres。
过滤器只会收窄投递范围。对源的访问由 cdc.subscribe 权限授予,绝不由过滤器授予。
返回: Stream, error
Stream 方法
channel
返回接收变更事件的通道。第一次调用会订阅该源(会让出);后续调用返回同一个通道。:receive() 会阻塞直到下一个变更到达,或在流结束时返回 nil:
local stream = cdc.stream("app:pg_cdc")
local ch = stream:channel()
while true do
local change = ch:receive()
if change == nil then break end -- 流已关闭
if change.op == "snapshot" then
seed_row(change.table, change.after)
elseif change.op == "insert" then
handle_new_user(change.table, change.after)
elseif change.op == "update" then
handle_update(change.table, change.before, change.after)
elseif change.op == "delete" then
handle_delete(change.table, change.before)
end
end
流是惰性的:先构造它,然后在生成它应观察的写入之前调用 channel()。这是实时观察,而不是重放订阅之前所做的变更。
当源以失败终止一个流时,通道会在关闭之前投递一个错误值。receive 是 channel 的别名。
close
停止订阅并释放该流。幂等;在任务作用域结束时也会自动关闭。release 是 close 的别名。
stream:close()
变更事件
在通道上收到的每条消息都是一个变更表:
| 字段 | 描述 |
|---|---|
op |
操作:insert、update、delete、snapshot 或 truncate |
schema |
表 schema |
table |
表名 |
relation |
限定关系名 |
before |
变更之前的行状态(update、delete)。只有当源具备 before_images 能力时才保证是完整的行镜像;db.cdc.postgres 用 WAL 中携带的旧元组来填充它,而这由表的 REPLICA IDENTITY 控制 |
after |
变更之后的行状态(insert、update、snapshot;delete 时不存在) |
source |
源条目 ID |
source_id |
源条目 ID,作为注册表 ID |
generation |
产生该事件的源代次 |
cursor |
源内不透明的每事件位置 |
transaction |
事务标识符,当驱动报告时 |
lsn |
变更的日志序列号(db.cdc.postgres) |
commit_lsn |
提交事务的 LSN(适用时) |
xid |
事务 ID(适用时) |
unchanged |
值未被传输的列(未变更的 TOAST 值) |
error |
事件上携带的驱动报告的错误描述 |
before 和 after 是以列名为键的行映射。
源信息
cdc.source 和 cdc.list_sources 的每个条目返回同样的记录:
| 字段 | 描述 |
|---|---|
id |
条目 ID |
kind |
db.cdc.postgres 或 db.cdc.sqlite |
name |
源名称(条目 ID) |
state |
unknown、starting、running、faulted 或 stopped |
generation |
当前源代次 |
epoch |
与 generation 相同的值 |
engine |
引擎名称,当驱动报告时 |
db_resource |
被观察的 SQL 资源条目 ID(db.cdc.sqlite) |
slot |
复制槽名称(db.cdc.postgres) |
publication |
Postgres publication,当配置时 |
tables |
被捕获的表,当配置时 |
streaming |
db.cdc.sqlite:源是否正在运行;db.cdc.postgres:条目的 streaming 协议设置 |
failover |
故障转移槽模式(db.cdc.postgres) |
temporary |
临时槽(db.cdc.postgres) |
snapshot |
条目级快照默认值 |
faulted |
源是否处于 faulted 状态 |
error |
最近一次源错误,当有记录时 |
admission |
active、snapshots、reserved_bytes、rejected |
capabilities |
snapshot、capture_resume、replayable、captures_external_writes、before_images、coalesced |
请基于 capabilities 而不是 kind 来分支:
local info = cdc.source("app:changes")
if not info.capabilities.before_images then
-- before 不保证是完整的行镜像;请自行保留最后已知状态
end
字段语义参见 CDC 源。
权限
| 操作 | 资源 | 描述 |
|---|---|---|
cdc.source |
源条目 ID | cdc.source;同时过滤 cdc.list_sources |
cdc.subscribe |
源条目 ID | cdc.stream,在订阅建立时会再次检查 |
被拒绝的操作返回 errors.PERMISSION_DENIED。
错误
| 条件 | 类型 |
|---|---|
| 没有上下文 | errors.INTERNAL |
| 需要源名称 | errors.INVALID |
| 无效或未知的流选项 | errors.INVALID |
在没有 capture_resume 的源上使用 after |
errors.INVALID |
| 源未注册 | errors.NOT_FOUND |
| 源未启动或正在被替换 | errors.UNAVAILABLE |
| 订阅容量耗尽 | errors.UNAVAILABLE |
| 权限被拒绝 | errors.PERMISSION_DENIED |
参见错误处理了解错误处理方法。