
# 分布式GTM-LITE容灾读
分布式GTM-LITE容灾读的实现原理与分布式备机读机制基本一致。
启用auto_csn_barrier参数后，系统进行barrier打点。在容灾读场景下，收集线程除持续向备DN节点收集barrier点的CSN外，还会对包括自身的每个CN节点发起请求，将每个CN视为独立分片参与校验。该机制要求至少存在一个CN节点能获取有效CSN值，最终计算出全局一致性的CSN保存至GTM中。
查询执行阶段，当所有备机节点满足一致性点时，系统优先选择级联备DN节点下发请求。级联备DN节点通过获取备机读的一致性点CSN值，基于csn-lsn info中查询对应LSN记录，最终将查询结果返回至CN节点。
分布式读机制依赖pgxc_node表维护的节点信息进行CN本地通信列表初始化。在容灾场景中，由于灾备集群的CN是主集群CN的备机，CN节点通过回放主集群pgxc_node表数据构建的，无法自主更新表中内容。因此主集群的pgxc_node表需额外记录灾备集群所有节点的静态信息。灾备集群的内部业务在访问系统表pgxc_node时，将通过GUC参数cluster_name（记录本集群的集群名）对表中的cluster_name字段进行过滤，筛选出本集群的节点信息。
#### 特性约束
- 容灾机制类约束：
  - 容灾集群不支持COPY TO操作，由于容灾集群的CN是备机，不能分配XID，COPY TO在CN上需要申请XID，故无法执行。执行时报错（ERROR: cannot assign TransactionIds during recovery）。
  
  - 分布式容灾单副本模式，不支持分布式读操作，可以使用维护模式进行连接执行本地查询。执行分布式查询时会出现报错（ERROR: Currently disaster cluster read is not supported）。
  
  - 如果主集群与容灾集群之间出现断连，那么从容灾集群上查询到的数据可能是旧数据。
  
  - GTM-Lite方案，在CN与DN连接时，需要根据当前全局一致性点，计算出对应分片中，可用的DN副本，以下场景可能出现报错：Current slice cannot select a normal standby for standby read, slice: 1
    - 同一副本中，如果出现两个DN回放快慢不一致，且更快的DN出现重启时。处理方法：此时全局一致性点不会再推进，等待回放慢的分片跟上全局一致性点，报错消失
    
    - 容灾映射刚完成构建，但还未收集到各个CN、DN的回放、回收信息时。处理方法：仅启动时间窗会短暂出现，等待几秒钟后不再出现。
    
    - 极致RTO模式下，如果出现不同分片回放差异很大，部分回放快的分片回收点超过全局一致性点时。报错后处理方法：等待业务高峰通过，各分片回放差异减小后，报错消失，如果仅有个别CN回放很慢，可以通过cm_ctl stop命令，手工停止CN规避此问题。
     
  
  - 当灾备集群出现CN剔除、CN加回、版本升级至506版本升级提交时，会对依赖pgxc_node表通信缓存产生影响，此时会触发内部的reload机制，reload期间（时间窗3s左右）内会出现以下已知报错：
    - node id %d is in pgxc_group but not in the handles, maybe rebuilding DisasterCache. 根因：缓存中pgxc_group与缓存中通信dn矩阵中记录的OID不匹配。
    
    - cache lookup failed for node %u. 根因：pgxc_node表中的oid，与缓存中记录的节点信息oid不匹配。
    
    - Cannot reload pool when in a transaction block. 根因：会话在事务中，检测发现pgxc_node更新，但此处事务不支持reload pool，此处会报错，但不会持续报错。
    
    - pooler: shmemNums does not match with usessNums, pgxc_node reload failed. 根因：通信缓存agent中记录的备机节点个数是旧的，但会话内存变量记录的备机节点个数，已经随pgxc_node表的更新而刷新了。
     
  
  - 因临时表、临时视图、临时序列和unlogged表，不产生Xlog，也不会同步到备机，所以备机读不支持查询此类对象，查询会报错：
    - ERROR: Temporary or unlogged table cannot be accessed on the standby.
     
   

- 回放查询冲突类约束：
  - 主集群频繁vacuum、频繁DDL会影响日志回放速度，回放机制本身可能出现查询与回放冲突，回放日志时，如果持有冲突的会话，已经执行超过max_standby_streaming_delay的时间，会主动结束查询，返回错误：ERROR: canceling statement due to conflict with recovery / ERROR: terminating connection due to conflict with recovery。
   
