多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

ProxySQL 后端 CLIENT_DEPRECATE_EOF 协商修复指南:从连接调用参数到 SQLite3 自环回归

ProxySQL 后端 CLIENT_DEPRECATE_EOF 协商修复指南:从连接调用参数到 SQLite3 自环回归 后端数据库负载均衡【免费下载链接】proxysqlHigh-performance proxy for MySQL and PostgreSQL项目地址https://gitcode.com/gh_mirrors/pr/proxysql点击查看免费下载CLIENT_DEPRECATE_EOF 是 MySQL 协议中「以 OK 包取代 EOF 包」的关键能力位ProxySQL 在向后端建连时必须准确表达该偏好同时严格以服务端握手包greeting为准判定真实支持度。本文基于仓库内的实现计划文档 docs/superpowers/plans/2026-08-15-backend-deprecate-eof-negotiation.md 及其配套设计文档 docs/superpowers/specs/2026-08-15-backend-deprecate-eof-negotiation-design.md完整讲解该协商机制为何需要改造、如何通过 MariaDB Connector/C 补丁与MySQL_Connection代码落地以及如何用 SQLite3 Server端口 6030自环测试把回归面锁定到mysql_real_connect(..., client_flags)这一公开 API 路径。读完本文你将掌握 ProxySQL 出站 EOF 协商的完整修复思路、可复现的 TAP 验证命令以及请求 ≠ 支持这一兼容性铁律在源码中的具体实现方式。背景CLIENT_DEPRECATE_EOF 的能力位之争在 MySQL 文本协议中一个查询结果集通常以 EOF 包收尾CLIENT_DEPRECATE_EOF协商开启后服务端改为用 OK 包结束结果集从而减少一次往返并携带更多状态信息。该能力位在 Connector/C 中被定义为CLIENT_DEPRECATE_EOF (1UL 24)见补丁对 include/mariadb_com.h 的修改。ProxySQL 作为代理存在双端协商问题前端客户端侧ProxySQL 作为服务端是否在握手 greeting 中宣告该能力受mysql-enable_client_deprecate_eof控制后端出站侧ProxySQL 作为客户端是否在mysql_real_connect()调用中请求该能力决定结果包按 deprecated-EOFOK 收尾还是 legacy-EOFEOF 收尾解析。计划文档指出旧实现存在一个API 不一致ProxySQL 通过改写 Connector/C 的持久配置字段MYSQL::options.client_flag来启用后端CLIENT_DEPRECATE_EOF而随包分发的 Connector/C 补丁也基于该配置字段决定是否保留服务端 greeting 宣告的位。问题在于同一个能力位通过mysql_real_connect()的client_flag参数在握手中发出Connector/C 却因为读取的是持久配置而丢弃了服务端宣告的位于是合法的 deprecated-EOF 结果集可能被当作 legacy-EOF 解析——这正是 PR #6076 中 g9 测试作业失败的直接原因仓库补丁注释亦以 issue #3280 关联该背景。核心设计决策请求是偏好greeting 才是事实修复的架构原则浓缩为三句话设计文档Decision一节ProxySQL 只通过本地client_flags参数表达出站偏好不再为这一能力位改写 Connector/C 的持久MYSQL::options.client_flagConnector/C 补丁使用其内部已合并的有效client_flag判断是否保留 greeting 位——该值同时包含mysql_real_connect()公开参数与持久选项永远不在mysql-server_capabilities中合成该位后端 greeting 未宣告即便 ProxySQL 请求了它仍然是 legacy-EOF 后端。由此得到设计文档中的核心状态矩阵该矩阵也是后续全部回归测试的判定基准SQLite3 监听端宣告该能力ProxySQL 请求该能力期望的后端状态是是server_capabilities保留该位按 deprecated EOF 解析否是server_capabilities无该位按 legacy EOF 解析此外fast-forward 会话维持更严格策略只有前端既协商client_flag含该位又被 ProxySQL 宣告前端连接server_capabilities含该位时出站请求才带上该位。这条约束被计划列为全局约束之一Preserve the fast-forward requirement that frontend negotiation and frontend advertised capability both permit the request。落点一Connector/C 补丁改为判断有效连接标志计划 Task 2 的第一步是修改 deps/mariadb-client-library/client_deprecate_eof.patch计划标注的区间为补丁内第 493-501 行把检查持久 options换成检查有效连接 flags。当前仓库中该补丁已包含目标实现第 493-502 行/* client_flag is the effective request for this connection: mysql_real_connect() * has already merged its public client-flag argument with mysql-options.client_flag. * If that merged request omits CLIENT_DEPRECATE_EOF, hide support advertised by the * server so the remaining result parser consistently expects legacy EOF packets. * This check only removes an unrequested capability from the greeting; it must never * add CLIENT_DEPRECATE_EOF when the server itself did not advertise support. See #3280. */ if ((client_flag CLIENT_DEPRECATE_EOF) 0) { mysql-server_capabilities ~CLIENT_DEPRECATE_EOF; }这段注释本身就是计划要求为 vendor patch 添加解释性注释的落地产物语义要点有三client_flag是mysql_real_connect()公开参数与MYSQL::options.client_flagOR 合并后的有效值因此无论调用方走哪条路径设置偏好补丁都能读到一致的事实条件只负责清除未请求的能力位属于减法操作天然不会伪造服务端支持后文所有结果解析逻辑如ma_net_safe_read、mthd_my_read_rows、mthd_stmt_read_all_rows中的分支都以mysql-server_capabilities CLIENT_DEPRECATE_EOF为唯一事实来源因此补丁在握手阶段修正server_capabilities后整个解析链路便保持一致。落点二ProxySQL 出站请求移入本地连接参数计划 Task 2 的第二步是改造 lib/mysql_connection.cpp 中的MySQL_Connection::connect_start_SetClientFlag()计划标注区间为第 903-956 行仓库当前实际实现位于第 991-1074 行。该函数负责组装出站mysql_real_connect_start()的client_flags其决策逻辑上方已按计划要求添加 Doxygen 块第 1020-1034 行明确区分三种状态/** * brief Select the backend CLIENT_DEPRECATE_EOF request for this connect attempt. * details The local connect-call flags express a client preference only. Connector/C * records actual support from the backend greeting in server_capabilities; * a backend that does not advertise the bit remains a legacy-EOF backend. * * par Normal backend connections * Request CLIENT_DEPRECATE_EOF when mysql-enable_server_deprecate_eof is enabled. * par Enforced session tracking * Request CLIENT_DEPRECATE_EOF regardless of that setting because session tracking * requires the deprecate-EOF protocol. * par Fast-forward connections * Replace the normal preference with the frontends negotiated state. Forward the * request only when the frontend both requested the capability and saw it advertised. */三种状态在源码中的实际实现lib/mysql_connection.cpp常规后端连接mysql_thread___enable_server_deprecate_eof为真时置位CLIENT_DEPRECATE_EOF强制会话跟踪ENFORCED当mysql_thread___session_track_variables session_track_variables::ENFORCED时无条件置位——因为会话跟踪协议本身依赖 deprecated-EOF 语义该覆盖逻辑与Admin_FlushVariables.cpp中ENFORCED 覆盖两个开关并打印警告的行为互相印证fast-forward 连接先无条件清除本地位client_flags ~CLIENT_DEPRECATE_EOF仅当前端连接的options.client_flag与options.server_capabilities同时含该位时才重新置位。组装完成的client_flags在connect_start()中直接作为mysql_real_connect_start()的最后一个参数传出lib/mysql_connection.cppasync_exit_statusmysql_real_connect_start(ret_mysql, mysql, host_ip, userinfo-username, auth_password, userinfo-schemaname, parent-port, NULL, client_flags);注意整个流程不再向mysql-options.client_flag写入该能力位这正是以本地参数而非持久配置表达偏好的代码级体现。回归测试设计两条独立的验证链路计划采用两条互补的 TAP 测试链路分别锁定公开 Connector/C API 路径和ProxySQL 出站建连路径。链路一直接 Connector/C 能力矩阵Task 1修改目标为 test/tap/tests/test_sqlite3_special_queries.cpp计划标注第 15-130 行当前仓库中矩阵实现在第 117-170 行配套测试二进制为test_sqlite3_special_queries_libmariadb-t。测试要点通过 admin 接口的 helperset_client_deprecate_eof/get_client_deprecate_eof见 test_sqlite3_special_queries.cpp切换mysql-enable_client_deprecate_eof从而控制 SQLite3 监听端端口 6030的 greeting 是否宣告该位对{宣告, 不宣告}两种状态都用mysql_real_connect(proxy, ..., SQLITE3_SERVER_PORT, NULL, CLIENT_DEPRECATE_EOF)直接连接即必须经由公开 API 的client_flags参数回归面正是这里然后断言MYSQL::server_capabilities位再执行SELECT CONNECTION_ID()并断言返回单行数值且等于mysql_thread_id()测试用例名必须与计划一致当前仓库已照此实现见 test_sqlite3_special_queries.cpp 与第 163-164 行ok(server_supports_deprecate_eof expected_server_capability, SQLite3 advertised CLIENT_DEPRECATE_EOF as configured); ok(connection_id_rc 0 valid_connection_id connection_id expected_connection_id, SELECT CONNECTION_ID() parses with the negotiated backend EOF mode);关键纪律Task 1 Step 3测试代码不得把CLIENT_DEPRECATE_EOF赋给MYSQL::options.client_flag必须让mysql_real_connect()的末参数成为唯一回归面补丁合入前运行该二进制预期RED宣告能力的情形会因为库收到 deprecated-EOF 结果、却在mysql_real_connect(..., client_flags)后清掉了 greeting 位而失败从而把 PR #6076 的 g9 故障变成可观测的单测回归。运行命令TAP_QUIET_ENVLOAD1 test/tap/tests/test_sqlite3_special_queries_libmariadb-t链路二SQLite3 自环后端路径Task 3修改目标为 test/tap/tests/test_match_eof_conn_cap.cpp计划标注第 1-975 行配套测试二进制为test_match_eof_conn_cap-t。它利用已有的**自环self-loop**配置把 ProxySQL 自身的 SQLite3 监听端127.0.0.1:6030hostgroupSQLITE3_HG默认 1459见 test_match_eof_conn_cap.cpp注册为后端让 ProxySQL 代理自身从而在单实例内覆盖所有前端/后端能力组合。Task 3 的核心是在既有连接获取路径上向后端 hostgroup 执行一条返回一行的查询断言恰好返回一行且值为期望值并覆盖两个关键状态test_match_eof_conn_cap.cpp{ .cli_depr_eof true, .srv_depr_eof true, .force_mismatch false }, { .cli_depr_eof false, .srv_depr_eof true, .force_mismatch false },其中后者的意义在于证明请求不会伪造服务端支持SQLite3 greeting 未宣告该位时结果仍必须按 legacy EOF 正确解析出一行数据。测试还利用apply_proxy_conf()及其从磁盘重载全局配置的清理逻辑保证每次退出路径都恢复全局配置。运行命令TAP_QUIET_ENVLOAD1 test/tap/tests/test_match_eof_conn_cap-t计划明确指出两条链路的职责分工Task 1 的直接 Connector/C 测试是 RED 回归证明锁定公开 APITask 3 的自环测试验证独立的 ProxySQL 出站建连路径锁定connect_start_SetClientFlag与mysql_real_connect_start的组合。两条链路合起来恰好覆盖设计矩阵的全部四格。仓库中还有一组补充测试位于 test/tap/tests_with_deps/deprecate_eof_support/覆盖 fast-forward 切换、缓存与混合 flags 等场景可视为该主题的配套纵深。配置恢复与测试卫生计划把在每条测试退出路径上恢复被修改的全局 MySQL 变量与测试配置列为全局约束。具体到代码test_sqlite3_special_queries.cpp 用 RAII 类restore_client_deprecate_eof在析构时把mysql-enable_client_deprecate_eof恢复为原始值test_match_eof_conn_cap依赖apply_proxy_conf()的清理逻辑从磁盘重载全局配置两套测试同时覆盖 greeting 的两种状态任一状态下的配置残留都会让下一次运行失去确定性。关键全局变量与互相制约CLIENT_DEPRECATE_EOF的双端开关是理解本方案配置面的钥匙变量作用面默认值源码依据mysql-enable_client_deprecate_eof控制 ProxySQL 前端 greeting 是否宣告该位truelib/MySQL_Thread.cpp 初始化线程局部变量定义于 include/proxysql_structs.hmysql-enable_server_deprecate_eof控制 ProxySQL 出站client_flags是否请求该位true同上mysql-session_track_variables取ENFORCED时强制双端启用无视上述开关—lib/Admin_FlushVariables.cpp 会打印覆盖警告前端 greeting 侧的宣告逻辑位于 lib/MySQL_Protocol.cpp当mysql_thread___enable_client_deprecate_eof为真、或session_track_variables为ENFORCED时才在mysql_thread___server_capabilities中置位并写入握手包否则显式清除。这与后端出站侧的三态决策形成对称设计——前端宣告与后端请求各自独立受控由 Connector/C 补丁最终以服务端 greeting 为准裁决真实解析模式。验证、文档与提交Task 4修复收尾阶段的验证与文档要求同样被计划明确固化本地最终验证git diff --check TAP_QUIET_ENVLOAD1 test/tap/tests/test_sqlite3_special_queries_libmariadb-t TAP_QUIET_ENVLOAD1 test/tap/tests/test_match_eof_conn_cap-t期望两个 TAP 命令均以退出码 0 结束、直接连接测试覆盖两种 greeting 状态、diff 无空白错误。PR #6076 正文需补充四段解释计划 Task 4 Step 2为什么MYSQL::options.client_flag不是mysql_real_connect(..., client_flags)的安全事实来源为什么 ProxySQL 改为通过本地 connect-call flags 请求该能力为什么后端 greeting 位缺失时仍按 legacy EOF 解析SQLite3 自环测试矩阵与本地/CI 验证命令。最终提交信息计划 Task 4 Step 3 给出的基准文本fix: preserve backend deprecate EOF negotiation Request CLIENT_DEPRECATE_EOF through the mysql_real_connect flags rather than mutating Connector/Cs persistent options. Retain the capability only when the backend greeting advertises it, so legacy backends continue to use legacy EOF parsing. Add direct Connector/C and SQLite3 self-loop coverage.推送 PR 分支后需重点检查此前失败的 g9 测试作业任何无关失败须与本次回归分开报告。边界与约束清单最后计划与设计文档明示的约束可归纳为一份可审计的检查单适合作为评审清单禁止合成任何代码路径都不得向mysql-server_capabilities添加CLIENT_DEPRECATE_EOFgreeting 是唯一支持来源fast-forward 双门槛前端协商位与前端被宣告位必须同时为真出站才请求该能力公开 API 回归面必须覆盖mysql_real_connect(..., client_flags)这一导致 PR #6076 g9 失败的确切路径配置恢复每条测试退出路径都必须恢复被修改的全局变量与测试配置注释完备ProxySQL 代码加 Doxygen、vendor patch 加解释性注释非目标不为后端无条件启用CLIENT_DEPRECATE_EOF、不依据请求标志推断服务端支持、不引入新的测试服务或复制既有自环环境。从当前仓库源码看计划描述的目标状态补丁第 493-502 行的有效标志判断、connect_start_SetClientFlag的三态决策与 Doxygen、两条测试链路中的矩阵实现均已落地本文所有代码片段均可直接对照仓库验证是一份可复现、可引用的实现与回归指南。赞分享后端数据库负载均衡【免费下载链接】proxysqlHigh-performance proxy for MySQL and PostgreSQL项目地址https://gitcode.com/gh_mirrors/pr/proxysql点击查看免费下载相关推荐ProxySQL PostgreSQL 后端连接 SSL 参数精细化配置pgsql_servers_ssl_params 表完全指南ProxySQL PostgreSQL 后端连接 SSL 参数精细化配置pgsql_servers_ssl_params 表完全指南 ProxySQL 支持后端数据库负载均衡ProxySQL SQLite3 监听器 CONNECTION_ID() 与 CLIENT_DEPRECATE_EOF 兼容性实现深度解析ProxySQL SQLite3 监听器 CONNECTION_ID 与 CLIENT_DEPRECATE_EOF 兼容性实现深度解析 导读 CONNECTIO后端数据库负载均衡ProxySQL 内置 SQLite3 Server 实战指南用 MySQL 客户端直连 SQLite 的协议翻译层ProxySQL 内置 SQLite3 Server 实战指南用 MySQL 客户端直连 SQLite 的协议翻译层 本指南系统讲解 ProxySQL 内置后端数据库负载均衡上一篇QVGE 开源项目使用教程下一篇微信支付SDK使用教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表