16 KiB
MySQL X Plugin Design For ProxySQL
Status
Drafted through brainstorming and narrowed to:
- hybrid config model
- dual-mode accounts
- in-process plugin loaded with ProxySQL
- same-process execution with clean extension API
- plugin-owned worker threads
- umbrella architecture plus implementation-ready slices 1-2
1. Objective
Design MySQL X Protocol support for ProxySQL such that:
- it can ship as a separately loadable plugin rather than as tightly embedded core code
- it supports deep integration rather than Router-style passthrough only
- it preserves reuse of canonical ProxySQL concepts where their semantics are shared
- it keeps protocol-specific complexity isolated from the classic MySQL frontend/backend code paths
This design covers four subprojects:
- frontend
mysqlxtermination - backend
mysqlxconnectivity - X-aware policy engine
- X-based Group Replication notifications
This document is implementation-ready for subprojects 1 and 2, and architectural for 3 and 4.
2. Scope
In scope for slices 1-2
- plugin boundary and extension API
- plugin-owned listeners and worker threads
- frontend X handshake and authentication termination
- dual-mode identity resolution using
mysql_usersandmysqlx_users - backend X session establishment
- backend identity mapping modes
- route and hostgroup integration
- per-session binding of future policy state
- plugin stats and admin surface needed to operate the feature
Out of scope for immediate implementation
- full X rule grammar and enforcement implementation
- query/document rewrite semantics
- pooling and multiplexing beyond explicit initial boundaries
- X-based Group Replication notification delivery
- broad protocol-neutral refactoring of ProxySQL core
- hot-unload of the plugin
3. Design Principles
- Keep X Protocol semantics out of ProxySQL core.
- Reuse core only for generic infrastructure and shared runtime state.
- Keep the plugin operationally integrated but implementation-isolated.
- Prefer explicit protocol-specific surfaces over overloading classic MySQL concepts beyond recognition.
- Preserve a path to future evolution without forcing a large core refactor first.
4. Chosen Direction
4.1 Architectural choice
Use a thin-core plugin with shared generic services.
ProxySQL core exposes a narrow, versioned extension API. The plugin owns:
mysqlxlistenersmysqlxworker threads- frontend X protocol state
- backend X protocol state
- identity merge and auth logic
- route selection policy
- X message parsing and classification
- X policy enforcement
mysqlxstats
Core provides:
- plugin lifecycle
- listener registration or polling integration hooks
- config snapshot and subscription APIs
- access to shared topology state
- logging
- stats/admin registration
- optional generic transport helpers if they are truly protocol-neutral
4.2 Config model choice
Use a hybrid config surface.
Shared:
mysql_usersmysql_servers- replication and Group Replication hostgroup state
mysqlx-specific:
mysqlx_usersmysqlx_routesmysqlx_rulesmysqlx_policy_profilesmysqlxstats tables
4.3 Account model choice
Use dual-mode accounts.
mysql_usersis canonical.mysqlx_userscontains X-specific overrides only.
5. Module Boundary
5.1 Plugin loading model
The plugin is loaded at ProxySQL startup through a versioned plugin interface.
Phase 1 assumes:
- load at startup
- disable or restart to upgrade
- no true dynamic unload while active sessions exist
This avoids unsafe lifetime problems while still preserving plugin separability.
5.2 Core extension API
The core-facing API should be intentionally small and protocol-neutral.
Required surfaces:
plugin_init(),plugin_start(),plugin_stop(),plugin_status()- listener registration API
- event-loop registration API or a socket registration API for plugin-owned loops
- snapshot access for:
mysql_usersmysqlx_usersmysql_serversmysql_replication_hostgroupsmysql_group_replication_hostgroups- relevant global variables
- subscription or version-tracking for config refresh
- hostgroup destination lookup helpers
- logging API
- metrics and admin table registration API
Explicitly not exposed:
- classic
MySQL_Protocol - classic
MySQL_Session - classic
MySQL_Connection - MariaDB Connector-C async state machine
5.3 Worker model
The plugin has its own worker threads.
Reasons:
- X handshake/auth/parser behavior differs substantially from classic MySQL.
- Reusing MySQL worker threads would couple the plugin to classic assumptions.
- Separate workers improve isolation, performance analysis, and plugin credibility.
Each plugin worker owns:
- accepted frontend client sockets assigned to that worker
- backend X sockets for those sessions
- per-session parser state
- per-session auth state
- per-session rule context
6. Runtime Architecture
The plugin is internally divided into four subsystems.
6.1 Frontend service
Responsibilities:
- owns
mysqlxlisteners - accepts client sockets
- performs TLS negotiation if ProxySQL-terminated TLS is enabled
- runs X handshake and auth exchange
- creates frontend session objects
6.2 Identity and policy binding
Responsibilities:
- resolve
mysql_usersplusmysqlx_users - determine whether the account is X-enabled
- determine allowed auth methods and TLS requirements
- attach route profile
- attach policy profile
- attach backend identity mapping mode
6.3 Backend X service
Responsibilities:
- choose a backend destination using shared topology state
- establish outbound backend X sessions
- authenticate using resolved backend identity mode
- enforce pooling boundaries if pooling exists
- expose connection health and routing state
6.4 Policy engine
Responsibilities:
- parse X messages after session establishment
- classify SQL vs CRUD vs document-store operations
- evaluate rules
- perform allow, deny, reroute, or future rewrite decisions
- produce observability events
For slices 1-2, this subsystem only needs architectural hooks and minimal scaffolding, not full implementation.
7. Identity Model
7.1 Canonical and override tables
mysql_users remains canonical for shared account identity.
mysqlx_users adds protocol-specific overrides.
Recommended shape for mysqlx_users:
CREATE TABLE mysqlx_users (
username VARCHAR NOT NULL PRIMARY KEY,
active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 1,
require_tls INT CHECK (require_tls IN (0,1)) NOT NULL DEFAULT 0,
allowed_auth_methods VARCHAR NOT NULL DEFAULT '',
default_route VARCHAR,
policy_profile VARCHAR,
backend_auth_mode VARCHAR NOT NULL DEFAULT 'mapped',
backend_username VARCHAR,
backend_password VARCHAR,
attributes VARCHAR CHECK (JSON_VALID(attributes) OR attributes = '') NOT NULL DEFAULT '',
comment VARCHAR NOT NULL DEFAULT ''
);
runtime_mysqlx_users should mirror it.
7.2 Resolution flow
Identity resolution is deterministic:
- Look up
mysql_users.username. - If no canonical user exists, deny authentication.
- Look up
mysqlx_users.username. - If no
mysqlx_usersrow exists, the account is not X-enabled by default. - Merge canonical and X-specific fields into a resolved identity object.
The resolved identity object includes:
- canonical username
- canonical default hostgroup
- max connections
- X enabled flag
- allowed auth methods
- TLS requirements
- default route
- policy profile
- backend auth mode
- backend credential override if configured
- protocol-specific attributes
7.3 Backend identity modes
The resolved identity supports three backend modes:
pass_through- backend X session authenticates using the same logical username and client-supplied secret semantics where supported
mapped- backend X session uses explicit backend credentials from the resolved identity
service_account- plugin authenticates the client itself and opens backend sessions using service credentials, with optional future impersonation metadata
Phase 1 should implement:
mappedservice_account
pass_through can be supported only if the auth semantics are safe and the plugin can avoid retaining client secrets inappropriately. If that cannot be made correct early, it should be documented as unsupported rather than approximated.
Until then, configuration validation should reject pass_through rather than silently downgrading it.
8. Frontend mysqlx Termination
8.1 Session establishment
On accept:
- assign socket to a plugin worker
- create session context
- initialize transport state
- begin X handshake
- negotiate capability set
- authenticate client
- resolve identity and policy binding
- select route target set
- establish backend X session
- transition to proxied active state
8.2 Authentication behavior
The plugin terminates frontend X authentication itself.
This is required for:
- deep integration
- dual-mode account enforcement
- future X-aware rules
- future session-aware routing and policy
The frontend auth design must support:
- account disabled behavior
- TLS-required behavior
- per-user allowed auth methods
- clear error mapping
- connection accounting before and after authentication
8.3 Session object
Each frontend session tracks:
- transport state
- TLS state
- X capability state
- authentication state
- resolved identity
- assigned route
- policy profile binding
- parser state
- backend session handle
- counters and timing
9. Backend X Connectivity
9.1 Backend transport model
The plugin manages backend X connections directly.
Do not build slice 2 on top of:
MySQL_ConnectionMYSQL *- MariaDB Connector-C classic protocol helpers
The backend side should be native to the plugin and X-aware from the start.
9.2 Route and destination model
Recommended route table:
CREATE TABLE mysqlx_routes (
name VARCHAR NOT NULL PRIMARY KEY,
bind VARCHAR NOT NULL,
destination_hostgroup INT NOT NULL,
fallback_hostgroup INT,
strategy VARCHAR NOT NULL DEFAULT 'first_available',
active INT CHECK (active IN (0,1)) NOT NULL DEFAULT 1,
attributes VARCHAR CHECK (JSON_VALID(attributes) OR attributes = '') NOT NULL DEFAULT '',
comment VARCHAR NOT NULL DEFAULT ''
);
runtime_mysqlx_routes should mirror it.
Hostgroup semantics remain shared with core topology tables.
9.3 Destination selection
Slice 2 should support:
first_availableround_robinround_robin_with_fallback
Destination eligibility is derived from shared runtime topology state:
mysql_servers- replication hostgroup mappings
- Group Replication hostgroup mappings
- monitor health state
The core may provide hostgroup snapshots, but the plugin should own final route selection semantics.
9.4 Pooling and multiplexing boundaries
For initial implementation:
- no cross-user multiplexing
- no session reuse across incompatible policy bindings
- no reuse across differing backend auth modes
- no reuse across differing backend capability sets
Pooling may be introduced cautiously for:
- identical backend target
- identical backend auth identity
- identical capability and TLS requirements
- no session-local state that makes reuse unsafe
This means the first implementation should assume one live frontend session owns one backend X session.
10. Policy Engine Architecture
The eventual policy engine must understand X message types well enough to classify operations.
The design should reserve explicit hooks for:
- SQL statement execution
- CRUD operations
- document collection operations
- transaction state
- prepared or repeated execution forms if exposed by X
Recommended policy objects:
mysqlx_policy_profilesmysqlx_rules
Where policy_profile on the resolved identity selects a ruleset.
Initial rule outcomes:
- allow
- deny
- reroute
- require primary
- require reader
- emit audit event
Rewrite should be a deferred capability unless message rewriting is proven safe for the targeted X operations.
11. Group Replication Notifications
This is Phase 2 of the broader mysqlx project, but not required for slices 1-2.
The plugin should still be architected so that a future GR notification subsystem can:
- maintain X connections to cluster members
- receive topology-change notifications
- trigger topology cache refresh
- reduce convergence time for route decisions
This subsystem should feed into shared routing state, not bypass it with a separate topology model.
12. Admin And Observability Surface
12.1 Config tables
Required:
mysqlx_usersruntime_mysqlx_usersmysqlx_routesruntime_mysqlx_routes
Deferred but architected:
mysqlx_policy_profilesmysqlx_rules
12.2 Stats tables
Recommended initial stats:
CREATE TABLE stats_mysqlx_routes (
name VARCHAR NOT NULL,
destination_hostgroup INT NOT NULL,
ConnOK INT NOT NULL,
ConnERR INT NOT NULL,
ConnUsed INT NOT NULL,
Bytes_data_sent BIGINT NOT NULL,
Bytes_data_recv BIGINT NOT NULL
);
And per-connection/session stats exposed through admin commands or a runtime table with:
- username
- route
- worker id
- backend host and port
- auth mode
- connection state
- bytes in and out
- session age
13. Core Integration Points
The required core changes should be narrowly scoped.
13.1 Required generic hooks
- plugin loader and registry
- plugin lifecycle management
- listener conflict validation integration
- config snapshot export for shared tables
- runtime config change notifications
- stats namespace registration
- logging and diagnostics hooks
13.2 Avoided core entanglement
Do not modify classic code so that X logic is threaded through:
- MySQL classic handshake path
- classic prepared statement machinery
- MariaDB Connector-C connection lifecycle
- MySQL worker thread scheduling
14. Risks
14.1 Largest implementation risk
Frontend auth termination plus backend X session establishment in a plugin is materially larger than Router-style passthrough. That is intentional, but it raises schedule risk.
14.2 Design risk
If the plugin API is too small, the plugin will be forced into ugly duplication.
If the plugin API is too broad, ProxySQL core becomes an accidental protocol framework.
The extension API must stay narrow and intentionally generic.
14.3 Operational risk
Dual-mode accounts can become confusing if fallback rules are vague.
Mitigation:
mysqlxdisabled unless explicitly enabled inmysqlx_users- deterministic merge rules
- admin diagnostics showing the resolved identity object
15. Testing Strategy
15.1 Slice 1 tests
- authenticate valid dual-mode account
- reject canonical user without
mysqlx_usersenablement - enforce TLS-required accounts
- enforce allowed auth methods
- resolve merged identity correctly
15.2 Slice 2 tests
- connect backend X session with mapped identity
- connect backend X session with service-account mode
- select destination from route hostgroup
- fallback when primary hostgroup has no eligible destination
- reject invalid route configuration
- preserve one frontend to one backend ownership
15.3 Plugin integration tests
- startup with plugin enabled
- startup with plugin disabled
- config reload of
mysqlx_users - config reload of
mysqlx_routes - worker-thread isolation under concurrent sessions
16. Recommended Implementation Order
- core plugin loader and minimal extension API
- plugin-owned listeners and worker threads
mysqlx_usersandmysqlx_routesconfig surfaces- resolved identity pipeline
- frontend X handshake and auth termination
- backend X session establishment
- route selection and fallback
- stats/admin diagnostics
- parser and policy hooks
- full rule engine
- GR notifications
17. Final Recommendation
Build mysqlx as an in-process ProxySQL plugin with its own worker threads and a narrow core extension API.
Keep:
- shared topology and canonical user identity in core-managed tables
- X-specific overrides, routes, and future rules in plugin-specific tables
Implement first:
- frontend auth termination
- dual-mode account resolution
- backend X session management
- route selection over shared hostgroups
Design now, but defer implementation of:
- full X-aware rule engine
- X-based Group Replication notifications
This is the best match for:
- deep integration
- plugin separability
- future extensibility
- minimal contamination of ProxySQL classic protocol internals