ProxySQL 集群模拟器 payload 处理:使用 jq 过滤器为 mysql_servers 批量注入 comment 字段

原创2026-10-08 21:27:511,659 阅读
文章标签:后端数据库负载均衡

ProxySQL 集群模拟器 payload 处理:使用 jq 过滤器为 mysql_servers 批量注入 comment 字段

在 ProxySQL 的集群模拟器(Cluster Simulator)测试体系中,payload 是最核心的输入文件,其 JSON 结构与真实集群状态一一对应。本篇文章围绕 add_servers_comment.md 所介绍的 jq 过滤器展开:它能够在模拟器 payload 的 mysql_servers 数组上,按主机名条件为每个服务器对象追加 comment 字段。读完本文,你将掌握该过滤器的逐行语义、在命令行中的实际调用方式,以及它如何与模拟器的状态校验逻辑协同,帮助你在编写 GALERA / READONLY / GROUP_REPLICATION 测试用例时快速为服务器打上可读性注释,并借此校验 comment 字段在 ProxySQL 内部的真实落库情况。

过滤器定位:payload 处理工具链中的一员

Cluster Simulator(源码位于 cluster_simulator.cpp)通过 SQLite 伪造后端服务器的连接状态,从而在不搭建真实 MySQL 集群的情况下,模拟并校验 ProxySQL 对 GALERA、READONLY、GROUP_REPLICATION 等集群模式的配置行为。模拟器的输入与输出都是 JSON 格式的 payload,随着测试场景增多,payload 会变得庞大且复杂,因此仓库专门在 docs/payloads_processing/README.md 中建议使用 jq 与 node 两类工具对 payload 做快速分析、批量改造与失败归因。

add_servers_comment 过滤器就是该工具链中的一员,它与同目录下的一系列过滤器(如 servers_in_reader_hostgroups.jq、init_state_writers_only.jq、mv_readers_to_writers.jq 等)一样,以「一个 .jq 文件 + 一个同名 .md 说明」的形式存放在 jq_filters 目录中。原文档对其功能只有一句概括:"Simple filter that allows adding an extra comment to the servers."——即一个允许为服务器追加额外 comment 的简单过滤器。其完整实现见 add_servers_comment.jq。

过滤器源码逐行解析

.[1] | .mysql_servers |
    map(
        if .hostname == "127.1.1.11" then
            . + {comment: "mysql01"}
        elif .hostname == "127.1.1.12" then
            . + {comment: "mysql02"}
        elif .hostname == "127.1.1.13" then
            . + {comment: "mysql03"} else . end
    ) | .[]

.[1]:定位 payload 中的第二个测试对象

模拟器 payload 是一个 JSON 数组,每个元素是一个独立的测试对象(cluster_type、mysql_servers、proxysql_init_state 等的集合),参见 simple_test_payload.json。.[1] 表示取数组下标为 1 的元素,即第二个测试对象——注意这里使用的是 [1] 而非 [0],如果 payload 中只有一个测试对象,使用本过滤器时需相应改为 .[0]。

.mysql_servers:进入服务器配置数组

mysql_servers 是 payload 中的通用成员(README 的「Simulator Payload Format」一节明确指出它是必需成员),数组内的每个对象至少包含 {hostgroup_id, hostname, port, status(可选)}。过滤器在此取得当前测试对象待改造的服务器列表。

map(...):对每个服务器对象做条件变换

jq 的 map 会对数组的每一个元素应用括号内的表达式,并返回新的数组。这里对每台服务器依次判断:

  • if .hostname == "127.1.1.11" then . + {comment: "mysql01"}:当主机名为 127.1.1.11 时,通过 + 运算符将 {comment: "mysql01"} 合并进原对象,得到带注释的新对象;
  • 随后的两个 elif 分支分别给 127.1.1.12、127.1.1.13 打上 mysql02、mysql03 注释;
  • else . end:其余主机名的服务器原样保留,不添加任何字段。

这种「按主机名分支注入字段」的写法保证了过滤器只针对目标服务器生效,非目标服务器不会被误改。

| .[]:将数组展开为对象流

管道末端的 .[] 把经过 map 处理后的数组重新展开成独立的对象流,配合 jq 的默认输出行为,每个服务器对象会以单行 JSON 的形式打印出来,便于后续重定向、拼接或人工核对。

命令行实际用法

按仓库原样执行:输出服务器对象流

将过滤器内容保存为 .jq 文件后,即可对任意模拟器 payload 运行:

jq -f add_servers_comment.jq payload.json

对 simple_test_payload.json 这类单测试 payload,运行前需要把过滤器中的 .[1] 调整为 .[0]。执行结果中,127.1.1.11/127.1.1.12/127.1.1.13 三个主机对应的服务器对象会被追加 comment 字段,而其他主机则保持原样。

建议的变体:原地更新整个 payload

原过滤器只输出 mysql_servers 子数组的流式结果,属于「分析型」过滤器。若希望保留 payload 其余成员、真正生成一份带注释的新 payload,可以将其改写为 jq 的赋值运算符形式:

.[1].mysql_servers |= map(
    if .hostname == "127.1.1.11" then . + {comment: "mysql01"}
    elif .hostname == "127.1.1.12" then . + {comment: "mysql02"}
    elif .hostname == "127.1.1.13" then . + {comment: "mysql03"}
    else . end
)

保存后通过 jq -f 执行,即可在完整保留 cluster_type、galera_servers_init_state、proxysql_init_state 等成员的前提下,仅更新目标位置的 mysql_servers 数组。

与既有分析型过滤器的组合

docs/payloads_processing/README.md 还给出了一个「求两个状态数组差集」的常用过滤器,用来定位 exp_proxysql_state 与 act_proxysql_state 之间的差异:

jq --compact-output ".results[0] | (.exp_proxysql_state? - .act_proxysql_state?) + (.act_proxysql_state? - .exp_proxysql_state?) | .[]" simulator_output.json

这种「先改造 payload、再对比状态差异」的组合,正好覆盖了从「准备测试数据」到「定位验证失败原因」的完整闭环。

与模拟器校验逻辑的衔接:comment 字段的精确匹配语义

为 mysql_servers 追加 comment 并非只是为了可读性,它还能直接参与模拟器的状态校验。在 README.md 对 proxysql_init_state / proxysql_final_state 的格式说明中明确指出:

数组内对象须为 {hostgroup_id, hostname, port, status} 结构,还可包含 weight、max_connections、use_ssl 与 comment。省略的可选成员不参与比较;而显式提供的成员(包括显式提供空字符串 comment)必须与真实状态完全一致。

这意味着:

  • 如果在 mysql_servers 中通过本过滤器注入了 comment,并在对应的 proxysql_init_state / proxysql_final_state 条目中也显式写出 comment 字段,那么模拟器在 verify 模式下会逐条比对 ProxySQL 内部 mysql_servers 表中的 comment 值;
  • 若 ProxySQL 实际写入的 comment 与期望值不一致(哪怕只是大小写差异),验证会以 verification_error 失败,并在 err_msg 中给出原因;
  • 反过来,如果状态数组中完全省略 comment,该成员不会被比较,验证依然可以通过。

因此,本过滤器在测试开发中的典型用法是:先用它批量给输入侧的 mysql_servers 打上注释,再在 proxysql_init_state / proxysql_final_state 中同步声明对应的 comment 值,从而把「服务器备注能否正确写入 ProxySQL 并被持久化」纳入端到端断言范围。

实战路径:从模拟到验证

结合 README.md 中的「Simulation and Verification guide」,一条完整的实践链路如下:

  1. 编译对应模式的 ProxySQL(模拟器要求 ProxySQL 以 SQLite 服务端方式运行):

    make build_deps -j$(nproc) && make testgalera -j$(nproc) && make build_tap_test -j$(nproc)
    

    其他模式依次替换为 testreadonly、testgrouprep、testreplicationlag。

  2. 启动带模拟配置的 ProxySQL:

    ./src/proxysql --sqlite3-server -f -c ${CLUSTER_SIM_DIR}/configs/galera_sim_conf.cnf -D ./galera_sim_datadir
    
  3. 用 jq 过滤器改造测试 payload:先构造一个 proxysql_init_state / proxysql_final_state 均为空的测试 JSON,再用上文过滤器为 mysql_servers 注入 comment,随后以 simulate 模式运行模拟器,让它回填实际的初始与最终状态:

    ./cluster_simulator --mode simulate -f tests/tests_payloads/next_test.json
    
  4. 将回填结果固化为新测试:确认回填的 proxysql_init_state / proxysql_final_state 符合预期(其中即可包含经校验的 comment 值),把 results 数组内容写回测试文件,再以 verify 模式运行:

    ./cluster_simulator --mode verify -f tests/tests_payloads/next_test.json
    

    得到 "err_type": "none" 即表示新测试通过,可将文件放入 tests 目录由 simulator_wrapper-t 统一批量执行。

同目录过滤器横向参考

jq_filters 目录还提供了其他可复用的过滤器,可与 add_servers_comment 配合理解 payload 加工思路:

这些过滤器与本过滤器共同体现了「用 jq 结构化地改造 JSON payload」的测试开发方法论:当需要为多个测试同时调整服务器属性、推导主机组关系或排查状态差异时,jq 过滤器比手工编辑 JSON 更可复用、更不易出错。若过滤逻辑复杂到难以用 jq 表达,仓库 docs/payloads_processing/README.md 也给出了备用方案——JSON 本身即合法 JavaScript,可使用 node/payload.js 作为模板进行更灵活的 payload 编程式加工。

小结

add_servers_comment 是一个体量虽小、语义清晰的 jq 过滤器:它通过对 mysql_servers 数组执行「按主机名条件合并 comment 字段」的映射变换,为 ProxySQL 集群模拟器的测试 payload 提供了标准化的注释注入手段。结合模拟器对 comment 可选成员的精确匹配校验语义,该过滤器不只是文本工具,更是构建「服务器备注落库断言」的关键一环。在 GALERA / READONLY / GROUP_REPLICATION 等场景的测试用例开发中,你可以直接复用 add_servers_comment.jq 或在其基础上扩展新的主机名分支,将服务器命名语义固化到测试数据里。

登录后查看全文
proxysql