MySQL Router plugin: apply Routing Guidelines managed by MySQL Shell (#6145)

RE
1mo ago
+8594-62

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 service register_mysql_route_hook plus 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 for COM_QUERY, COM_STMT_PREPARE and COM_STMT_EXECUTE.
    • set_hostgroup overrides qpo->destination_hostgroup.
    • deny goes through the existing error_msg path: the statement fails and the session stays open.
    • The cookie is reset on COM_CHANGE_USER and COM_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's routing_guidelines sources. 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 and MEMBER_VERSION are read.
  • Shell contract: check-in writes SupportedRoutingGuidelinesVersion=1.1 (Shell refuses setRoutingOption('guideline', ...) for a router without it) and CurrentRoutingGuideline.
  • 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 is invalid and 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 in stats_mysql_router_errors (guideline_parse, guideline_validation, guideline_evaluation).

Precedence with operator rules

  • Operator mysql_query_rules are never modified and are evaluated first.
  • The hook only remaps the plugin's own route_writer and route_reader destinations 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-robin uses equal weights; first-available uses 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: connectionSharingAllowed is not applied; $.router.routeName inside 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) and plugin_load (ABI 10). All mysql_router_* unit tests and plugin_lifecycle_unit-t pass.
  • New E2E group mysql-router-ic-rg-g1 with CI workflow CI-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 CurrentRoutingGuideline in v2_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 setRouteOption and 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.
    • 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, clusterSetName and isClusterInvalidated.
  • 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' in mysql_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 hostgroups rg:<route>:all|writer|reader in 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 operator mysql_query_rules and remaps only the plugin's route_writer/route_reader destinations.
  • 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 CurrentRoutingGuideline are 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.1 so Shell accepts it.

Implementation and testing

  • ABI-10 adds the register_mysql_route_hook service; 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 in stats_mysql_router_errors.
  • New E2E group mysql-router-ic-rg-g1 passes 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 the mysql-router-ic-g1 regression passes 65/65.
  • The PRNG behind $.session.randomValue is 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.