MySQL Router plugin: apply Routing Guidelines managed by MySQL Shell (#6145)
RE
1mo ago+8594+8594+0123456789012345678901234567890123456789-62-62-01234567890123456789
Implements #6145 for InnoDB Cluster: Routing Guidelines created, modified, activated and removed with an unmodified MySQL Shell 9.x are applied by proxysql_mysql_router.so to real client traffic.
Design: docs/superpowers/specs/2026-09-17-mysql-router-routing-guidelines-design.md. User docs: doc/mysql-router-plugin.md ("Routing Guidelines").
Approach (hybrid)
- Destinations (
$.server.*) are evaluated against the live topology at every reconcile. Each enabled route gets three plugin-owned hostgroups (rg:<route>:all|writer|reader, in the 8000-8999 band), published in the same atomic main/disk/runtime generation as the rest of the Router config. - Routes (
$.session.*,$.router.*) are evaluated natively per client session, with the full expression language, through a new core ABI-10 MySQL route hook.
Changes
Core (PROXYSQL40 only)
ProxySQL_Plugin.h: ABI layout 10 adds the tail serviceregister_mysql_route_hookplus payload/result types (user, schema, client/proxy address, connect attributes, destination after query rules, per-session cookie).ProxySQL_PluginManager: registration, a lock-free fast-path probe, and shared-lock dispatch, following the same pattern as the query hook.MySQL_Session: calls the hook right after the query processor forCOM_QUERY,COM_STMT_PREPAREandCOM_STMT_EXECUTE.set_hostgroupoverridesqpo->destination_hostgroup.denygoes through the existingerror_msgpath: the statement fails and the session stays open.- The cookie is reset on
COM_CHANGE_USERandCOM_RESET_CONNECTION. - Without a registered hook the cost is one atomic load per statement.
Plugin
- Engine (
routing_guidelines*.cpp): document validation and the expression language (lexer, parser, type checks, constant folding, evaluator, all functions, 1.0/1.1 tag semantics), written against MySQL Router'srouting_guidelinessources. Parsed guidelines are immutable and thread-safe; route matching takes about 130 ns. - Metadata: 2.2 to 2.4 accepted; the active guideline is read with MySQL Router's own router > ClusterSet > Cluster selection query;
$.router.*info, router tags andMEMBER_VERSIONare read. - Shell contract: check-in writes
SupportedRoutingGuidelinesVersion=1.1(Shell refusessetRoutingOption('guideline', ...)for a router without it) andCurrentRoutingGuideline. - Runtime (
guideline_runtime.cpp):- pools, hostgroup allocation and release, and the immutable snapshot read by the hook;
- "last valid generation" (an invalid document leaves state
stale; with no previous valid document the state isinvalidand nothing is applied); - the guideline document cache, so the guideline survives a restart during a metadata outage.
- Observability:
runtime_mysql_router_guideline,runtime_mysql_router_guideline_routes(explain: per route and pool hostgroup, priority, classes, strategy, members/weights, notes),runtime_mysql_router_guideline_destinations; errors instats_mysql_router_errors(guideline_parse,guideline_validation,guideline_evaluation).
Precedence with operator rules
- Operator
mysql_query_rulesare never modified and are evaluated first. - The hook only remaps the plugin's own
route_writerandroute_readerdestinations on the Router listeners; any other destination is left unchanged. - With no active valid guideline the hook is a no-op.
Documented equivalence (also in runtime_mysql_router_guideline_routes.notes)
- Priority fallback: the first group with an available member at the last refresh.
- Strategies:
round-robinuses equal weights;first-availableuses decreasing weights. - rw_split port: writes go to the PRIMARY members of the first group containing one; reads go to the non-PRIMARY members of the first group containing them.
- No route matched: the statement fails (ProxySQL cannot fail at handshake time).
- Guideline change: an existing session re-selects its route on the next statement instead of being disconnected.
- Not applied / not supported:
connectionSharingAllowedis not applied;$.router.routeNameinside destinations is rejected explicitly.
Tests
- Unit tests (unit-tests-g1):
mysql_router_routing_guidelines_unit-t: 710 assertions, including Shell default documents for Cluster, ReplicaSet and ClusterSet.mysql_router_guideline_runtime_unit-t: 46 assertions on pools, weights, fallback, rw_split pools, precedence, cookie reuse, stale/invalid/removal and config append.- Updated
metadata_v2_2(2.3/2.4 plus guideline read),admin_schema(new tables) andplugin_load(ABI 10). Allmysql_router_*unit tests andplugin_lifecycle_unit-tpass.
- New E2E group
mysql-router-ic-rg-g1with CI workflowCI-mysql-router-ic-rg-g1.yml:- Real 3-member InnoDB Cluster plus read replica, created with unmodified MySQL Shell 9.7.0 (metadata 2.4.0), with guidelines created through the AdminAPI and the production plugin bootstrapped.
test_mysql_router_routing_guidelines-t(53 checks) covers:- Shell activation, and Shell
listRouters()reporting the ProxySQL Router's current guideline; - explain views, and
CurrentRoutingGuidelineinv2_routers; - routing by
$.session.user,$.session.connectAttrs.program_name,NETWORK($.session.sourceIP, 16), plus the rw/ro routes; - a session matching no route; rw_split reads and locking reads;
- an operator-rule destination that is not remapped;
- disabling a route with
setRouteOptionand convergence; - a corrupted document leading to
stale, an error row, traffic on the last valid generation, then recovery; - removal restoring baseline routing and releasing the route hostgroups;
- operator rules unchanged across all of the above.
- Shell activation, and Shell
- 53/53 on two runs from freshly created infra.
- Regression: existing
mysql-router-ic-g1(metadata 2.2, Shell 8.4.8) passes 65/65.
Not in this PR (follow-ups)
- ReplicaSet and ClusterSet guideline support: blocked on the plugin's topology expansion. The engine already models
clusterRole,clusterSetNameandisClusterInvalidated. - Remaining acceptance-matrix work from #6145: coverage for every server/Shell pair, primary and ClusterSet transitions, and metadata outage during an active guideline. Outage behaviour is implemented but not yet exercised by the E2E.
- Cosmetic: the bootstrap still records
metadata_schema='2.2'inmysql_router_instance.
Summary by cubic
Implements #6145 for InnoDB Cluster: Routing Guidelines created, modified, activated, and removed with an unmodified MySQL Shell 9.x are now applied by proxysql_mysql_router.so to real client traffic.
Behavior
- Destinations (
$.server.*) are classified against the live topology at every reconcile; each enabled route gets plugin-owned hostgroupsrg:<route>:all|writer|readerin the same atomic generation as the rest of the Router config. - Routes (
$.session.*,$.router.*) are matched per session by the new ABI-10 route hook, which runs after operatormysql_query_rulesand remaps only the plugin'sroute_writer/route_readerdestinations. - With no valid active guideline the hook is a no-op; an invalid document leaves the last valid generation (
stale) or applies nothing (invalid) when none exists, and the rejection keeps being reported on later refreshes. - Guideline state and
CurrentRoutingGuidelineare published only after the generation containing the guideline; errors are still reported immediately. - A statement matching no route fails, and existing sessions re-select their route on the next statement after a guideline change.
- The plugin reads the active guideline from metadata 2.3/2.4 with MySQL Router's selection query and advertises
SupportedRoutingGuidelinesVersion=1.1so Shell accepts it.
Implementation and testing
- ABI-10 adds the
register_mysql_route_hookservice; without a hook the per-statement cost is one atomic load. - The engine implements document validation and the full expression language (1.0/1.1) as immutable, thread-safe compiled guidelines; the last valid guideline persists across a restart during a metadata outage, and the active document is fingerprinted so edits to a rejected one do not republish.
- New
runtime_mysql_router_guideline*views expose guideline state; errors are reported instats_mysql_router_errors. - New E2E group
mysql-router-ic-rg-g1passes 53/53 checks against a real 3-member InnoDB Cluster plus read replica managed by unmodified Shell 9.7.0; unit tests cover engine (710 assertions) and runtime (46), and themysql-router-ic-g1regression passes 65/65. - The PRNG behind
$.session.randomValueis documented as non-security (Sonar cleanup). - ReplicaSet and ClusterSet guideline support and the remaining acceptance-matrix coverage are follow-ups.
Summary by CodeRabbit
-
New Features
- Added MySQL Router Routing Guidelines support for InnoDB Cluster Metadata 2.3 and 2.4.
- Supports guideline versions 1.0–1.1, destination classification, route matching, session-based routing, and reader/writer pools.
- Added runtime views for guideline status, routes, destinations, and generated hostgroups.
- Added guideline selection through router options, with visibility into active, stale, invalid, or unavailable states.
- Added integration test coverage for Routing Guidelines with MySQL Shell 9.x.
-
Bug Fixes
- Invalid guideline updates retain the last valid configuration, while removing a guideline restores baseline routing.