- 快照类约束： 串并行回放容灾读：
  - 主集群做DDL操作，备集群回放DDL日志的时候，DDL在备集群上的某些节点中可能还没回放成功，校验快照失败，报错并结束查询（ERROR:current snapshot is invalid for this partition/relation）。
  
  - 主集群频繁vacuum会导致灾备集群查询的数据被提前清理掉等问题，读会话会检查快照是否大于DN最新的回放查询冲突点，不满足则说明此快照已经太旧了，不能读最新数据，主动报错并结束查询（ERROR: gtm csn small: gtm csn 123456789, lastReplayedConflictCSN 123456999 ）。
  
  
  极致RTO容灾读：
  - 由于磁盘空间不足、修改GUC参数触发了强制回收，或发生过启停，或某一分片回放慢导致全局一致性点长时间推进缓慢，均会导致正常回放的CN/DN，由于本地历史版本不支持当前一致性点而不可读，报错结束查询（ERROR：snapshot csn is not in csn-lsn list）。
  
  - 系统表（relfilenode\<16384）不会处理延迟DDL。在涉及系统表的TRUNCATE或DROP操作时，相关文件将被直接删除，包括极致RTO历史版本。在此类场景下，备机读操作可能因无法定位历史版本而报错："ERROR: standby_read_buf can't find history version page/ERROR: block old version can not found"。
  
  - 段页式表，均不会进行延迟DDL处理，所以在段页式表触发TRUNCATE、DROP、REINDEX类的DDL操作时，备机回放会直接删除原数据文件，导致读段页式表0号页面报错。
   
- 性能约束
  - 当主备集群查询所能占用到的资源相同时，对于并行回放下的Astore表：
    - 在主集群无写业务、备集群的日志都回放完成的场景下，对于select count(\*) from xxx语句的负载，从时延的维度上观察，备集群的查询性能达到主集群的80%。
    
    - 在带有写业务的场景下：主集群和容灾集群的查询性能可能都会受业务影响而有波动，单次查询不保证容灾集群的性能达到主集群的80%；在无DDL的场景下，对于sysbench和TPCC的负载，从TPS、第95百分位数和平均时延的维度上观察，容灾集群的性能达到主集群的80%。
    
    - 如果有多个查询的主节点或备节点部署在同一个机器上，因这个机器的资源不足导致查询性能下降的情况下，则上面两个场景不成立。
    
    - 当发现前两个场景不成立时，建议检查容灾集群中各节点是否存在资源（CPU、内存、磁盘）受限的情况。
    
    - 由于容灾集群在并行回放下的Astore表不支持index only scan查询，因而对于select count(\*) from xxx语句的负载可能需要读取更多的数据页面，建议通过调整shared_buffers参数进行缓存优化。
     
   
- 兼容性场景约束 当升级版本升级至506版本时，升级观察期期间，会使用旧版本映射方案处理容灾读通信列表，此时有相关约束：
  - 容灾集群上pgxc_node表的内容是同步自主集群的，所以容灾集群的CN会根据pgxc_node表中的node_oid与真实的节点信息做一层映射，但主备集群的node_name不是完全对应的，这会导致通过EXECUTEDIRECT ON语法实现的视图在容灾集群上的查询结果出现异常（结果重复或缺失），建议直接在对应的节点上查询。
  
  - 主备集群异构、CN数量不一致时，进行灾备集群读之前，应先查询出与主集群存在映射关系的灾备集群CN，再根据查询出的CN进行相关业务操作（查询方式：SELECT \* FROM pgxc_disaster_read_status()）。如果连接到和主集群不存在映射关系的CN，由于此CN自身的一致性点不参与全局一致性点的计算，可能会出现连接报错（ERROR: cn data invisible: local csn 123456789, gtm snapshotcsn 123456999）。
  
  - 如果使用JDBC连接容灾集群，且开启了负载均衡功能，那么查询请求会发送到主集群上，导致查询性能受到影响。
  
  - 重启cn容灾搭建完成后，因容灾读本集群拓扑节点信息，依赖pgxc_node表回放后内部映射机制的构建。在容灾映射构建完成前，读请求会出现报错：Not support disaster read before init. 容灾集群关系搭建完成，灾备集群正常启动后，10s左右（主集群、灾备集群之间无RTO差距）此读报错消失。
  
  - 容灾集群由于pgxc_node表，是通过回放记录主集群的拓扑信息，如果出现，1）双集群集群间倒换，2）1：N主备集群部署场景下，主备集群倒换，3）主集群节点替换、CN剔除等，主集群拓扑结构变更导致灾备pgxc_node表被动更新时，由于分布式查询CN中使用的部分系统缓存，通过pgxc_node表中的记录oid作为key，当线程读到新的pgxc_node表信息，但系统缓存还未来得及更新时，此时间窗（3s左右）内会出现以下已知报错：
    - node id %d is in pgxc_group but not in the handles, maybe rebuilding DisasterCache. 根因：缓存中pgxc_group与缓存中通信dn矩阵中记录的OID不匹配。
    
    - cache lookup failed for node %u. 根因：pgxc_node表中的oid，与缓存中记录的节点信息oid不匹配。
    
    - Cannot reload pool when in a transaction block. 根因：会话在事务中，检测发现pgxc_node更新，但此处事务不支持reload pool，此处会报错，但不会持续报错。
    
    - pooler: shmemNums does not match with usessNums, pgxc_node reload failed. 根因：通信缓存agent中记录的备机节点个数是旧的，但会话内存变量记录的备机节点个数，已经随pgxc_node表的更新而刷新了。
     
   

- GTM-Lite备机读类约束 GTM-Lite容灾读与备机读使用相同的机制处理读逻辑，也会继承 [分布式GTM-Lite支持备机读的规格与约束](https://support.huaweicloud.com/distributed-devg-v10-gaussdb/gaussdb-12-0835.html#ZH-CN_TOPIC_0000002590360192__li45131184516)。
  
 
