[译]逻辑复制的Tablesync workers

2022-06-21 15:52:35 浏览数 (1)

逻辑复制的Tablesync workers

富士通的OSS团队和其他OSS社区成员合作,一直在贡献代码增强PG的逻辑复制功能。

逻辑复制的PUBLISHER/SUBSCRIBER模型设计的基础是如何使用一个后台进程完成订阅功能。本文介绍订阅进程的一些背景知识以及我们对Tablesync进程做的一些增强。我们在这方面所做的大部分工作都不是面向用户的;有必要提供一些背景信息,以便可以在上下文中描述我们的更改。

Workers

两种后台进程执行SUBSCRIPTION复制:(Apply Worker)回放进程和一个或多个Tablesync进程。

CREATE SUBSCRIPTION命令发启一个回放进程,回放进程然后发启N个Tablesync进程(SUBSCRIPTION订阅一个或多个表)。

回放进程长期存在,直到drop掉该SUBSCRIPTION。Tablesync进程是瞬时的,每个Tablesync进程尽在相关表的synchronization阶段存在。

Tablesync进程的注意目的是通过复制已发布表(CREATE SUBSCRIPTION时)的所有行来初始化复制表。

Tablesync进程和回放进程都能够接收和处理挂起的复制协议消息--例如,用于订阅端复制CRUD(CREATEREADUPDATEDELETE)的消息。

通常情况下,Tablesync进程完成之前,你不会足够快观察它。但是,观察到的话,下面就是它的样子:订阅多个表,walsender是发布节点,replication workers是订阅节点。

Tablesync状态

复制消息可能会持续到达,即使在Tablesync进程正在发启和copy时。所有的进程都在提供包含相同复制消息的不同流,因此随着时间的不同Tablesync进程可能会落后或者领先回放进程。为了避免对同一消息进行双重处理,回放进程和Tablesync进程通过states来同步自己。

回放进程和Tablesync进程使用states进程间通信

1)STATE_FINISHEDCOPY是PG14引入的

2)Tablesync States的一些状态在共享内存中(SYNCWAIT,CATCHUP)其他的在系统表。可以查询pg_subscription_rel系统表查看每个订阅表的Tablesync状态(srsubstate)

下面是5个订阅表。2个TableScync进程仍在复制数据时捕获的状态:

代码语言:javascript复制
postgres=# SELECT * FROM pg_subscription_rel;
 srsubid | srrelid | srsubstate | srsublsn
--------- --------- ------------ ------------
   16571 |   16385 | d          |
   16571 |   16403 | d          |
   16571 |   16412 | r          | 0/11A31128
   16571 |   16421 | r          | 0/11A2F350
   16571 |   16394 | r          | 0/11A2F510
(5 rows)

上面的输出显示前 2 个 Tablesync Worker 仍在复制数据,3 个 Tablesync Worker 已完成。

状态码如下:

Code

State

i

STATE_INIT

d

STATE_DATASYNC

f

STATE_FINISHEDCOPY (see note 1 above)

w

STATE_SYNCWAIT

c

STATE_CATCHUP

s

STATE_SYNCDONE

r

STATE_READY

copy_data = false选项

Tablesync进程的主要目的是用于初始表数据的COPY和同步。但是这个是可选的,如果使用copy_data=false选项创建SUBSCRIPTION,则跳过所有的复制。这种情况下,Tablesync进程的启动状态已经设置为STATE_READY--这将导致Tablesync进程立即退出。

Tablesync错误

Tablesync进程工作过程中如果遇到错误(例如在DATASYNC阶段可能存在主键违规数据),那么Tablesync进程会记录错误并退出。

回放进程知道所有未到达STATE_READY的已订阅表,因此稍后将检测到丢失的Tablesync进程,并(乐观的)重新启动另一个替换它。

如果同样的(或任何)错误再次发送,那么这个替换的进程也将失败,另一个重新启动的Tablesync进程将继续取代它。这个循环直到:

