SlonyBrainstorming
Slony 功能集思广益
这收集了 2010 年 11 月/12 月针对 Slony 的下一个主要版本的会议上的想法。
很可能许多这些功能 *不会* 被实现,基于“工作量大于价值”的评估。
这项工作的目的在于捕捉一套完善的想法,以便有效地评估哪些值得实现。
slon 扩展
Slon 监控
- 由 Chris Browne 提供
- slon 以可查询的形式记录它正在进行的工作。
- 需要在事件循环开始时写入 (+COMMIT)。
- [如 Yehuda 所指出的] 这也应该对 slonik 有用,可以用来指示“怎么了”?
- 错误 #175
围绕各种机制进行了辩论。唯一没有明显不可接受特性的方法是向 Slony-I 架构添加一个表。
- SNMP
- 用于网络监控的现有标准。
- 缺点 - 需要额外的基础设施,包括库,可能还需要额外的工具才能使用它。
- 更大的缺点:计划删除 SNMP 支持 - 请参阅 错误 #170
- 传播
- 快速
- 缺点 - 需要额外的基础设施,这些基础设施并不特别“标准”。
- 需要大量的编码工作才能确保有组件在监听。
- 需要相当多的努力才能确保有合适的配置。
- NOTIFY/LISTEN
- 内置于 Postgres 中 - 不需要新的基础设施。
- 在 Postgres 9.0 及更高版本中可以携带有效负载。
- 只有在 COMMIT 时才会可见。
- 结果不会存储;监听器必须预先声明它们的兴趣。
- SQL 表
- 内置于 Postgres 中 - 不需要新的基础设施。
- 只有在 COMMIT 时才会可见。
- 另一个需要管理和清理的表,但是如果我们限制存储的数据(例如 - 没有历史记录),那么维护工作量就不会很大。
监控要求
我们想要了解的关键事实。
- 复制滞后?
- 组件(例如 - slon、slonik)在做什么?
请注意,slon 具有多个线程。 - 事件的最新异常状态(例如 - 错误消息)。
- 是否有任何非 SYNC 事件未完成?
- 积压量?
- 集群的配置是什么?
复制滞后?
现有的视图 sl_status 从确认的角度捕捉到这一点。这并不完美,但并不明显的是,在这方面有很高的优先级需要增强。
组件在做什么?
没有相关内容以可用的方式被捕获。
人们认为,我们可能做的是添加一个表,每个线程都会记录 *我在做什么?*(这将替换以前所做的任何操作)。
此表将包含一个元组,用于
- 每个远程工作线程
- 清理线程
- 每个远程监听线程
- 本地 SYNC 线程
它将跟踪以下内容:
- 处理开始时间
- 我是哪个线程/进程?
- 我属于哪个节点?
- 我在做什么?
可能包含几个部分,以涵盖以下几种事实。- 事件 ID
- 事件类型
虽然这可以从 sl_event 中获取,给定节点和事件 ID - 其他事件活动
同样,可以从 sl_event 中获取,给定节点和事件 ID - 后端 PID
这将支持 slonik 场景;如果 PID 已经失效,那么活动显然已经完成(或失效!),并且可以安全地清理。
请注意,此表的内容应该非常小;每个节点上每个 slon 线程一个元组。
这还需要能够捕获 **slonik** 正在做什么;这似乎更麻烦。
- 可以有多个 slonik 实例同时运行 - 多个同时事件!
- 没有自然的“事件循环”,因此 slonik 活动预计会随着时间的推移而自行清理。
- 捕获连接的后端 PID 可以知道 slonik 进程何时完成。它们 **应该** 是自清理的,但我们必须能够应对故障,否则我们会让情况变得更糟。
建议的 slon 实现
为了建立连接以捕获这些监控数据,出现了两种方法。
- 每个线程都打开自己的数据库连接。
不可接受:导致 *大量* 增加对数据库连接的使用,而这些连接基本上处于闲置状态。 - 建立一个“监控线程”。
- 消息队列允许其他线程存储条目(包括时间戳),监控线程定期将这些条目刷新到数据库。
- 此线程可以合并到现有的本地 SYNC 线程中,该线程并不十分繁忙。但这只是性能优化。
事件的最新异常状态
这会捕获有关发生的最新问题的消息,存储
- 异常发生时间
- 事件 ID
- 节点 ID
- 描述 / 错误消息
非 SYNC 事件未完成?
此信息已经被捕获,并且可以通过运行一个查询来揭示,该查询在源节点上询问所有满足以下条件的事件。
- 不是 SYNC 事件
- 尚未被订阅者确认
积压量
这似乎很麻烦;计算涉及特定 SYNC 的 sl_log_* 元组的数量需要运行与远程工作线程相同的复杂查询,以确定哪些元组需要应用。
这是一个生成复杂的查询,并且运行起来非常昂贵。
请注意,错误 #167 正在更改此查询。
集群配置
这被捕获为 错误 #176
有一个现有的工具可以对集群配置进行一些分析;请参阅 test_slony_state.pl
最好有一个工具可以生成节点之间关系的图表,捕获
- 节点
- 订阅集,以及它们所走的路径
- 节点之间的路径
- 监听路径
对于订阅集图表,最好包含每个节点的复制状态/延迟的指示,表明以下内容:
- 事件编号
- 落后于父节点的事件
- 落后于父节点的时间
- 落后于源节点的事件
- 落后于源节点的时间
更快的复制 - COPY 协议
- 使用 COPY + 触发器在 sl_log_* 上。
- 由 Jan Wieck 提供
- 元组信息的全新编码。
- 触发器直接进行堆更新。
- 消除解析每个语句的开销。
- COPY 隐式引入流式传输。
- 消除了处理大型元组的当前内存管理逻辑的需要。
- 应该减少 slon 解析 sl_log_* 游标,生成 I/U/D 流的工作量。
- 需要将日志传输分离到一个单独的守护进程,因为已知有些用户依赖于能够将日志传输数据解析为 INSERT/UPDATE/DELETE 语句。
- 基于有限的兴趣,这目前似乎不是优先事项。
SYNC 管道
- 由 Jan Wieck 提供
- 打开到源数据库的两个连接,在处理之前的请求时开始拉取新数据,并将 I/U/D 请求推送到订阅者。
- 如果使用 COPY+触发器来流式传输数据,则可能没有必要。
- 最大价值来自以下情况:将第一行延迟到 **%f 秒** 的时间等于处理 SYNC 所需时间的剩余部分。
- 如果在 sl_log_* 上启动游标的查询占用了大部分时间,那么尽早启动下一个查询就不会获得太多收益。
- 如果处理 sl_log_* 数据的查询占用了大部分时间,那么尽早启动下一个查询也不会获得太多收益。
- 实际上会产生性能差异的情况似乎非常狭窄。
压缩 DELETE 请求序列
- 由 Chris Browne 提供
- 请注意,TRUNCATE 已经复制。
- 有两种解释方式...
- log 触发器在收到它们时会压缩它们,本质上将后续请求合并到单个 sl_log_* 元组中
这改变了对 sl_log_* 的 仅限插入 理解 - slon 远程工作线程在处理 SYNC 时会压缩它们
总体而言,这似乎是一个相当可疑的功能。
SNMP
- 由 Chris Browne 提供
- 2005 年添加了一些最小的 SNMP 功能
- 自此未经修改;需要 --with-netsnmp 选项才能在 ./Configure 中使用
- 我们应该改进它还是删除它?
- 列表线程
- 在 2010-11-30 的讨论中,有一些争论关于这是否值得增强以支持在其他地方讨论的一些监控要求
- 支持者认为,SNMP 存在并且是基于网络监控的标准协议
- 反对者观察到以下几点
- 未知当前代码是否有效
- 对于我们来说,在 Slony-I 中使用 SNMP 进行监控会导致对 SNMP 库以及可能的其他工具的依赖。
我们当然 不希望 强制安装和配置像 Nagios 这样的东西作为 Slony-I 的组件。 - 相反,请考虑我们已经到处使用 libpq;对于我们来说,在表中捕获一些监控信息不需要额外的组件。
删除提案:Bug #170
更好地处理 sl_log 积压
- 当复制出现积压时,sl_log 表可能会变得非常大。这会减慢复制速度。
- 让 slony 根据需要创建新的 sl_log_x 表。
- 查看邮件列表讨论
- 请注意,Vivek Khera 已经注意到,当他使用 SSD 而不是旋转磁盘时,这种情况发生的频率较低,因此显然磁盘性能对这种情况有重大影响。
Bug #167 有一些进一步的观察
在追赶主要积压的过程中,接收 slon 会越来越慢,有时甚至会慢到无法再跟上正在记录的新更改的速率。
这是由远程工作线程从 sl_log 中选择查询的问题引起的。日志元组的下边界是第一个快照的 min_xid 或第一个快照的 xip-向量。该 OR 子句导致规划器无法为针对 sl_log 的索引扫描创建下限扫描键。因此,它始终从开头扫描,并使用过滤条件。在某个时刻,这会变得非常昂贵,甚至会切换到 seqscan。
针对此问题的修复尝试是将那些在第一个快照时正在进行,但在第二个快照时不再进行的事务提取到另一个 UNION 中。这将允许计划为大部分日志行使用下限扫描键,并使用索引扫描的嵌套循环将来自当前 OR 子句的少数事务拉入。
-- Jan Wieck
孤立节点
- 根据 列表上的讨论
- 允许配置某些节点应被忽略以进行确认。
- 这允许清理线程修剪 sl_log_(1|2) 数据。
- FAILOVER 可能已经支持“孤立”节点可能丢失的概念。
- 一个有趣的扩展:SUBSCRIBE SET 可以自动将“子”节点标记为孤立,直到订阅完成并追赶上为止。
- 这仅在只涉及一个集合时有效;如果有多个订阅,躲避只对第一个有效。
- 对于多个订阅,它将完美运行。这里的重点是释放主节点和其他转发器(而不是正在忙于订阅的节点的数据提供者),以避免保留日志副本。
- 这需要一个更好的名字。可能性
- 孤立的
- 非依赖的
父级检查
- 在节点启动时,检查提供我订阅的节点是否认为我的节点存在
- 如果他们这样做,一切正常
- 如果他们没有,那么我可能是一个失败的节点
- 如果无法建立连接,则警告此情况(可能使用非常快的超时),但暂时继续...
使用 application_name
- 由 Chris Browne 提供
- 如果在支持 GUC application_name 的版本上,连接应该捕获“这是 slon”
- application_name 在 PG 9.0+ 上可用
- 我们使用的值可能应包含以下各种(可能是所有)内容
- slon
- slon 管理的节点的 ID
- 正在查询的辅助节点的 ID
- 可能是集群名称?
已知错误
#53 - 高 RAM 使用率,表数量多
- #53
- 可能涉及用哈希表替换数组
- 可能的 BSD 许可的哈希表实现
- C 哈希表
另请参阅 ryantenney / chashtable @ GitHub
注意:在 Xen 中使用 - UTHash
- lookup3 - 公共领域
- C 哈希表
#80 - slon 守护程序在故障转移后循环重启自身
#81 - 重复键 sl_nodelock-pkey 以及未检测到的重复 slon(8) 进程
#111 - UNSUBSCRIBE SET 取消未完成的 SUBSCRIBE SET
#126 - 连接上的客户端侧 KEEPALIVE
#137 - EXECUTE SCRIPT 未按正确顺序应用
- #137
- 将 DDL 从 sl_event 移至 sl_log_(1|2)
- 允许 DBA 在 slonik 脚本中指定 EXECUTE SCRIPT 需要哪些锁
#152 - DDL 嘈杂 - 可能通过“其他健康标准”的想法得到很好地处理
#163 - 更改为在 Slony 定义的表中使用 TIMESTAMPTZ
#166 : SYNC 的大小
- 错误 #166
- 询问特定 SYNC 涉及多少工作非常有用
- 允许评估以下问题
- 此 SYNC 非常大吗?
- slon 忙于处理巨大的 SYNC 吗?
或者,如果 SYNC 很小,长时间运行的 SYNC 表明某些东西已损坏
如果我们添加一个评估此值的存储函数,它应该允许对 src/slon/remote_worker.c 中的 C 逻辑进行重大简化,该逻辑目前执行以下操作
- 拉取两个事件的未决事务列表
- 生成一个可能非常大的 WHERE 子句,指示从 sl_log_(1|2) 中拉取元组时需要排除的事务集
- 压缩该 WHERE 子句,以防止查询过长而导致 Postgres 解析器崩溃
相反,我们想象一个函数可以执行以下操作
- 创建一个临时表来捕获事务 ID 值
- 如果该表已存在,则截断该表
- 拉取上面描述的事务列表,并将其存储在临时表中
WHERE 子句将被更改为从临时表中提取数据,而不是显式地将值写入 WHERE 子句中。这将消除 src/slon/remote_worker.c 中的大部分代码,包括
- 函数 compress_actionseq(大约 280 行代码)
- sync_helper() 的部分内容
- sync_event() 的部分内容
开放问题
- 这是一个重大节省吗?
- 反复对临时表运行 TRUNCATE 会有问题吗,每次处理一组 SYNC 事件都会运行一次?
- 我们希望在临时表上建立索引吗?
slonik 扩展
ABORT
- 最初由 Vivek Khera 提及
- 如果复制落后,则失败
- 事件计数 - “在 3 个事件内是可以的”
- 时间间隔“落后不超过 15 秒是可以的”
- 其他健康标准
- 由 Chris Browne 提供
- 节点在那里吗?
- 复制集在那里吗?
- 订阅是否处于活动状态?
- 运行 SQL,如果出现错误则失败
隐式 WAIT FOR EVENT
- 提案
- 最初由 Chris Browne 提及
- 应该跟踪(根据 Yehuda 的说法)一些状态信息,以便如果它持续等待一段时间,这对于用户或监控事物的人来说不会是一个谜。
- 需要节点间协调的 Slonik 命令应该检查所有相关 slon 进程是否正在运行
- 值得注意的例外包括 STORE NODE、STORE PATH
- 如果 slon 未运行,Slonik 应该发出警告或报错
控制隐式 WAIT FOR EVENT
为了同时支持旧版 slonik 脚本和新脚本,建议使用以下功能
- slonik 应该有一个命令行选项来停用“自动等待”
- 用户可能希望在脚本中控制等待行为,因此我们应该添加两个 slonik 命令
- 激活自动等待
- 停用自动等待
DATE
- 由 Stuart Bishop 建议
- 基本上,能够在 Slonik 脚本中获取时间戳的概念
TRY BLOCK
- Chris 和 Stuart 的对话
- 也许我们应该禁止在 TRY 块中运行非事务性命令
- 非事务性命令
- WAIT FOR EVENT
- FAILOVER
- EXECUTE SCRIPT
- STORE NODE
- 也许应该消除 TRY?
对这意味着什么的良好定义尚未出现,因此我们没有可以实现的东西。
指定的前言
- 根据 Steve Singer 的说法
- -p 选项会自动引入前言
- 请参阅 [1]
批量添加表
- 根据 Steve Singer 的说法
- set add table (... tables='public.*', ..);
- set add table (.. tables='billing.(!password)', ...);
- 请参阅 [2]、[3]
SET ADD TABLE 的默认值更少
- 根据 Steve Singer 的说法
- 自动确定 ID
- 自动确定来源
- 请参阅 [4]
自动记录
- 由 Chris Browne 提供
- -v 选项会导致所有请求都被记录
- 允许指定 syslog 参数是否有价值?
- 详细记录到 syslog 消除了对 DATE 命令的需求
- 现有的 ECHO 命令允许手动模拟此操作;DATE 将完成此操作。
咨询锁
提供一种方法,以便应用程序可以检测咨询锁。根据 Stuart Bishop 的说法 [5]
对此没有太多共识。初步计划:如果其他更改进展顺利,以后可能需要看一下这个。
新的 FAILOVER
通用提案,FAILOVER 应该成为一个更复杂的操作命令,允许
- 丢弃被认为已死节点
- 将多个集的故障转移操作作为一个请求执行
因此,类似于
failover (dead nodes=(1,2,4), set id=1, backup node=3, set id=2, backup node=5, set id=3, backup node=3);
- 故障转移应该检查各种条件,如果满足任何条件则中止
- 需要有路径支持通信,以便新的主节点赶上
- 需要运行 Slons 来支持让主节点赶上所需的节点
- 如果某个节点托管了无法保留的订阅,则该订阅可能会被标记为已死
- 自动终止该订阅?
- 拒绝故障转移,直到订阅被标记为已死?
这需要添加一个已死订阅子句...
交互模式
- 逐行运行自定义命令
通用功能
提交时间戳
- 由 Jan Wieck 提供
- 需要 PostgreSQL 扩展
- 消除了对定期生成 SYNC 事件的需求
- 简化了搜索 sl_log_* 数据的查询
- 能够将提交时间传递到订阅者
DDL 触发器
- 由 Jan Wieck 提供
- 需要 PostgreSQL 扩展
- 能够自动生成 DDL_SCRIPT 事件
- 讨论为... DDL_Triggers
目前不计划进行此操作。如果有人自愿实现 DDL 触发器,可能会重新考虑。
从 postgres 中提取 lexxer
- 来自 TODO,可能来自 Peter Eisentraut
- 请注意,这可能比 src/parsestatements 中的代码复杂得多
目前看来不值得。看起来修复 src/parsestatements 中的 bug 比修复这个更容易,而且实现这个比使用现有的解析器要困难得多。
报告挂起的 SYNC 请求的大小
- Bug 166
- 可以作为 test_slony_state.pl 的扩展或 slonik 内部实现
日志传送
- Bug 177
- Afilias 没有使用它;不介意通过删除它来缩减代码库。
- 似乎有一些用户会因删除它而受到伤害,因此这似乎不可行。
- 一个有吸引力的想法:将 slon 分成两种形式
- “常规”的,管理适合转发、故障转移等的数据库节点
- 仅日志传送节点
- 请注意,在 remote_worker.c 中散布着许多日志传送功能。如果分离出来,这可能会使日志传送节点代码变简单,而且常规 slon 也更简单。
通用管理员 Conninfo
有一些已知工具需要知道管理员 conninfo 连接信息
- slon 进程可能使用此连接访问 slon 管理的节点
- slonik 需要连接到所有节点,目前使用ADMIN CONNINFO 前言材料来配置此操作。
- 管理工具,如配置分析工具test_slony_state.pl
- slon-ctl 脚本也应该在此处考虑
不幸的是,不够实用。相关示例文件为 bases.h,它通过假设节点具有单个主机/端口/用户名标识来过于简化。
建议我们创建一个通用的“属性文件”来捕获此信息,以便各种常用工具可以重复使用它。