Previously, plugin-chassis stored authoritative mysqlx runtime state
inside admin-db tables (runtime_mysqlx_users / _routes / _backend_
endpoints / _variables). LOAD/SAVE commands shuffled rows between
mysqlx_<X> and runtime_mysqlx_<X> via plain INSERT ... SELECT, and
MysqlxConfigStore::load_from_runtime read the runtime_<X> tables back
out into its in-memory map. The data lived in three places (editable
mysqlx_<X>, persistent runtime_mysqlx_<X>, in-memory store) with no
detection of skew between them. Issue #5687.
The canonical pattern (mysql_users / GloMyAuth / runtime_mysql_users
in lib/ProxySQL_Admin.cpp::__refresh_users / save_mysql_users_runtime
_to_database) keeps Admin and the module strictly separated:
Admin owns the editable configuration table and
provides a runtime_<X> view of module state
Module owns runtime state in its own data structures
runtime_<X> is rebuilt on demand from module state, not
persistent storage
This commit restructures the mysqlx plugin to match.
# MysqlxConfigStore: per-entity install / save / project triplets
Replaces the monolithic load_from_runtime() with three independent
operations per entity (users / routes / endpoints / variables):
install_<X>_from_admin(db, err)
LOAD <X> TO RUNTIME path. SELECT the editable mysqlx_<X>
table (and the cross-module runtime_mysql_users /
runtime_mysql_servers projections where applicable), build a
new local representation, atomically swap into the in-memory
store under the store's mutex.
save_<X>_to_admin_table(db)
SAVE <X> [FROM RUNTIME] TO MEMORY path. Mirror save_mysql_
users_runtime_to_database(false): mark all rows in mysqlx_<X>
inactive, then upsert the live store contents with active=1.
Inactive rows the operator deactivated but didn't delete are
preserved.
project_<X>_to_runtime_view(db)
Runtime-view refresh path invoked by the chassis before any
admin SELECT touches runtime_mysqlx_<X>. Mirror save_mysql_
users_runtime_to_database(true): DELETE the projected table,
then INSERT live store contents.
install_all_from_admin() is a convenience wrapper that runs all four
in sequence; production code calls the per-entity methods so each
LOAD command only touches its own slice of state, and unit tests
have a single entry point that exercises the whole pipeline.
# MysqlxConfigStore data-model additions
- MysqlxResolvedIdentity gains `comment` (preserved through round-
trip; the canonical mysql_users path also preserves comments).
- MysqlxRoute gains `comment` for the same reason.
- New public MysqlxBackendEndpointOverride struct (replaces the
file-local MysqlxEndpointOverride that used to be in the .cpp).
- New endpoint_overrides_ map: per-(hostname,mysql_port) overrides
preserved verbatim across LOAD calls so SAVE can round-trip and
so the runtime-view projection can faithfully reflect what was
loaded. Previously these overrides were dropped after being
folded into hostgroup_endpoints_.
# Plugin: register_runtime_view + rewritten LOAD/SAVE callbacks
In mysqlx_admin_schema.cpp:
- Removes copy_table() and reload_config_store(); they were the
INSERT...SELECT shovel between editable and runtime tables that
encoded the architectural mistake.
- Each load_<X>_to_runtime callback now calls install_<X>_from_
admin(*ctx.admindb, err) and never touches runtime_mysqlx_<X>.
- Each save_<X>_from_runtime callback now calls save_<X>_to_admin_
table(*ctx.admindb) and never reads runtime_mysqlx_<X>.
- rows_affected on LOAD now reports the active row count in the
editable table (the source); on SAVE it reports the row count
in the editable table after the dump (the destination).
- Adds four refresh_<X>_runtime_view free functions and registers
them via services.register_runtime_view() during schema
registration. The chassis invokes these before any admin SELECT
against the projected table.
In mysqlx_plugin.cpp::mysqlx_start():
- Drops copy_to_runtime() entirely (the old "copy editable
mysqlx_<X> to runtime_mysqlx_<X> at startup" step that no longer
has a purpose).
- Replaces the single load_from_runtime call with the four
install_<X>_from_admin calls so a failure in one entity is
surfaced individually in the log.
- Keeps sync_disk_to_memory() unchanged. Disk-tier persistence is
legitimate admin behaviour; only the runtime-tier copy was wrong.
# Net effect at the SQL level
- INSERT INTO runtime_mysqlx_<X> ... no longer happens on any LOAD
or SAVE. The only writes to runtime_mysqlx_<X> are the on-demand
projections, triggered by the chassis pre-SELECT refresh path.
- SELECT FROM runtime_mysqlx_<X> always reflects current
MysqlxConfigStore state, even if the operator never ran LOAD
since the last edit to mysqlx_<X>.
- mysqlx_<X> remains the editable table the operator writes to.
The bug-stay-out-of-runtime contract documented in
include/ProxySQL_Plugin.h (the rewritten doc block) now matches
what this plugin actually does.