1) 问题原因已解决,以便Tablesync进程可以完成而不会出错。

2) 问题表从SUBSCRIPTION中删除。

Tablesync增强

富士通 OSS 团队正在与开源社区合作,以增强 PostgreSQL 的逻辑复制。我们为 Tablesync Worker 做出的一些改进包括:

1) 永久复制槽和来源跟踪

逻辑复制槽是 Postgres 用来跟踪哪些主WAL文件需要为SUBSCRIPTION的WALsender保留的机制.每个槽都表示以原server上的顺序产生的流来回放。插槽还保存有关该流中当前位置的信息。每个 Tablesync Worker 都有一个关联的复制槽。这些以前是临时插槽,仅在每个 Tablesync Worker 的生命周期内存储在内存中。如果 Tablesync Worker 意外崩溃,则插槽丢失,当替换 Tablesync Worker 启动时,它将从新创建的临时插槽开始,然后再次重复所有操作,就像第一次看到一样。

Tablesync Worker 已修改为现在使用永久槽而不是临时槽。复制源信息保存在槽中,用于跟踪已复制的数据,因此通过使用永久槽,这意味着在崩溃/重新启动后,现在可以从最近记录的检查点再次获取复制。

槽的名称:pg_<subscription OID>_sync_<table relid>_<system id>

例如:pg_16571_sync_16403_6992073143355919431

一个新的状态 - STATE_FINISHEDCOPY

初始数据复制阶段可能非常昂贵。对于默认情况(copy_data = true),Tablesync Worker 会复制所有表数据。这可能是 GB 的数据,可能需要很长时间才能完成。如上所述,如果 Tablesync Worker 中发生任何错误,则会重新启动一个新的 Tablesync Worker 以替换它。以前,这意味着重新启动的 Tablesync Worker 将从 DATASYNC 状态再次启动,因此 COPY 将再次发生。

引入了新的 Tablesync 状态 STATE_FINISHEDCOPY。当复制阶段完成并提交表数据时分配此状态。因为 Tablesync 的 Slot 现在是永久的,所以 在这个提交检查点也会更新源信息。现在,在设置完 FINISHEDCOPY 状态后,如果发生任何后续错误导致 Tablesync Worker 重新启动,代码逻辑知道(昂贵的)复制步骤已经完成——它不会重复它,因为复制重新开始从最后一个已知的起源。

多事务支持

以前,Tablesync Workers 完全在单个事务中运行,该事务要么提交,要么不提交,具体取决于是否发生任何错误。Tablesync Worker 已得到增强以支持多个事务:

(1) 现在初始复制部分 (DATASYNC-FINISHEDCOPY) 在一个事务中运行。

(2) 现在复制消息处理部分在它们自己的事务中运行(这使得 Tablesync Worker 对复制消息的处理与 Apply Worker 相同)

结合新的 FINISHEDCOPY 状态,这意味着复制部分可以独立提交。此外,由于复制源 跟踪记录在永久槽中,这意味着可以跳过任何已经提交的数据。

杂项改进

富士通还在 PostgreSQL 逻辑复制领域贡献了许多其他错误修复和小改进,我们定期参与对其他贡献补丁的审查。以下是我们在其他人的帮助下编写的更多 PostgreSQL 14 更改:

1)重命名逻辑复制全局“wrconn”

2)改进一些与复制相关的错误消息的样式

3)修复stream_cleanup_files中的悬空指针引用

4)澄清tablesync.c中的注释

5)修复同一个表的多个复制截断的死锁

6)在更多地方使用Enums进行逻辑复制消息类型

好处

对 Tablesync Worker 所做的改进有助于进行逻辑复制:

1) 在失败的情况下更强大

2) 更高效(对于能够避免昂贵的表重新COPY(如果已经提交)的场景)

3) 更一致(多事务逻辑与 Apply Worker 相同)

4) 更稳定(通过错误修复)

原文

https://www.postgresql.fastware.com/blog/logical-replication-tablesync-workers

0 人点赞