mirror of https://github.com/sysown/proxysql
feature/aws-rds-monitor
v3.0_fix_ci-mysqlx-cache-and-soak
GH-Actions
v3.0
v3.0_fix_codecov-coverage-path-prefix
issue-1288-load-mysql-variables-feedback
ci/fix-pgsql-socket-g1-cache-key
v3.0_fix_ci-mysqlx-fetch-depth
cleanup/drop-unreachable-port-defaults
feature/pgsql-native-backend-protocol
fix/pgsql-unix-socket
ci-pgsql-socket-g1
v3.0_fix_coverage-gcov-prefix-strip
fix/pgsql-omit-port-zero
aws-rds-bg
fix/jemalloc-page-size-auto-detect
docs/passthrough-auth-spec
feature/ci-codecov-tap-all-groups-callers
fix/run-tests-backtick-leak
ci/zstd-level-15
feature/ci-codecov-tap-all-groups-callees
v3.0_partition-fairness
feature/ci-codecov-tap-legacy-g2
v3.0_partition-gate
fix/ci-cache-restore-path
v3.0_fix-stale-pause-until
feature/perf-improvements-test2
fix/kill-proxysqlgenai-build-flag
feat/passthrough-auth
ci-trigger-tolerate-cleanup-401
fix/5790-mariadb-collation-255
fix/parsersql-1.0.3-pg-set-fixes
issue_5639
pgsql_dns_cache
fix/5755-followup-typecast-digest-fixtures
v3.0_merge-5776-5784
v3.0_latency_consistency_improvement
fix/galera-g5-cluster-start
fix/ghcr-pull-retry
v3.0_cap_violation_5767
ci-mariadb10-galera-GH-Actions
ci/fix-gr-g5-cluster-start
ci-mysql84-gr-g1-g9-GH-Actions
perf/pull-ci-base-from-ghcr
fix/split-test-groups
fix/rename-set-parser-workflow
gh-actions/add-set-parser-algorithm-3-g1
fix/4760-advertise-lenenc-auth-capability
feature/mysqlx-stack-consolidated
feature/mysqlx-asan-coverage-docker-isolation
fix/ci-unit-tests-tsan-project-name
feature/mysqlx-tsan-v3-companion
feature/ci-builds-add-tsan-matrix
ci-reduce-polling-interval
feature/mysqlx-tsan-workflow-only
feature/mysqlx-ci-validation-workflows
feature/mysqlx-test-leak-cleanup
feature/mysqlx-behavioural-tap
feature/mysqlx-parity-cleanup
ci-g5-enable-cluster
feature/mysqlx-tls-passthrough
feature/mysqlx-asymmetric-tls
feature/mysqlx-state-machines
feature/build-tsan-plumbing
feature/mysqlx-observability-p0
fix/mysqlx-review-findings
v3.0-test-ci
ci/fix-mysql-apt-key-expired-v2
ci/fix-mysql-apt-key-expired
mariadb-rpl-helper
fix-fc-parsing
ci/fix-upload-artifact-eacces
ci/fix-cache-prune-permissions
ci/shrink-test-cache
ci/fix-tap-build-target
ci/gh-actions-readme-pointer
ci/fix-mysql84-infradb-label
ci/add-missing-group-reusables
infra-mysql57-binlog
feature/pgbouncer-compat
v3.0_pgsql_sslkeylog_5281
fix/5554-resolution-family-limitation
fix/3p-ci-error-handling
v3.0-5493
v3.0-ci260322_cluster
copilot/extract-server-selection-algorithm
copilot/extract-health-state-logic
copilot/extract-query-rule-matching-logic
copilot/extract-connection-pool-logic
v3.0-set_parser_v3
feature/arm-builds
release-notes-3.0.6-4.0.6-draft
v3.0.6-add-tap-test_stats_table_check
v2.7.3-test260221
v4.0-mcp-stats
copilot/uninstall-amazon-linux-2023
fix-prometheus-labels-test
tap-mcp-client
agent-skill-tap-test
v4.0-tsdb1
v3.0-fix_5256
gh-pages
feature/modern-docs
v4.0
v4.0-fix-vec-search
v4.0_rag_sys_prompt
v4.0_rag_mcp
v4.0-tsdb
feature/v4-docs-init
otel_system_libs
otel_clean
v3.0-5288
otel
otel_2
fix/postgresql-cluster-sync
v3.0-releate_notes_scripts_fixes
test_gh-actions_triggers
postgresql-digest-testing-improvement
v3.0_select_auto_commit
v3.0-5218
fix-5221
fix/5186-proxysql-stop-admin-crash
v3.0-4951
add-claude-github-actions-1763877527835
fix-rpm
v3.0-DS_crash
add-claude-github-actions-1763663272333
add-claude-github-actions-1763663091346
add-claude-github-actions-1763663091411
add-claude-github-actions-1763476725261
add-claude-github-actions-1763476725489
v3.0_optimizations_and_stability
v2.7.3.1
v3.0.3-upgrade_json
v3.0.sonar-cli
v3.0.sonar-config
otel-tracepoint
v3.0.2-merge-upgrade_deps-add_new_distros
v3.0.2-upgrade_deps
v3.0.2-add_new_distros
v3.0-add_more_testing_groups
v3.0-upgrade_prometheus-cpp
v3.0-upgrade_json
v3.0-upgrade_sqlite3
v3.0-upgrade_libmicrohttpd
v3.0-upgrade_curl
v3.0-add_centos10_builds
v3.0-add_fedora42_builds
v3.0_PG_PrepStmt
v3.0-sliced_groups
v3.0_auth_negotiation
v2.7
v2.7-fix_run_name
v3.0_4799_4827
v3.0-3687
v2.7-pmm_runtime_servers_metrics
v2.7-4839
v2.7-4841
v2.7-bump_version_to_2.7.3
2.6.6-4841
v2.x_pg_PrepStmtBase_240714
v3.0-4803-4817
v3.0-4803
v2.7-minorBugs
v3.0-privates
v2.x-logging_mem_2
v2.7_fix
v2.7_amd64_build_fix
v2.7-fix_aux_threads_ssl_leaks
v2.7-fix_ssl_params_leak
v2.7-rm_malloc_conf_on_version
v2.7_compression
v2.7-actions-add-3p-tests-parameter
none
v2.7-fix_hang_on_resume
v2.x-logging_mem
v2.7_servers_defaults
v2.7-mariadb_column_metadata_integrity_check
ssl_optimization
v2.7_reg_test_4716_single_semicolon
v2.7_issue_4707_threshold_resultset_size
v2.7_reg_test_4723_query_cache_stores_empty_result
2.7_randomized_cache_ttl
v3.0_fix_multiple_builds
v3.0_servers_defaults
v2.7-update_actions_triggers_v2
v2.7-update_actions_triggers
v2.6
v2.6.x-update_triggers
v2.6-4646
v2.7.1-update_actions
v2.x
v2.6.x-testing-global-multiplexing-disabled
use-wrlock-in-dns-cache-empty
v2.6.x-fix-darwin
v2.x-admin_list_ciphers
v2.x-sqlite3_pass_exts
v2.x-tap_tests_opt_ssl
v2.6.0-update_to_libhttpserver_v0.19
v2.x_router_2ports
v2.6.0-update_to_openssl_v3.1.5
v2.x-2411025
v2.x-profiling_poc1
v2.x_sha2pass_draft2
v2.x-webui_fixes
v2.6.0-more-makefile-fixes
v2.x-20230914_test
v2.x-20230913_test
v2.5.5-branch
v2.5.5-branch_255_patches
v2.x-aurora_autodiscovery-refactor_cluster_mysql_servers-gr_bootstrap_mode_2
v2.x_mysql_connector_j_fixes
v2.6-deprecate_old_clickhouse
v2.x_refactor_cluster_mysql_servers
v2.x-aurora_autodiscovery
v2.x-zd70545
v2.x-aurora_autodiscovery_shunned_promotion
v2.x-tap20230609
v2.x-test20230530
v2.x_sha2pass_draft2-TEST
v2.x-session_track_system_variables_v2
v2.x-status-variables-for-set-stmts
v2.x-enable_session_state_trackers
v2.x-increase-logging-eof_fast_forward-t
v2.x-3863-special-query
v2.x-session_track_system_variables
v2.x_refactor_read_only_action
v2.x_sha2pass_draft1
v2.2.0-sqliteserver_read_only
v2.x-digest_umap_aux-comparison
v2.4.8
v2.x-4105_4114
v2.x-3583-server_closed_conn
v2.x-group_replication_rework-SHUNNED_promotion
v2.1.0-var-global-multiplex
v2.x-CI-hostname-tap-test-fixes
v2.x-limit-version-check
v2.x-fix_deprecate_eof_warning
v2.x-3698
v2.x_tidb_replica_read
v2.x-HostGroups_attributes
v2.0.18.221009
v2.x-ci_reg_test_3273_ssl_con
TAP_test_restapi
v2.x-tap_tests_groups
v2.x-tap_test_sqlite3_server-t
PRS_3888_3903_2
PRS_3888_3903
v2.x_code_refactor_2206
v2.x-multipacket_poc_1
v2.x-impr_hg_latency_obsv
v2.x-gcc-warnings
v2.x-hg_lock_session_id
v2.x-3768
v2.x-3371
v2.x-ci_verifications
v2.x-thread_local_qps_limit
v2.x-parser_table
v2.1.1-3207
v2.x-qps_limits
v2.x-3711
v2.x-3642
v2.x-3674
v2.x-ssl3_warnings
V2
v2.3.2
v2.3.2_3646_3647
v2.x-client_err_limit_conn_timeout
v2.x-keep_multiplexing_regression_fix
v2.3.2-3628
v2.2.2-to-v2.3.0-7
v2.2.2-to-v2.3.0-7_merge
v2.2.2-to-v2.3.0-6
v2.2.2-to-v2.3.0-6_merge
v2.2.2-to-v2.3.0-5
v2.2.2-to-v2.3.0-4
v2.2.2-to-v2.3.0-3
v2.2.2-to-v2.3.0-2
v2.2.2-to-v2.3.0-1
v2.3.1
v2.0.14-70226
v2.3.0
v2.x-client_err_limit-gr_replication_lag_action
v2.2.2
v2.2.1-3603
v2.2.1-centos7-ASAN
v2.2.1
v2.2.1-3601
v2.2.1-3599
v2.2.1-3597
v2.2.1-3595
v2.2.0-restapi_server_exc_log
v2.x-3574
v2.x-3558
v2.2.0-3546-centos-7-gcc-8
v2.x-3549
v2.x-cluster_large_mysql_users
v2.x-cov_ci_verification
v2.0.14-tb1
v2.0.14-tb1-3494
v2.0.14-tb1-3488
v2.0.14-tb1-3117
v2.0.14-tb1-2762
v2.0.14-2762
v1.4.13-arm
v2.1.1-3296
v2.2.0
v2.0.18
v2.1.1
v2.0.18-3342
v2.0.18-3182
v2.1.1-3184
v2.1.0-revert-da7fdfe14
v2.0.18-revert-da7fdfe14
v1.4.13-70160
v2.0.18-3354
v2.0.18-3350
v2.0.14-3339
1.4.13-70160
v2.0.18-3339
v2.1.1-3317
v2.1.1-3319
v2.0.18-3317
v2.1.2-LBalgo
v2.0.18-1574
v2.1.2-hgman
v2.0.17
v2.1.0
v2.0.17-3288
v2.0.17-3276
v2.0.17-3273
v2.0.16
v2.0.16-3267
v2.0.16-3265
v2.0.16-3262
v2.0.16-3261
v2.1.1-3252
v2.1.1-collation
v2.0.16-3252
v2.0.16-collation
v2.1.0-parser
v2.0.16-3219
v2.0.16-3216
v2.0.16-3201
v2.0.16-2330
revert-3191-v2.0.16-3190
v2.0.16-3204
v2.0.16-3177
v2.0.16-2619
v2.0.16-3190
v2.0.16-3187
v2.1.0-70118
v2.0.16-3133
v2.0.16-3133_ci_verification
v2.0.16-3150
v2.0.16-change_user
v2.0.15
v2.0.15_amd64_fix
v2.0.15_arm64_packages
v1.4.14-ssl
v2.0.15_arm64
v2.1.0-2820
v2.0.15-sslbug
v2.0.15-KillTrx
v2.0.14
v2.0.14-ch_build_fix
v2.0.14-focal
v2.0.14-valgrind20200904
v2.1.0-3042
v2.0.14-3035
v2.0.14-3036
v2.0.14-2955
v2.0.14-vars
v2.0.14-3005
v2.0.14-3003
v2.0.14_2970_2979
v2.0.14-NOTSOCK
v2.1.0'
v2.0.14-2958
v1.4.10-zd
v2.0.13
v2.0.13-autocommit_fix
v2.1.0-2892
v2.0.13-2711
v2.0.13-duplicated_variables
v2.0.13-duplicated_variables_for_2.1.0
v2.0.12-deprecate_eof
v2.1.0-1377
v2.1.0-admin_queries
v2.0.12-var-global-multiplex
v2.1.0-var-foreign-key
v2.0.12
v2.0.12-tab-small-log
v2.0.12-var-foreign-key
v2.0.12-var-long-query-time
v2.0.12-galera-shunned
v2.1.0-admin_queries_2
v2.1.0-tap-rm-config
v2.0.12-tap-rm-config-test
v2.1.0-QP_stmt_3
v2.0.11-fix-multi-2-ci
v2.0.11-fix-multi
v2.0.11-266_0-3
2.1.0
v2.0.11
v2.1.0-track-vars
v2.1.0-track-variables
v2.0.11-track-variables
v2.0.11-2526
v2.0.11-tap-tests
v2.0.13-2698-commit1
v2.0.10-galera-pxc-maint-mode
v2.0.11-track-vars
v2.0.10-2647
v2.0.11-track
v2.0.11-track-session-vars
v2.0.9-var-array-review
v2.0.11-stats
v2.0.10
v2.0.10-centos67
v1.4.14.2
1.4.14.2
v1.4.14-show-warnings
v2.0.9
v2.0.9-var-array_2
v2.0.9-var-array
v1.4.16
v2.0.8
val214-changing_charset
v2.0.6
v1.4.16-1922_2
v1.4.13.2
v2.0.4-charset248
v2.0.5
v1.4.10-67841
v2.0.4
v2.0.4-sqlite327
v2.0.3
v2.0.2
v1.4.15
v2.0.1
v1.4.14
v2.0.0
v1.4.14-ping_shun
v1.4.14-1828
v1.4.14-latency_awareness
v1.4.12
v1.4.13-admin_deadlock
v2.0.0-improve_speed
v1.4.13
v1.4.13-autocommit_revert
v1.4.11.2
v1.4.13-ps
v2.0.0_bionic_deb_fix
v2_962
v1.4.12-1640
v1.4.11-names_tz
v1.4.12-1693
master
v1.4.11
v1.4.10
v1.4.6
v1.3.10
jenkins_test
v2.0.0-cachegrind
v1.4.9
v2.0-lab
v149_1511
v149_1382
v1.4.7-f2
v1.4.7-f1
v149_1491
v1.4.5-kub
v1.4.8
v2.0-web2
v1.4.3
v1.4.7
bsd_install_update
v1.4.2
v1.4.1-ch2
v1.4.1
v1.3.9
v1.4.1-ch
v1.3.8
v1.3.8-dev
v1.3.7
v1.3.7-dev
v1.3.6-dev
v1.4.0-clickhouse
v1.4.0
v1.3.6
v1.3.5
v1.3.5-dev
v1.4.0-955
v1.3.4
v1.3.4-dev
v1.3.3
v1.3.3-dev
v1.3.2
v1.3.2-dev
v1.3.2-766
v1.3.0h
v1.3.1-utf8mb4
1.4.0-840
v1.3.1
v1.2.6
v1.3.0
v1.4.0-797
v1.2.5-715
v1.2.5
v1.2.4-lowmem
v1.3.1-dev-mem
v1.2.0
connleak
lab-1.2.0
v1.1.2
T107_add_proxysql_consul_requirements
T89_write_consul_integration_doc
T98_consul_multi_table_config
mongoose
evhttp
SQLiteServer
1.0
3.0.9
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
2.7.3
2.6.6.1
3.0.1
2.7.2
3.0.0
2.6.6
2.7.1
2.7.0
2.6.5
2.6.4
2.6.3
2.6.2
2.6.1
2.5.5
2.6.0
2.5.4
2.5.3
2.5.2
2.5.1
2.4.8
2.5.0
2.4.7
2.4.6
2.4.5
2.4.4
2.4.3
2.4.2
2.4.1
2.4.0
2.3.2
2.3.1
2.3.0
2.2.2
2.2.1
2.2.0
2.0.18
2.1.1
2.0.17
2.0.16
2.0.15
2.0.14
2.0.13
2.0.12
2.0.11
2.1.0
2.0.10
2.0.9
2.0.8
2.0.7
2.0.6
2.0.5
2.0.4
2.0.3
2.0.2
1.4.16
1.4.15
2.0.1
1.4.14
1.4.13
1.4.12
1.4.11
1.4.10
1.3.10
1.4.9
2.0.0
1.4.8
1.4.7
1.4.6
1.4.4
1.4.3
1.4.2
1.3.9
1.3.8
1.4.1
1.3.7
1.4.0
1.3.6
1.3.5
1.3.4
1.3.3
1.3.2
1.3.1
1.3.0h
1.3.0g
1.3.0f
1.3.0e
1.3.0d
1.3.0c
v1.3.0b
1.4.5
v1.1.0
v1.1.0-rc
v1.1.1-beta
v1.1.1-beta.1
v1.1.1-beta.2
v1.1.1-beta.3
v1.1.1-beta.4
v1.1.1-beta.5
v1.1.1-beta.6
v1.1.2
v1.2.0a
v1.2.0b
v1.2.0c
v1.2.0d
v1.2.0e
v1.2.0f
v1.2.0g
v1.2.0h
v1.2.0i
v1.2.0j
v1.2.0k
v1.2.1
v1.2.2
v1.2.3
v1.2.4
v1.2.4.0923
v1.2.5
v1.2.6
v1.3.0
v1.3.0a
v1.3.0g
v1.3.0h
v1.3.1
v1.3.10
v1.3.2
v1.3.2-1
v1.3.3
v1.3.4
v1.3.5
v1.3.6
v1.3.7
v1.3.8
v1.3.9
v1.3.9-prev.1
v1.4.0
v1.4.1
v1.4.10
v1.4.11
v1.4.12
v1.4.13
v1.4.14
v1.4.15
v1.4.16
v1.4.2
v1.4.3
v1.4.4
v1.4.5
v1.4.6
v1.4.7
v1.4.8
v1.4.9
v2.0.0-beta.1
v2.0.0-rc1
v2.0.0-rc2
v2.0.1
v2.0.10
v2.0.11
v2.0.12
v2.0.13
v2.0.14
v2.0.15
v2.0.16
v2.0.17
v2.0.18
v2.0.2
v2.0.3
v2.0.4
v2.0.5
v2.0.6
v2.0.7
v2.0.8
v2.0.9
v2.1.0
v2.1.1
v2.2.0
v2.2.1
v2.2.2
v2.3.0
v2.3.1
v2.3.2
v2.4.0
v2.4.1
v2.4.2
v2.4.3
v2.4.4
v2.4.5
v2.4.6
v2.4.7
v2.4.8
v2.5.0
v2.5.1
v2.5.2
v2.5.3
v2.5.4
v2.5.5
v2.6.0
v2.6.1
v2.6.2
v2.6.3
v2.6.4
v2.6.5
v2.6.6
v2.7.0
v2.7.1
v2.7.2
v2.7.3
v3.0.0-alpha
v3.0.1
v3.0.2
v3.0.3
v3.0.4
v3.0.5
v3.0.6
v3.0.7
v3.0.8
v3.0.9
v3.1.6
v3.1.7
v3.1.8
v3.1.9
v4.0.6
v4.0.7
v4.0.8
${ noResults }
5296 Commits (6cdd4e697eb86982e3dfd2ceab009ead5da0efcb)
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
6cdd4e697e |
fix: correct lock unlock order in Authentication classes
Fix lock order reversal in MySQL_Authentication, ClickHouse_Authentication, and PgSQL_Authentication. In memory_usage() and dump_all_users(), locks were acquired in order (frontends, backends) but released in the same order instead of the reverse. This creates a potential deadlock if another thread attempts to acquire the same locks in opposite order. Swap unlock calls to release backends before frontends, matching the reverse of the acquisition order. |
2 months ago |
|
|
967ab401ec |
genai: fix lifecycle reloads and drop dead MCP stats registration
Load GenAI and MCP variables before runtime initialization, reinitialize the AI stack on reload, and make the anomaly hook obey the configured enable flags. Restore the missing MCP command aliases and the disk/config reload verbs, fix the MySQLX packaging filename mismatch, and update the unit harness for the plugin ABI. Also remove the stale stats_mcp_* registrations from the plugin surface. There is no plugin-side writer for those tables in this branch, so advertising them as live schema was misleading. Update the unit test and bootstrap comments to match the actual exposed surface. |
2 months ago |
|
|
b6b6a4f62b |
fix(genai): properly gate sqlite-vec hook on PROXYSQLGENAI
Reverts |
2 months ago |
|
|
dffa89de7a |
fix(ci): repair non-PROXYSQL40 dbg builds + genai unit-test linking
CI surfaced two separate build breakages that had been latent since
the carve-out steps but only became visible once they ran on the
right matrix variants.
# lib/MySQL_Session.cpp + lib/PgSQL_Session.cpp: chassis dispatch
# block needs PROXYSQL40 guard
The query-hook dispatch I added in Step 2.2 references chassis types
(`ProxySQL_PluginProtocol`, `ProxySQL_PluginQueryHookPayload`,
`ProxySQL_PluginQueryHookResult`, etc.) without an `#ifdef PROXYSQL40`
guard. Those types are only declared when ProxySQL_Plugin.h is
compiled with `-DPROXYSQL40` — v3.0/v3.1 dbg builds (debian12,-dbg
and ubuntu22,-tap on the CI matrix) define neither, so the lib step
fails:
MySQL_Session.cpp:4477:127: error: 'ProxySQL_PluginProtocol' has
not been declared
MySQL_Session.cpp:4477:85: error: 'proxysql_has_configured_plugin
_query_hook' was not declared
in this scope
[+ 7 more identical errors in PgSQL_Session.cpp]
Wrap both dispatch blocks (MySQL_Session.cpp:~4477-4509,
PgSQL_Session.cpp:~2414-2446) in `#ifdef PROXYSQL40`. The block was
only meaningful when the chassis is present anyway — the call to
`proxysql_has_configured_plugin_query_hook` short-circuits to false
when no plugin is loaded, but the SYMBOL doesn't exist at all in
non-chassis builds.
# test/tap/tests/unit/Makefile: PROXYSQLGENAI autodetect probe was
# defining the macro without a way to satisfy the resulting linker
# requirements
Pre-Step-7 the autodetect probed for `MCP_Tool_Handler` in
libproxysql.a; this symbol is plugin-side now, so the probe never
matches and PROXYSQLGENAI stays empty. My previous attempt to
"fix" the autodetect (probing `invoke_register_schemas_phase`) made
it match again — but that defines PROXYSQLGENAI for the WHOLE unit-
test build, including tests like `genai_llm_clients_unit-t` and
`genai_mcp_endpoint_unit-t` that pull in plugin headers AND
instantiate plugin classes. Those classes' definitions live in
plugin .cpp files that the unit-test build does NOT link, so the
result was:
genai_llm_clients_unit-t.cpp:201: undefined reference to
`LLM_WriteCallback(void*, unsigned long, ...)' [+5 more]
Solution: never auto-define PROXYSQLGENAI for the unit-test build.
Tests that legitimately exercise plugin internals
(`genai_plugin_anomaly_unit-t`, `genai_plugin_backend_client_unit-t`,
`genai_plugin_load_unit-t`, `genai_fts_string_unit-t`) declare a
new GENAI_PLUGIN_DEFINES variable on their dedicated rule and add
the specific plugin .cpp files they need to the link line. Every
other genai_*_unit-t test guards its body in `#ifdef PROXYSQLGENAI
... #else SKIP #endif`, so falling through to the SKIP path keeps
CI green. Coverage for those tests in non-genai builds was already
zero — the autodetect was never activating PROXYSQLGENAI for them in
practice (the probed symbol moved out in Step 6). This commit
documents that reality instead of pretending to maintain coverage
that didn't exist.
# test/tap/tests/unit/genai_fts_string_unit-t.cpp: stub for
# MySQL_Tool_Handler::execute_query
The new dedicated rule for genai_fts_string_unit-t compiles
plugins/genai/src/MySQL_FTS.cpp directly into the test binary.
MySQL_FTS::index_table calls MySQL_Tool_Handler::execute_query — but
this test only exercises the pure string helpers (sanitize_name /
escape_*) and never reaches index_table, so the call site is
unreachable at runtime. The linker still needs a body, so we
provide an empty stub directly in the test .cpp. The real
implementation lives in MySQL_Tool_Handler.cpp which we can't link
without dragging in the full plugin runtime stack.
|
2 months ago |
|
|
482ce386cd |
chore(genai-carveout): strip dead PROXYSQLGENAI blocks + plugin hardening
Two threads of cleanup that had to land together because they touch
both core (where the dead code lives) and the plugin (where the new
module-owned state lives):
# Strip dead #ifdef PROXYSQLGENAI from lib/, include/, src/ (Spec Q9)
Step 7 of the carve-out left PSQLGA empty in lib/Makefile, so every
#ifdef PROXYSQLGENAI block in the core build became permanently false.
The blocks remained as dead text (visible to readers, irrelevant to
the binary). Strip ~600 lines spanning:
src/main.cpp 2 sites
lib/Admin_Bootstrap.cpp 3 sites (mcpdb open + attach +
flush_*_variables hooks)
lib/Admin_FlushVariables.cpp 2 sites (~250 lines of stub +
#if 0 _ORIGINAL bodies)
lib/Admin_Handler.cpp 5 sites (LOAD/SAVE GENAI VARIABLES
dispatch + LOAD/SAVE MCP
VARIABLES dispatch +
MCP profiles handler)
lib/ProxySQL_Admin.cpp ~10 sites (init_*_variables +
load_mcp_server +
load/save_mcp_query_rules
+ GenericRefreshStatistics
MCP probes)
lib/ProxySQL_Admin_Stats.cpp 3 stats___mcp_* function bodies
include/proxysql_admin.h 7 method declarations
include/ProxySQL_Admin_Tables_Definitions.h 4 unused DDL macros
The plugin now owns LOAD/SAVE MCP VARIABLES, LOAD/SAVE GENAI VARIABLES,
LOAD/SAVE MCP PROFILES, LOAD/SAVE MCP QUERY RULES via its admin command
registry; the chassis dispatcher catches them before any of the dead
core branches would have fired.
Caveat — known break: the `stats_mcp_*` tables are still registered
(empty schema) but the populator path that fed them is gone. SELECTs
return empty result sets until a follow-up wires the populator into
the plugin (likely via register_runtime_view or a parallel stats-side
ABI). Tracked separately.
# Plugin hardening (review-feedback items 4-7)
* plugins/genai/Makefile: add -fvisibility=hidden,
-fvisibility-inlines-hidden, -fstack-protector-strong (mirrors
plugins/mysqlx). Tag proxysql_plugin_descriptor_v1 with
__attribute__((visibility("default"))) so the loader's dlsym still
resolves; without the explicit annotation the hidden-default flag
would also hide the entry point.
* plugins/genai/src/Anomaly_Detector.cpp: convert
genai_anomaly_embed_fn to std::atomic<…> with relaxed loads/stores.
The detector hot-path read races with init/stop on the lifecycle
thread; atomics give us a portable no-torn-pointer guarantee
without paying for acquire/release fences we don't need (no other
state synchronises with the pointer). Replaces the hand-wavy
"no mutex needed" comment with the actual reasoning.
* plugins/genai/src/{plugin_main,plugin_commands,plugin_tables}.cpp:
route the 17 fprintf(stderr, "genai plugin: ...") log lines
through a new genai_log() helper that calls
services->log_message when the chassis is wired up and falls back
to stderr for early-init / unit-test paths. Lines now land in
proxysql.log alongside the rest of the runtime.
* plugins/genai/include/MCP_Thread.h: add get_variable_string(name,
std::string&) returning a std::string, and switch
mcp_save_variables_to_admindb (plugin_main.cpp) to it. The legacy
get_variable(name, char*) had an unbounded sprintf into a stack
buffer and the SAVE path was passing char val[256] — a real
overflow risk for *_endpoint_auth bearer tokens.
* plugins/genai/src/plugin_main.cpp: rewrite the genai_init / genai_stop
comment block with the actual teardown ordering rationale (consumers
before producers; atomic-clear-then-delete for the embed hook;
why Prometheus counters stay registered).
|
2 months ago |
|
|
25e4388ae6 |
feat(plugins/genai): Step 7 follow-up — Phase B schema registration + ai-g1 plugin wiring
Three small but related fixes that unblock running the ai-g1
integration suite against the post-Step-7 build.
1. Plugin: register MCP admin tables in Phase B (register_schemas),
not Phase C (init).
The chassis ABI guarantees that tables registered via
`services->register_table` from a plugin's `register_schemas`
callback are merged into the admin SQLite schema BEFORE admin
bootstrap creates the DB. Registering at init() instead is too
late: by then the DB is already created without the
plugin-declared tables.
The genai plugin was registering at init(). Symptom: at
plugin start (Phase D) the call into
`mcp_load_target_auth_map_from_admindb` errored out with
"no such table: runtime_mcp_auth_profiles" because the table
never got created.
plugins/genai/src/plugin_main.cpp:
- New `genai_register_schemas` callback that invokes
`genai_register_admin_tables(services)`.
- Plugin descriptor gains the `register_schemas` field
(mirrors plugins/mysqlx pattern; Step 0's
PROXYSQL_PLUGIN_ABI_VERSION = 2 supports it).
- Removed the redundant call in genai_init().
2. Core: drop PROXYSQLGENAI gates that Step 7 left around the SET
mcp-X / SET genai-X validation paths.
Step 7 stopped passing -DPROXYSQLGENAI to core compiles, so the
`#ifdef PROXYSQLGENAI` blocks I'd left in lib/Admin_Handler.cpp
for the loose-prefix `mcp-*` / `genai-*` validators (Step 4.F /
Step 5) silently expanded to nothing. Symptom:
`SET mcp-port=6071` admin SQL got "Unknown global variable".
lib/Admin_Handler.cpp:
- Loose mcp-* / genai-* prefix checks in is_valid_global_variable
are now unconditional (no #ifdef).
- The MCP VARIABLES disk-side handler (LOAD MCP VARIABLES FROM
DISK / SAVE MCP VARIABLES TO DISK) is also un-gated; it's pure
SQL DML against global_variables LIKE 'mcp-%' with no plugin
runtime dependency. The plugin-side runtime push remains the
LOAD MCP VARIABLES TO RUNTIME verb registered in
plugin_commands.cpp.
3. Test infra: bind-mount the genai plugin .so + per-group cnf for
ai-g1, mirroring the mysqlx-soak / mysqlx-e2e pattern.
Without these, the ai-g1 setup hook fails at SET mcp-port because
the proxysql container's cnf doesn't list `plugins=("...")` and
the .so isn't visible inside the container.
test/infra/control/start-proxysql-isolated.bash:
- PROXYSQL_LOAD_GENAI_PLUGIN=1 (set by the group's env.sh) now
triggers a `-v ${WORKSPACE}/plugins/genai/...so:/usr/lib/proxysql/...so:ro`
bind into the proxysql container.
test/tap/groups/ai/proxysql-ci.cnf (new):
- Identical to test/infra/control/proxysql-ci.cnf except for the
trailing `plugins=("/usr/lib/proxysql/ProxySQL_GenAI_Plugin.so")`
line that asks the chassis to load the genai plugin at
Phase A.
test/tap/groups/ai-g1/env.sh:
- PROXYSQL_LOAD_GENAI_PLUGIN=1
- PROXYSQL_CONFIG_OVERRIDE=…/test/tap/groups/ai/proxysql-ci.cnf
After this commit the ai post-infras hook (`MCP configuration
completed` … `MySQL test data seeding completed` … `PostgreSQL test
data seeding completed` … `AI post-infras hook completed`) runs to
completion against the post-carve-out proxysql + genai plugin .so.
|
2 months ago |
|
|
62ed0660a7 |
feat(plugins/genai): Step 7 — drop PROXYSQLGENAI from core build
Last step of the GenAI plugin carve-out. The PROXYSQLGENAI macro is
no longer compiled into core: lib/libproxysql.a and the proxysql
binary build identically with or without `PROXYSQLGENAI=1`. The
user-facing build flag stays — it now means "also build the genai
plugin alongside proxysql" rather than "compile GenAI into the
binary".
What this commit does
plugins/genai/Makefile
Hardcodes -DPROXYSQLGENAI in the plugin's CXXFLAGS — the plugin
IS the GenAI surface, so the macro is always-on inside its own
compilation unit. Lets the existing `#ifdef PROXYSQLGENAI`
guards inside plugin source files keep working without a
massive guard-stripping pass (deferred follow-up cleanup).
lib/Makefile, src/Makefile
Drop the `PSQLGA := -DPROXYSQLGENAI` setter — core no longer
sees the macro on the compile line. PSQLGA stays as an empty
variable so the existing `$(PSQLGA)` cascades in CXXFLAGS
references stay structurally identical (cleanup deferred).
Also: lib/Makefile no longer compiles MCP_Tool_Handler.oo (the
base class moved to the plugin in this commit too — see below).
Top-level Makefile
Plugin-build rules switched from `$(if $(filter 1,$(PROXYSQL40))`
to `$(if $(filter 1,$(PROXYSQLGENAI))` — `PROXYSQL40=1` enables
the chassis but not necessarily the genai plugin. Skip-message
text updated to match. `PROXYSQLGENAI=1` still implies
`PROXYSQL40=1` per the existing tier hierarchy, so the user
invocation stays unchanged.
MCP_Tool_Handler base class
Moved to plugins/genai/ (`include/MCP_Tool_Handler.h` -> plugin
include, `lib/MCP_Tool_Handler.cpp` -> plugin tool_handlers).
Was the last GenAI-tier .cpp in core — only existed there
because AI/RAG_Tool_Handler still inherited from it before
Step 5. Now plugin-internal.
lib/proxy_sqlite3_symbols.cpp
Hook for sqlite_vec_init unconditional now (sqlite-vec extension
object always linked into proxysql, since the genai plugin
relies on it via this hook at dlopen time).
lib/MySQL_Thread.cpp / lib/PgSQL_Thread.cpp
`variables.ffto_enabled` default no longer flips to true when
PROXYSQLGENAI is set (the gate was: PROXYSQLFFTO + PROXYSQLGENAI
-> true; else false). With PROXYSQLGENAI removed, the default
is false everywhere; operators opt in via admin SQL.
CLAUDE.md
Feature-tier table now lists Plugin Chassis (PROXYSQL40) and
AI/MCP (PROXYSQLGENAI) separately, with a note explaining what
PROXYSQLGENAI=1 means post-carve-out. Conditional Components
section calls out that GenAI/MCP/RAG/LLM lives in plugins/genai/.
What this commit does NOT do (deferred)
- Stripping the `#ifdef PROXYSQLGENAI` guards from the plugin's
own source files (LLM_Bridge.cpp, GenAI_Thread.cpp, etc. all
have `#ifdef PROXYSQLGENAI ... #endif` brackets). The
plugin's Makefile force-defines the macro so they keep
expanding. A guard-stripping pass would be ~50 file edits
with zero behaviour change.
- Stripping the dead `#ifdef PROXYSQLGENAI` blocks from core
(lib/Admin_Handler.cpp etc. still have FIXME-stubbed blocks
inside `#ifdef PROXYSQLGENAI` from the 4.C / 5 surgery). With
the macro undefined in core's compile line, those blocks
silently expand to nothing — they're dead code already.
Cleanup follow-up.
- Removing the now-no-op stubs themselves
(init_mcp_variables, flush_mcp_variables___*, load_mcp_server,
stats___mcp_*, etc.). They linger as dead wrappers around
early-returns; their declarations in proxysql_admin.h linger
too. Cleanup follow-up.
All three plugin unit tests pass:
genai_plugin_load_unit-t : 25/25
genai_plugin_anomaly_unit-t : 6/6
genai_plugin_backend_client : 27/27
Carve-out scoreboard, FINAL:
Step 0-2 : infrastructure (chassis cherry-pick, ABI extensions)
Step 3 : ~1.1 K LOC Anomaly_Detector
Step 4.A : ~1 K LOC GENAI: / LLM: query-prefix removal
Step 4.B : ~0.5 K LOC backend_client helper
Step 4.C : ~13 K LOC MCP subsystem + 8 tool handlers + MySQL_FTS
Step 4.F : n/a runtime-reconfig admin SQL via plugin commands
Step 4.G : n/a MCP admin/config/stats tables via plugin ABI
Step 5 : ~7 K LOC GenAI/LLM/AI/RAG
Step 6 : ~5 K LOC AI_Vector_Storage / Discovery_Schema /
MySQL_Catalog / Static_Harvester pair
Step 7 : n/a PROXYSQLGENAI macro dropped from core build
Net core surface change: ~28 K LOC moved out of lib/ + include/
into plugins/genai/. src/proxysql binary shrunk from 197 MB
(pre-carve-out, all PROXYSQLGENAI compiled in) to 152 MB; plugin
.so grew to ~56 MB. Net process RSS roughly neutral.
|
2 months ago |
|
|
4552236700 |
feat(plugins/genai): Step 6 — move RAG/Vector/discovery surface into the plugin
Final file-move step in the carve-out: relocates the leaf modules
that didn't depend on (and weren't depended on by) anything still in
core. After this commit, the only PROXYSQLGENAI-guarded code
remaining in libproxysql.a is `MCP_Tool_Handler` — the base class
the plugin's AI_Tool_Handler / RAG_Tool_Handler inherit from — and
that lives in core only because removing it would force pulling
those derived plugin classes into core just for the inheritance
edge. Step 7 deletes the PROXYSQLGENAI macro entirely.
Files moved (git mv preserves history)
Headers (include/ -> plugins/genai/include/)
AI_Vector_Storage.h
MySQL_Catalog.h
Discovery_Schema.h
Static_Harvester.h
PgSQL_Static_Harvester.h
Sources (lib/ -> plugins/genai/src/)
AI_Vector_Storage.cpp
MySQL_Catalog.cpp
Discovery_Schema.cpp
Static_Harvester.cpp
PgSQL_Static_Harvester.cpp
Core changes were minimal because these are leaf modules:
include/proxysql.h: drop the AI_Vector_Storage.h include.
include/cpp.h: drop AI_Vector_Storage / Discovery_Schema /
MySQL_Catalog / Static_Harvester / PgSQL_Static_Harvester
includes (replaced with a single carve-out history comment that
Step 7 will delete entirely).
lib/ProxySQL_Admin.cpp: drop the Discovery_Schema.h include — its
only live use was via the now-#if-0'd save/load_mcp_query_rules
blocks 4.C left behind for follow-up reference.
lib/Makefile: drop AI_Vector_Storage.oo, MySQL_Catalog.oo,
Discovery_Schema.oo, Static_Harvester.oo, PgSQL_Static_Harvester.oo
from the GenAI _OBJ_CXX list.
Test fixture probe update
test/tap/tests/unit/Makefile: PROXYSQLGENAI auto-detect probe
switched from `AI_Vector_Storage` (now plugin-only) to
`MCP_Tool_Handler` (the last GenAI-tier symbol still in core).
Step 7 deletes the auto-detect entirely.
All three plugin unit tests pass:
genai_plugin_load_unit-t : 25/25
genai_plugin_anomaly_unit-t : 6/6
genai_plugin_backend_client : 27/27
Binary size deltas:
src/proxysql 159 MB -> 152 MB (-7 MB)
plugins/genai/...Plugin.so 47 MB -> 55 MB (+8 MB)
Net process RSS roughly neutral, as the design predicted.
Carve-out scoreboard:
Step 0-2 : infrastructure
Step 3 : ~1.1 K LOC (Anomaly_Detector)
Step 4.B : ~0.5 K LOC (backend_client)
Step 4.C : ~13 K LOC (MCP subsystem + tool handlers + MySQL_FTS)
Step 4.F : runtime-reconfig admin SQL routed through plugin commands
Step 4.G : MCP admin / config / stats tables registered via plugin ABI
Step 5 : ~7 K LOC (GenAI/LLM/AI/RAG)
Step 6 : ~5 K LOC (AI_Vector_Storage + Discovery_Schema +
MySQL_Catalog + Static_Harvester pair)
Pending: Step 7 — delete the PROXYSQLGENAI macro from build flags
and any remaining `#ifdef PROXYSQLGENAI` blocks.
|
2 months ago |
|
|
28cf4ceab8 |
feat(plugins/genai): Step 5 — move GenAI/LLM/AI surface into the plugin
Largest carve-out commit since 4.C: moves the GenAI / LLM / AI / RAG
surface (about 7K LOC) out of core into plugins/genai/. After this
commit, libproxysql.a no longer publishes any of GenAI_Threads_Handler,
LLM_Bridge, LLM_Clients, AI_Features_Manager, AI_Tool_Handler,
RAG_Tool_Handler, or the legacy GloGATH/GloAI globals.
Files moved (git mv preserves history)
Headers (include/ -> plugins/genai/include/)
GenAI_Thread.h
LLM_Bridge.h
AI_Features_Manager.h
AI_Tool_Handler.h
RAG_Tool_Handler.h (promoted from Step 6 — bidirectional
dep with AI_Features_Manager forced
the early move, same way MySQL_FTS.h
got promoted in 4.C)
Sources (lib/ -> plugins/genai/src/ or …/tool_handlers/)
GenAI_Thread.cpp
LLM_Bridge.cpp
LLM_Clients.cpp (.cpp without a header; class decls
live in LLM_Bridge.h)
AI_Features_Manager.cpp
AI_Tool_Handler.cpp -> tool_handlers/
RAG_Tool_Handler.cpp -> tool_handlers/
Plugin lifecycle takeover
plugins/genai/src/plugin_main.cpp:
- Defines plugin-local GloGATH and GloAI alongside the existing
GloMCPH (all three are .so-internal aliases for the contexts
genai_init constructs).
- genai_init now also constructs GenAI_Threads_Handler (calls
->init()) and AI_Features_Manager (calls ->init()), mirroring
the pre-Step-5 ProxySQL_Main_init_GenAI_module sequence.
- genai_stop tears them down in reverse order.
plugins/genai/src/ProxySQL_MCP_Server.cpp:
- AI / RAG endpoint registration re-enabled (was #if 0'd in 4.C
because the constructors weren't reachable from the plugin
then). /mcp/ai and /mcp/rag are once again live when the
operator has GloAI configured.
plugins/genai/src/Anomaly_Detector.cpp + plugin_main.cpp:
- get_query_embedding() now calls through a function-pointer
hook (genai_anomaly_embed_fn) that plugin_main installs in
genai_init and clears in genai_stop. This indirection lets
the test fixture (which compiles Anomaly_Detector.cpp directly
into a test binary, without GenAI_Thread.cpp) link cleanly —
the pointer stays null, the detector short-circuits to empty
embedding, and the existing nullptr-guard in
check_embedding_similarity does the rest.
Core neutralization
src/main.cpp:
- Drops the GloGATH / GloAI globals, ProxySQL_Main_init_GenAI_module
definition + call, the shutdown branch, and the
`if (GloGATH) init_genai_variables()` call.
lib/Admin_Bootstrap.cpp / Admin_FlushVariables.cpp / Admin_Handler.cpp /
ProxySQL_Admin.cpp / MySQL_Session.cpp / ProxySQL_Admin_Stats.cpp:
- Drop the corresponding `#include` and `extern` declarations.
- flush_genai_variables___* stubbed (mirrors the 4.C MCP pattern;
original bodies preserved in `#if 0` for the upcoming
LOAD/SAVE GENAI verbs port).
lib/Admin_Handler.cpp:
- Loose-prefix accept for `genai-*` in is_valid_global_variable
(mirrors the 4.F mcp-* fix), so SET genai-X=Y still reaches
main.global_variables; runtime push happens via the plugin's
LOAD GENAI VARIABLES TO RUNTIME (registered in a follow-up).
include/cpp.h, include/proxysql.h:
- Removed the AI_Features_Manager.h / LLM_Bridge.h / GenAI_Thread.h
/ AI_Tool_Handler.h / RAG_Tool_Handler.h includes that no
longer resolve from core's -I path.
test/tap/test_helpers/test_globals.cpp:
- Drops the matching `#include`s and stub globals.
Test fixture
test/tap/tests/unit/Makefile:
- PROXYSQLGENAI auto-detect probe switched from `GenAI_Thread`
(now plugin-only) to `AI_Vector_Storage` (still in core for
Step 6). After Step 6 the probe will switch again, and
Step 7 deletes the auto-detect entirely.
All three plugin unit tests pass:
- genai_plugin_load_unit-t : 25/25
- genai_plugin_anomaly_unit-t : 6/6
- genai_plugin_backend_client : 27/27
Carve-out scoreboard:
Step 0-2 : infrastructure
Step 3 : ~1.1 K LOC (Anomaly_Detector)
Step 4.B : ~0.5 K LOC (backend_client)
Step 4.C : ~13 K LOC (MCP subsystem)
Step 4.F : runtime-reconfig admin SQL routed through plugin commands
Step 4.G : MCP admin / config / stats tables registered via plugin ABI
Step 5 : ~7 K LOC (GenAI/LLM/AI/RAG)
Pending: Step 6 (AI_Vector_Storage, MySQL_Catalog, Discovery_Schema,
Static_Harvester, PgSQL_Static_Harvester) and Step 7 (delete the
PROXYSQLGENAI macro entirely).
|
2 months ago |
|
|
9078fd3150 |
feat(plugins/genai): Step 4.G — register MCP admin tables via plugin ABI
Moves MCP admin / config / stats SQLite table registration from
lib/Admin_Bootstrap.cpp into the plugin via the chassis
register_table service. Pure refactor: the same DDL strings produce
the same SQLite tables in the same SQLite DBs; only the registration
path changes.
New plugins/genai/src/plugin_tables.cpp registers 14 tables:
admin DB (6)
mcp_query_rules, runtime_mcp_query_rules
mcp_auth_profiles, runtime_mcp_auth_profiles
mcp_target_profiles, runtime_mcp_target_profiles
config DB (3)
mcp_query_rules, mcp_auth_profiles, mcp_target_profiles
(no runtime_* in config; matches the pre-4.G layout)
stats DB (5)
stats_mcp_query_tools_counters, _reset
stats_mcp_query_digest, _reset
stats_mcp_query_rules
The DDL strings still live in include/ProxySQL_Admin_Tables_Definitions.h
for now — plugin_tables.cpp #includes the header. Migrating the
macros into a plugin-private header is a follow-up gated on no
remaining core caller of the MCP table macros (Step 7 territory).
Core changes (lib/Admin_Bootstrap.cpp):
- Deleted the 9 admin/config insert_into_tables_defs() entries
inside `#ifdef PROXYSQLGENAI` for mcp_*.
- Deleted the 5 stats insert_into_tables_defs() entries similarly.
- Replaced both blocks with a one-line comment pointing at the new
plugin owner.
Plugin lifecycle:
- genai_init() now calls genai_register_admin_tables() right after
genai_register_admin_commands(). Both run during chassis Phase C
(init), before admin module bootstrap so the chassis dispatcher
merges plugin tables into the admin DB schema before any DML.
Test extension (test/tap/tests/unit/genai_plugin_load_unit-t.cpp):
- Updated the "skeleton registers no tables (yet)" assertions to
expect 6 admin / 3 config / 5 stats — matching plugin_tables.cpp.
- 25/25 tests passing.
What this changes for operators (no-op):
- Same SQL: SHOW TABLES still shows mcp_*, runtime_mcp_*, stats_mcp_*.
- Same DDL: column lists, primary keys, defaults — all preserved.
- Same admindb / configdb / statsdb attachment.
|
2 months ago |
|
|
3a78a9180f |
fix(admin): restore SET mcp-X=Y admin SQL via loose prefix check
Step 4.C stubbed the `mcp-*` branch of `is_valid_global_variable` in lib/Admin_Handler.cpp because GloMCPH (whose has_variable() the original check called) was deleted. That broke `SET mcp-port=9090` and similar admin SQL with "unknown variable" errors. The chassis ABI doesn't yet have a way for plugins to register a variable-name namespace, so the cleanest restoration is a loose prefix check: accept any `mcp-<name>` token as a valid global variable. The SET / UPDATE statement reaches `main.global_variables` as before; the plugin's `LOAD MCP VARIABLES TO RUNTIME` is what actually validates and pushes the name into MCP_Threads_Handler. Trade-off documented in the comment: a typo (`SET mcp-prot=9090`) silently writes the row and goes ignored at runtime — there's no "unknown variable" error at SET time anymore. This is the same trade-off plugin chassis make for any opt-in variable-namespace plugin; a future chassis enhancement (register_variable_namespace ABI) can tighten it. This closes the SET regression that 4.C introduced. Combined with the LOAD/SAVE verbs the prior 4.F commits registered, the operator- visible MCP admin SQL surface is now functionally restored: SET mcp-port=9090; -- writes main.global_variables LOAD MCP VARIABLES TO RUNTIME; -- pushes to plugin runtime UPDATE main.mcp_target_profiles SET ...; -- writes main.* LOAD MCP PROFILES TO RUNTIME; -- rebuilds auth map UPDATE main.mcp_query_rules SET ...; LOAD MCP QUERY RULES TO RUNTIME; -- pushes to Discovery_Schema |
2 months ago |
|
|
5fe286e972 |
feat(plugins/genai): Step 4.C — move MCP subsystem into the plugin
Per docs/superpowers/plans/2026-04-19-step4-mcp-subsystem-move.md
"4.C / 4.D / 4.E merge" revision. This is the largest single carve-out
commit so far: ~13 K LOC physically relocated, plus surgical edits
across the core admin SQL surface to neutralize references to the
moved code.
What moved (git mv preserves history)
Tool handlers (lib/<name>.cpp -> plugins/genai/src/tool_handlers/<name>.cpp;
include/<name>.h -> plugins/genai/include/<name>.h)
Admin_Tool_Handler, Cache_Tool_Handler, Config_Tool_Handler,
MySQL_Tool_Handler, Observe_Tool_Handler, Query_Tool_Handler,
Stats_Tool_Handler.
MCP listener stack (lib/<name>.cpp -> plugins/genai/src/<name>.cpp;
include/<name>.h(pp) -> plugins/genai/include/...)
MCP_Endpoint, MCP_Thread, ProxySQL_MCP_Server.
Promoted from Step 6 (bidirectional dep with MySQL_Tool_Handler):
MySQL_FTS.
MCP_Tool_Handler base stays in core because AI_Tool_Handler and
RAG_Tool_Handler (Steps 5/6 scope) inherit from it; moving the base
would force pulling those in too.
Plugin lifecycle takeover
plugins/genai/include/genai_plugin.h: GenAIPluginContext now holds
the MCP_Threads_Handler*.
plugins/genai/src/plugin_main.cpp: genai_init constructs the
MCP_Threads_Handler and assigns to a plugin-local GloMCPH symbol
(the legacy global name, kept for the moved tool handlers that
still reference it; the symbol no longer exists in core).
genai_stop tears down on shutdown. genai_start is a no-op for now;
the MCP listener auto-start is gated on the admin-SQL surface that
4.F restores.
plugins/genai/src/ProxySQL_MCP_Server.cpp: AI_Tool_Handler and
RAG_Tool_Handler endpoint registration is disabled via stub
assignments (the headers stay included so member-pointer types
resolve). Steps 5/6 re-enable when those classes move into the
plugin.
Core neutralization (FIXME stubs awaiting 4.F/4.G)
src/main.cpp: GloMCPH global removed; ProxySQL_Main_init_MCP_module
removed; shutdown block deleted.
lib/ProxySQL_Admin.cpp: init_mcp_variables, load_mcp_server,
load_mcp_query_rules_to_runtime, save_mcp_query_rules_from_runtime
stubbed to no-ops; original bodies preserved inside `#if 0` for
4.F reference.
lib/Admin_FlushVariables.cpp: flush_mcp_variables___database_to_runtime
and flush_mcp_variables___runtime_to_database stubbed similarly.
lib/ProxySQL_Admin_Stats.cpp: stats___mcp_query_tools_counters,
stats___mcp_query_digest, stats___mcp_query_rules stubbed.
lib/Admin_Handler.cpp: has_variable("mcp-…") and the
load_target_auth_map call site in LOAD MCP PROFILES … stubbed.
Result: the MCP listener does NOT start automatically between this
commit and 4.F (mcp-* admin variables are not pushed into the
running plugin). Documented in the plan's "4.C/4.D/4.E merge"
section.
src link change: --whole-archive on libproxysql.a
src/Makefile: the proxysql binary now links libproxysql.a with
-Wl,--whole-archive so all symbols are exported via
-Wl,--export-dynamic, allowing the genai plugin .so to dlopen
cleanly even when core code paths don't reference moved-but-still-
in-core classes (LLM_Bridge, Discovery_Schema, MySQL_Catalog,
Static_Harvester, …) directly. test/tap/tests/unit/Makefile gets
the same change so unit tests can dlopen the plugin. Without this,
ld dead-code-eliminates the unreferenced symbols and dlopen fails
with "undefined symbol".
Tests passing
- genai_plugin_anomaly_unit-t : 6/6
- genai_plugin_load_unit-t : 8/8 (lifecycle init/start/stop +
admin table registration)
- genai_plugin_backend_client_unit-t : 27/27
Carve-out scoreboard
Step 0-2 : 0 (infrastructure only)
Step 3 : ~1.1 K (Anomaly_Detector)
Step 4.B : ~0.5 K (backend_client helper, no callers yet)
Step 4.C : ~13 K (this commit; MCP subsystem)
Pending : Steps 4.F, 4.G, 5, 6, 7
|
2 months ago |
|
|
2b781ffbfd |
feat(session): Step 4.A — remove the GENAI:/LLM: query-prefix escape hatches
Per decision Q2 in the GenAI plugin carve-out design doc, the in-line
MySQL-protocol prefixes that bypassed routing, ACLs, and the query
processor are removed. Users reach GenAI features through MCP, admin
SQL, or the REST endpoint -- as the rest of the carve-out finishes,
nothing else needs the in-core GENAI:/LLM: handlers.
Removals:
lib/MySQL_Session.cpp (-933 lines)
- handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY___genai
- handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY___llm
- handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___genai_send_async
- handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___handle_genai_response
- genai_cleanup_request
- check_genai_events
- The COM_QUERY prefix-detection block that dispatched to those
handlers
- The check_genai_events poll-loop hook in WAITING_CLIENT_DATA
include/MySQL_Session.h (-67 lines)
- All matching method declarations, with their doxygen
include/Base_Session.h (-15 lines)
- GenAI_PendingRequest struct
- pending_genai_requests_ map
- next_genai_request_id_ counter
- genai_epoll_fd_ per-session epoll fd
lib/Base_Session.cpp (-9 lines)
- The init block that created the per-session genai_epoll_fd_
LLM: was deleted alongside GENAI: even though only GENAI: is named in
the design. They share the same `#ifdef PROXYSQLGENAI` gate, the same
async-genai socketpair plumbing, and the same rationale. Leaving LLM:
behind would have left a half-block referencing functions that no
longer exist; if we want LLM: back later the plugin can re-implement
it (or a more general request-multiplexer ABI extension can land).
The async-genai socketpair protocol (GenAI_RequestHeader /
GenAI_ResponseHeader, defined in include/GenAI_Thread.h) is now
unreferenced from outside lib/GenAI_Thread.cpp itself; those structs
disappear when GenAI_Thread moves to plugins/genai/ in Step 5.
Verified:
- clean rebuild of libproxysql.a + proxysql + ProxySQL_GenAI_Plugin.so
- all 60 unit-test binaries pass
- smoke test: daemon starts, SELECT through admin works, SIGTERM
shuts down cleanly
Net diff: -1010 / +28 lines. Largest deletion of the carve-out so far.
|
2 months ago |
|
|
f1c0ea2dbc |
feat(genai): Step 3 — carve Anomaly_Detector out of core into plugins/genai/
Per docs/superpowers/specs/2026-04-16-genai-plugin-carveout-design.md
"Migration sequence — Step 3", this is the first step that actually
moves GenAI surface out of libproxysql.a. The Anomaly_Detector now
lives entirely inside the genai plugin and is exercised through the
plugin query-hook ABI added in Step 2; core has zero references to it.
Files moved (git mv preserves history):
include/Anomaly_Detector.h -> plugins/genai/include/Anomaly_Detector.h
lib/Anomaly_Detector.cpp -> plugins/genai/src/Anomaly_Detector.cpp
Plugin-side adjustments:
- Drop the `#ifdef PROXYSQLGENAI` guard from both files (the file only
compiles inside the plugin now; the macro remains a core build flag
but the carve-out gates these files by *location*, not by macro).
- Update the json-include relative path to the new depth.
- Replace `Anomaly_Detector::get_query_embedding` with a stub that
returns an empty vector. The previous implementation called
`GloGATH->embed_documents`, but `GloGATH` (the GenAI_Threads_Handler)
is still a core symbol at this point in the carve-out. The
embedding-similarity stage was already nullptr-guarded on
`vector_db`, so an empty embedding short-circuits cleanly to
"no anomaly". Step 5 reattaches the embedding back-end when
GenAI_Thread moves into the plugin.
Plugin lifecycle wiring (plugins/genai/src/plugin_main.cpp):
- `genai_init` now: registers two Prometheus counters
(`proxysql_genai_detected_anomalies_total`,
`proxysql_genai_blocked_queries_total`) against the shared registry
exposed by Step 2.3, registers the query hook for both MySQL and
PgSQL protocols, and constructs the Anomaly_Detector.
- `genai_start`: flips the started flag (the detector has no separate
start phase).
- `genai_stop`: closes and deletes the detector; counters stay
registered (prometheus-cpp has no Unregister).
Plugin query-hook adapter (plugins/genai/src/plugin_hooks.cpp, NEW):
- Bridges ProxySQL_PluginQueryHookPayload to Anomaly_Detector::analyze.
- Increments the appropriate Prometheus counter on detection / block.
- Translates AnomalyResult into ALLOW or DENY(message) per the ABI.
Core removals (the symmetric carve-out):
- lib/MySQL_Session.cpp: delete the in-core `#ifdef PROXYSQLGENAI`
anomaly-detection block in COM_QUERY (lines around 5370-5377 pre-edit)
and the `handler___status_WAITING_CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_detect_ai_anomaly`
method. Same logic now runs inside the plugin via the query hook
added in Step 2.2.
- include/MySQL_Session.h: drop the matching method declaration.
- lib/AI_Features_Manager.{cpp,h}: remove the anomaly_detector field,
init_anomaly_detector(), close_anomaly_detector(),
get_anomaly_detector(), and the corresponding init() / shutdown()
call sites. AI_Features_Manager now owns only the LLM bridge and
vector_db (LLM_Bridge moves in Step 5; vector_db in Step 6).
- lib/AI_Tool_Handler.{cpp,h}: drop the anomaly_detector field and
the constructor parameter that fed it -- the field was stored but
never read, dead code that was awkward to keep alive across the
carve-out.
- lib/ProxySQL_MCP_Server.cpp: update the AI_Tool_Handler ctor call
site accordingly (one less argument).
- include/MySQL_Thread.h: remove `st_var_ai_detected_anomalies` /
`st_var_ai_blocked_queries` from `enum status_variable_id` and
`ai_detected_anomalies` / `ai_blocked_queries` from
`enum p_th_counter`. The plugin's Prometheus counters replace
these (the old ones were per-thread integer increments surfaced
by the legacy stats path; the plugin uses Prometheus directly).
- lib/MySQL_Thread.cpp: drop the matching entries from
MySQL_Thread_status_variables_counter_array and from
th_metrics_map (the prometheus registration table).
- include/proxysql.h, include/cpp.h: drop the now-stale
`#include "Anomaly_Detector.h"` lines.
- lib/Makefile: drop Anomaly_Detector.oo from the libproxysql object
list.
Tests:
- Delete the in-core TAP tests that linked against the removed symbols:
anomaly_detection-t, anomaly_detection_integration-t,
anomaly_detector_unit-t (Docker-dependent, won't link), plus the
unit test genai_anomaly_unit-t (linked against libproxysql's copy).
- Add test/tap/tests/unit/genai_plugin_anomaly_unit-t.cpp: replaces
the deleted unit test by compiling the plugin's
Anomaly_Detector.cpp directly into the test binary. The
friend-class helper pattern (Anomaly_Detector_TestHelper) is
preserved so private methods (normalize_query,
check_sql_injection) remain testable. Six assertions cover the
same surface plus a regression test that analyze() now runs
cleanly without a vector_db.
Plugin Makefile changes:
- Add Anomaly_Detector.cpp + plugin_hooks.cpp to SRCS.
- Pull in the include paths Anomaly_Detector.cpp's transitively-
included headers need (mariadb client for mysql.h, libev, libscram,
libusual, postgresql, jemalloc, microhttpd, libhttpserver, curl,
re2, pcre, zstd, libconfig, libdaemon, ssl). These mirror the
set already used by test/tap/tests/unit -- the test harness pulls
the same headers in transitively from libproxysql.a's headers.
Doxygen documentation added for every new function and for the new
header structures (GenAIPluginContext fields, genai_query_hook,
register_prometheus_counters, all three lifecycle callbacks, plus
file-level @brief blocks for plugin_main.cpp, plugin_hooks.cpp,
genai_plugin.h, and the moved Anomaly_Detector.{h,cpp}).
Verified end-to-end:
- Full clean rebuild of libproxysql.a + proxysql + ProxySQL_GenAI_Plugin.so.
- All 60 unit-test binaries pass (genai_anomaly_unit-t replaced by
genai_plugin_anomaly_unit-t; net same).
- Smoke test: launching proxysql with
plugins=("...ProxySQL_GenAI_Plugin.so") shows
"Anomaly: Initializing Anomaly Detector v0.1.0" /
"Anomaly Detector initialized with 11 injection patterns" in the
log on startup, and "Anomaly Detector closed" on SIGTERM -- the
plugin's lifecycle owns the detector cleanly.
Carve-out scoreboard so far (lines of GenAI surface moved out of core):
Step 0-2 : 0 (infrastructure prep only)
Step 3 : ~1100 (Anomaly_Detector.{h,cpp}) + counter/handler removals
|
2 months ago |
|
|
1c84a376fe |
feat(session): Step 2.2 — call plugin query hook on the COM_QUERY hot path
Wires the Step 2.1 plugin query-hook ABI into the actual session hot
paths. Both MySQL_Session and PgSQL_Session now consult any plugin-
registered query hook just before the query dispatches to a backend,
and short-circuit with an error to the client if the hook returns
DENY.
Hot-path discipline:
- Gate is proxysql_has_configured_plugin_query_hook(proto), a lock-free
atomic load + one pointer-sized read. When no plugin has registered
a hook for the protocol (the overwhelmingly common case), the only
cost is that single load + branch.
- The dispatch helper takes the plugin-manager mutex; that path is only
entered after the gate says "maybe". The gate is documented as
spurious-true-tolerant: a concurrent unload can null the manager
between the gate and the dispatch, in which case the dispatch helper
re-checks under the lock and returns false.
- Payload assembly uses the pointers already on hand in the session
(client_myds->{addr.addr, myconn->userinfo->{username,schemaname}}).
No allocation, no copy.
Placement:
- MySQL_Session.cpp: same block as the existing PROXYSQLGENAI in-core
anomaly detector check. The two coexist during the carve-out
transition; the in-core detector goes away in Step 3 once
Anomaly_Detector lives inside the plugin and consumes the hook.
- PgSQL_Session.cpp: parallel block right after the existing SQLi
detector check, the equivalent location for pgsql.
Error response on DENY:
- MySQL: ER 1313 / SQLSTATE HY000 with the plugin's message
(or "Query blocked by plugin" if the plugin returned an empty
message), via generate_pkt_ERR + RequestEnd, matching the existing
in-core anomaly-detector blocking style.
- PgSQL: ERRCODE_INSUFFICIENT_PRIVILEGE via generate_error_packet +
RequestEnd.
End-to-end smoke verified: with the genai skeleton plugin loaded (no
hook registered yet), an admin SELECT 1+1 returns the right answer --
the no-hook fast path is invisible. All 59 unit-test binaries pass.
Note: hot-path tests with a real registered hook live in
plugin_query_hook_unit-t (Step 2.1). An end-to-end TAP test that
exercises the call site through a real backend is deferred to the
group-tests work; not in scope for Step 2.
|
2 months ago |
|
|
6d8dff2939 |
docs(chassis): mark query-hook ABI scaffold-state explicit + add TODO markers
The chassis ABI 2 query-hook surface (ProxySQL_PluginQueryHookPayload /
Result / Action plus register_query_hook / dispatch_configured_plugin_
query_hook) is wired through ProxySQL_PluginManager and exercised end-
to-end by unit tests. But the production data plane never calls
proxysql_dispatch_configured_plugin_query_hook — neither MySQL_Session
nor PgSQL_Session has the integration point.
Net effect today: a plugin can register a query hook successfully and
unit tests can drive dispatch through it, but a real client query
arriving over MySQL or PgSQL will never consult the hook. The DENY
contract from the public ABI ("DENY prevents a query from dispatching")
is currently a promise the production path doesn't keep.
# Why scaffold-state instead of immediate full integration
Wiring the dispatch into MySQL_Session::handler___status_WAITING_
CLIENT_DATA___STATE_SLEEP___MYSQL_COM_QUERY_qpo (and the COM_STMT_
PREPARE / COM_STMT_EXECUTE entries that share the qpo machinery) is
mechanical but touches a 95k-LOC hot-path file. A bug in the
integration would manifest as a query-blocking regression that's hard
to debug and easy to ship.
Same for PgSQL_Session. The integration deserves its own focused PR
with TAP coverage that exercises both ALLOW and DENY paths against a
fake plugin — not a side-quest in this PR which is already large.
# What this commit does
1. Updates the ABI doc block in include/ProxySQL_Plugin.h to call out
the scaffold state explicitly. A plugin author reading the header
today would reasonably assume the hook fires in production; that
assumption is wrong, and the doc block now says so. References the
precise grep target ("TODO(plugin-query-hook)") that points at the
missing injection sites.
2. Adds two TODO(plugin-query-hook) markers to MySQL_Session.cpp:
- line ~3387 (COM_STMT_PREPARE → COM_QUERY shared codepath)
- line ~5403 (the main COM_QUERY execution path)
Each immediately follows the GloMyQPro->process_query call, where
CurrentQuery is populated and the dispatch payload would be ready.
3. Adds one TODO(plugin-query-hook) marker to PgSQL_Session.cpp at
line ~2387, following the GloPgQPro->process_query call.
# What this commit deliberately does NOT do
The actual dispatch wiring. The next person picking this up has:
- A precise injection site (3 in MySQL, 1+ in PgSQL).
- Pre-built ABI surface (proxysql_has_configured_plugin_query_hook
for the lock-free fast-path probe; proxysql_dispatch_configured_
plugin_query_hook for the actual hook invocation under shared lock).
- Existing test_helpers/fake_plugin.cpp with a HOOK_DENY env toggle
that exercises the dispatch path under unit test today — extensible
to TAP integration tests once the production wiring lands.
Caught by an external review pass, finding #5. Filing as a deferred
fix with explicit scaffold acknowledgement rather than rushing the
integration.
|
2 months ago |
|
|
90e888e1d0 |
fix(chassis): runtime-view dispatch fires unconditionally on admin
Doc-accuracy review found that proxysql_refresh_configured_plugin_
runtime_views was unreachable for plugin-registered tables. The call
was placed inside
if (refresh==true) { // ProxySQL_Admin.cpp:1637
...
if (admin) {
// existing core runtime_* refreshes
proxysql_refresh_configured_plugin_runtime_views(...)
}
}
`refresh` only gets set to true by hardcoded substring matches
against core's own table names (lines 1358-1634: runtime_mysql_users,
runtime_mysql_servers, runtime_mysql_query_rules, etc.). None of
those substrings match runtime_mysqlx_* (or any other plugin-
registered view), so on a bare
SELECT * FROM runtime_mysqlx_users
the gate was false, my dispatch was skipped, the table was empty
(or stale from a previous query that DID match a core substring),
and the SELECT returned wrong data. The whole "on-demand projection"
mechanism the previous commits documented was broken for the entry
case. Issue #5687 / PR #5688.
The fix is one-line structurally: hoist the dispatch out of the
`if (refresh==true)` block and place it right after the substring-
detection section, gated only on `if (admin)`. The chassis
dispatcher itself (refresh_runtime_views_for_query in
ProxySQL_PluginManager.cpp) already does its own per-view substring
match against query_no_space, so a query that touches no registered
view is a cheap no-op (one shared lock + N substring scans, N ==
registered-view count). Calling unconditionally on every admin
query is therefore both correct and cheap.
Test: new plugin_runtime_views_unit-t (20 ok asserts) drives
ProxySQL_PluginManager::register_runtime_view +
refresh_runtime_views_for_query directly. Covers:
- register_runtime_view rejects null callback / empty name /
case-insensitive duplicate.
- Per-query dispatch fan-out: only matching callbacks fire,
join queries fire all referenced views, unrelated queries
fire nothing, case-insensitive match works, backtick-quoted
identifiers match.
- Whole-identifier boundary: longer-suffix overlap (runtime_
mysqlx_users_extra), left-prefix overlap (stats_runtime_
mysqlx_users), embedded-in-string-literal — none falsely
match. Boundary cases (start of string, end of string) do
match.
This is the test the PR-#5688 review pass identified as the chassis-
hook coverage gap. Builds standalone (no fake-plugin loader needed)
since it drives the manager directly.
|
2 months ago |
|
|
f42c3ee1ab |
feat(chassis): add register_runtime_view ABI for module-owned state
Plugins that declare admin-side runtime views of their own in-memory state need a way to register a projection callback the chassis can invoke when admin SELECTs against the registered table. Without this hook, the only way for a plugin to surface its runtime state to admin operators was to *persist* a duplicate copy of the data into admin_db, which violates the separation of duties between Admin (owns configuration tables and views) and the module (owns runtime state). Refs #5687. ABI surface (in include/ProxySQL_Plugin.h): - struct ProxySQL_PluginRuntimeView { table_name, refresh, opaque } - new services.register_runtime_view callback - bumps PROXYSQL_PLUGIN_ABI_VERSION to 3 The new field is appended at the end of ProxySQL_PluginServices, so ABI-2 plugins keep working — they neither set nor read past the previous layout. ABI 3 plugins set abi_version=3 and use the new field. Loader accepts ABI 1, 2, and 3. Plumbing (in lib/ProxySQL_PluginManager.cpp): - register_runtime_view_service() free fn wires through to the manager's runtime_views_ vector - ProxySQL_PluginManager::register_runtime_view() rejects empty table names, null callbacks, and duplicate registrations - sql_references_table_ci() does whole-identifier substring match, so a SELECT on `runtime_mysqlx_users` doesn't fire the refresh for `runtime_mysqlx_users_extra` if both ever coexist - refresh_runtime_views_for_query() iterates registered views and invokes the matching refresh callbacks - proxysql_refresh_configured_plugin_runtime_views() is the Admin-callable wrapper that takes the manager shared lock Admin-handler integration (in lib/ProxySQL_Admin.cpp): - the existing pre-SELECT refresh block (where runtime_mysql_users triggers save_mysql_users_runtime_to_database(true), etc.) now also calls proxysql_refresh_configured_plugin_runtime_views(). This is the SAME refresh point the canonical core tables use, so plugin views are guaranteed to refresh before the admin query actually executes against admindb. Doc block at the bottom of ProxySQL_Plugin.h rewritten to spell out the separation-of-duties contract explicitly, replacing the previous "copy_table guidance" that incorrectly endorsed plugins persisting their runtime state to admin_db tables. This commit is the foundation only — no plugin uses the new API yet. The follow-up commits convert the mysqlx plugin's four entity pairs (users / routes / endpoints / variables) to the canonical pattern. |
2 months ago |
|
|
d4427731b7 |
Merge remote-tracking branch 'origin/v3.0' into plugin-chassis
Picks up the v3.0 fixes that unblock plugin-chassis CI: - |
2 months ago |
|
|
83725ea4e5 |
feat: add --no-plugins kill switch; fix 4 pre-existing test failures
## (1) --no-plugins kill switch (issue #5680) When the plugin chassis is enabled (PROXYSQL40 builds), an operator upgrading to v4.0.0 gets the chassis active by default. If a critical chassis bug is found post-release, the recovery options today are: - Roll back the entire ProxySQL package to v3.x. - Edit the config file to comment out plugins=() and restart. Add a runtime kill switch — a CLI flag (and equivalent env var) that bypasses plugin loading entirely, regardless of config. Cuts the time-to-recover dramatically and reduces deployment anxiety. CLI: --no-plugins (gated by PROXYSQL40) Env: PROXYSQL_NO_PLUGINS=1 (CLI takes priority) Field: GloVars.no_plugins Wired through: when set, LoadConfiguredPlugins / InitConfiguredPlugins / StartConfiguredPlugins / StopConfiguredPlugins all become no-ops. LoadConfiguredPlugins emits a single startup log line so the operator knows the bypass took effect: Plugin chassis disabled by --no-plugins / PROXYSQL_NO_PLUGINS=1; skipping load of N configured plugin(s) Verified: --no-plugins appears in proxysql --help. The bypass message string is in the binary. Full end-to-end smoke against a Docker MySQL deferred to issue #5677. ## (2) plugin_manager_unit-t #62 + #88 (issue #5679) Test bug. The two assertions encoded the pre-ab9d5a103 contract that "destructor skips stop on plugins that were never started". Commit |
2 months ago |
|
|
55e90d1a76 |
fix(plugin-chassis,mysqlx): chassis read-path scaling, graceful shutdown, hardening
Six independent items from the independent review of PR #5651, batched together because each one alone is small. 1) lib/ProxySQL_PluginManager.cpp: replace g_active_plugin_manager_mutex with a std::shared_mutex. Readers (dispatch_admin_command, dispatch_query_hook, resolve_alias_to_canonical) take a shared lock so multiple worker threads can be inside plugin callbacks concurrently; writers (publish/unpublish in load/stop) take the unique lock. The previous std::mutex serialized every plugin-callback dispatch on one mutex. Once a plugin actually wires a query hook into MySQL_Thread / PgSQL_Thread, every concurrent client query on every worker would have queued behind that mutex — silently negating ProxySQL's per-worker parallelism. The lock-free proxysql_has_configured_plugin_query_hook() didn't help, since the actual dispatch still took the lock. Switching to shared_mutex on the read path lets dispatch scale linearly. 2) plugins/mysqlx/include/mysqlx_session.h + plugins/mysqlx/src/mysqlx_session.cpp + plugins/mysqlx/src/mysqlx_thread.cpp: add MysqlxSession::shutdown_notify_client() and call it from Mysqlx_Thread::run() on the way out of the worker loop. Previously Mysqlx_Thread::stop() flipped running_=false and joined. The destructor then deleted sessions, closing their fds. Connected clients saw an unannounced TCP RST mid-response and a torn TLS record. Now the worker, on its way out, walks sessions_ and for each live session: enqueues a Mysqlx::Error frame (code 1053, "Server is shutting down", FATAL severity); flushes one write_to_net pass; if TLS is up, calls SSL_set_quiet_shutdown(1) + SSL_shutdown so the peer's TLS stack sees a proper close_notify rather than a torn record. Best-effort throughout — never blocks on unresponsive peers because the process is exiting. 3) plugins/mysqlx/include/mysqlx_session.h + plugins/mysqlx/src/mysqlx_session.cpp: remove the dead TLS_PASSTHROUGH enum value and the two corresponding branches. handler_tls_accept_init's first three lines and the `tls_mode_ != TLS_PASSTHROUGH` predicate in handler_connecting_server only ran when set_tls_mode(TLS_PASSTHROUGH) had been called, which never happened in production — the `mysqlx_tls_mode` config column is never plumbed into a session. Worse, the PASSTHROUGH branch did not actually implement an opaque pipe (it just skipped TLS termination and resumed clear-text X-Protocol parsing, which would desync any real client). Drop the value rather than carry a misleading enum that suggests a feature exists. Future passthrough work should reintroduce a properly-wired implementation. 4) plugins/mysqlx/src/mysqlx_connection.cpp: check the return value of inet_pton in start_connect; fail fast on anything that isn't a valid IPv4 dotted-quad. Previously the return was discarded. inet_pton on a hostname (or IPv6 literal, or empty string) silently left sin_addr at 0.0.0.0 — producing a connect to 0.0.0.0/INADDR_ANY rather than the intended target. Real footgun because mysqlx_backend_endpoints.hostname accepts arbitrary strings. Now: fail with ERROR_STATE so the misconfig surfaces instead of routing traffic to the wrong target. Hostname resolution is still the upstream pipeline's job; start_connect deliberately stays narrow. 5) plugins/mysqlx/src/mysqlx_data_stream.cpp: move do_ssl_handshake's 64 KiB scratch buffer from the stack to a thread_local static. ASan-instrumented builds and large-thread-pool configs can run with thread stacks tight enough that a stack-allocated 64 KiB local straddles the limit. Each Mysqlx_Thread owns its own thread_local instance so the buffer is not shared between threads. 6) plugins/mysqlx/src/mysqlx_thread.cpp: document the listener-removal semantics on remove_listener_for_route. Document that already-accepted sessions on a listener that's being removed continue running against their existing target_hostgroup_ / target_address_ / target_port_ until they finish or hit idle timeout. That matches surrounding MySQL behaviour (DROP TABLE doesn't cancel in-flight queries; ALTER doesn't kick off open prepared statements). Future change can call shutdown_notify_client on matching sessions if active disconnection becomes desirable. NOT changed: the agent-flagged "compression overshoot" issue at plugins/mysqlx/src/mysqlx_session.cpp:1283. The zstd-stream loop already caps the resize at `cap` on line 1369 (`if (new_sz > cap) new_sz = cap;`) before the resize, so decompressed never grows past the cap; there is no overshoot. Verified by tracing the loop. Skipped. Verified locally: - plugin .so builds clean with PROXYSQL40=1 and the implied tier flags. - libproxysql.a and src/proxysql build clean. - plugin chassis tests (plugin_lifecycle_unit-t, plugin_dispatch_unit-t, plugin_manager_unit-t, plugin_query_hook_unit-t) build and pass with the new shared_mutex read path. plugin_manager_unit-t shows the same 2 pre-existing destructor-related failures it had before this commit (verified by stash + rebuild). - mysqlx_robustness_unit-t passes 74/74. - mysqlx_session_unit-t has the same pre-existing failures at 33-34. |
2 months ago |
|
|
04bccec51e |
chore(plugin-chassis): tighten gating, drop dead paths, gate forgery setters
Five clean-up items from the independent review of PR #5651. None of these change behaviour on a normal run; they each fix a concrete way the current code is misleading or unnecessarily exposed. 1) lib/ProxySQL_Admin.cpp: gate the three plugin-DB-handle getters (proxysql_plugin_get_admindb / _configdb / _statsdb) under #ifdef PROXYSQL40. Previously these were defined unconditionally and emitted symbols into v3.0/v3.1 binaries. The chassis is a v4.0 feature; the user explicitly required that v3.x builds carry no plugin-aware code. Wrap in PROXYSQL40 so they are entirely absent from v3.x linkage. ProxySQL_PluginManager.cpp's extern declarations (lines 23-25) are already inside the file-wide PROXYSQL40 gate and so resolve only when the gate is active. 2) lib/ProxySQL_PluginManager.cpp + include/ProxySQL_PluginManager.h: delete the dead `#else /* !PROXYSQL40 */` branches. Both files are wrapped in a top-level `#ifdef PROXYSQL40` covering the entire body. Inner `#ifdef PROXYSQL40 ... #else ... #endif` blocks therefore had unreachable `#else` arms — 30+ lines of "pre-chassis two-phase loader" in the .cpp, plus a redundant declaration of proxysql_load_configured_plugins in the .h. The dead arms read as load-bearing alternative implementations on review and that is exactly the wrong signal. Drop them; the single PROXYSQL40 path is the only one. 3) lib/Admin_Bootstrap.cpp + include/proxysql_admin.h + src/main.cpp: remove ProxySQL_Admin::materialize_plugin_tables(). `Admin::init()` already merges plugin-declared schemas into tables_defs_{admin,config,stats} (~line 944) and runs the DDL via check_and_build_standard_tables() (~line 994), all on the same first-boot/reload code path the core tables use. The follow-up call to GloAdmin->materialize_plugin_tables() in main.cpp ran a second name-dedup pass that found everything already present and produced an empty new-rows set — i.e. the post-init helper was a no-op disguised as load-bearing infrastructure. Delete the helper, the header declaration, and the main.cpp call site, and update the comments in main.cpp + ProxySQL_PluginManager.cpp to point at Admin::init() as the single canonical materialization site. Leave a NOTE in Admin_Bootstrap.cpp at the old call site so anyone re-adding a similar helper sees why the prior one was removed. 4) plugins/mysqlx/include/mysqlx_session.h + plugins/mysqlx/src/mysqlx_session.cpp: gate the two genuine forgery vectors behind MYSQLX_TEST_BUILD. inject_identity_for_test() bypasses the full auth flow — no credential check, no capability negotiation, just sets identity_ to a caller-supplied MysqlxResolvedIdentity. resolve_backend_target_ for_test() drives a private routing helper without an authenticated identity. Both are necessary for unit tests but should not be reachable at all in shipped binaries; an in-process exploit reaching the session can call inject_identity_for_test() to forge an authenticated identity. Wrap them in #ifdef MYSQLX_TEST_BUILD; define -DMYSQLX_TEST_BUILD in the test Makefile only. The remaining target_*_for_test() getters are read-only state observers and are left unconditional — they cannot mutate the session and a debugger could observe the same state regardless. 5) test/tap/tests/unit/Makefile: define -DMYSQLX_TEST_BUILD on every unit test compile line via OPT. This is the test-only knob that re-enables the gated forgery methods so unit tests still compile. plugins/mysqlx/Makefile does NOT define this macro, so the production .so does not compile in the entry points. Verified locally: - plugins/mysqlx/ProxySQL_MySQLX_Plugin.so builds clean with PROXYSQL40=1 PROXYSQL31=1 PROXYSQLFFTO=1 PROXYSQLTSDB=1 PROXYSQLGENAI=1 (no MYSQLX_TEST_BUILD). - test/tap/tests/unit/mysqlx_robustness_unit-t builds and runs: 74/74 assertions pass, including the ones that exercise inject_identity_for_test (visible only because the test Makefile defines MYSQLX_TEST_BUILD). |
2 months ago |
|
|
514025c270 |
Fix cmp() to be const-qualified and remove redundant OpenSSL linking on Darwin
|
2 months ago |
|
|
520bd1be4e |
Fix TrxId_Interval comparison operators to be const for macOS compilation
|
2 months ago |
|
|
ae88c07089 |
Fix GTID range validation
|
2 months ago |
|
|
7ca0d153cc
|
Merge pull request #5654 from sysown/fix/pgsql-active-tx-on-broken-conn
fix(pgsql): surface mid-transaction backend errors instead of silently replaying |
2 months ago |
|
|
3e69e9967b |
fix(pgsql): use snprintf instead of sprintf in SQL3_GlobalStatus for tx_poisoned counters
SonarCloud flagged 3 unbounded sprintf calls in SQL3_GlobalStatus() that write tx_poisoned counters into a fixed 256-byte buffer. Replace with snprintf(buf, sizeof(buf), ...) for bounds safety. Follow-up to PR #5659. |
2 months ago |
|
|
f5e8732afb |
fix(pgsql): review-driven fixes for tx_poisoned feature
Consolidated feedback from four independent reviews (correctness, security, test-coverage, code-quality). Changes, in four logical groups: 1. Extend coverage to clean-ErrorResponse backend-death path (correctness review #2). The most common real-world mid-tx backend-death scenarios — graceful postgres shutdown (57P01), crash shutdown (57P02), connect-now-refused (57P03) — deliver a clean ErrorResponse on an otherwise-reusable connection, so they reach handler_minus1_HandleErrorCodes, not handler_minus1_ClientLibraryError. Pre-fix, poison only fired on the connection-broken path. Now it fires on both. The caller at PgSQL_Session.cpp:~3222 grew a tx_poisoned check that mirrors the one already added at :3214 for the reusable-state=false path. 2. Tighten the poison preflight to true in-tx only (security F1). Previously gated on myconn->IsActiveTransaction(), which returns true via the disconnect-heuristic set by 68c76eb42's compute_unknown_transaction_status — even for a plain autocommit statement that happened to be killed mid-query. Now gates on is_in_transaction() via transaction_state_manager, which tracks explicit BEGIN/COMMIT/ROLLBACK. A non-tx statement killed mid-flight no longer gets a misleading "current transaction is aborted" synthesized error. 3. Harden the simple-query classifier (correctness #3/#4/#5, test review 1.11). - Trim the trailing null terminator from the Simple Query payload before applying the "no trailing content" check, otherwise valid statements like "ROLLBACK\0" / "ROLLBACK WORK\0" fall through to KIND_REJECT. - Skip SQL line comments (-- to EOL) and block comments (/* */ non-nesting) in leading whitespace, so ORM-prepended comments don't block recovery. - After matching ROLLBACK/ABORT/COMMIT/END, require the rest of the statement to be a plain whole-tx end (optional WORK/ TRANSACTION noise word + optional trailing ';'). Reject: * ROLLBACK TO SAVEPOINT (partial rollback — can't honor on a dead backend) * ROLLBACK AND CHAIN (chained tx — same) * ROLLBACK; SELECT 1; (multi-statement — we'd silently drop the second stmt) Rejection is ERROR 25P02 + stay poisoned. 4. Code-quality refactor (simplify review #1, correctness #7/#8, security F2/F3). - Centralize the ErrorResponse(25P02) + ReadyForQuery('E') packet construction via write_tx_poisoned_error(). Four sites collapse to one-liners; message string lives in one place. - Fix NoticeResponse SQLSTATEs: WARNING-class notices now use ERRCODE_WARNING (01000, generic) for the backend- context notice and ERRCODE_NO_ACTIVE_SQL_TRANSACTION (25P01) for the "there is no transaction in progress" notice on the COMMIT-on-poisoned path. Previously both used ERRCODE_SUCCESSFUL_COMPLETION (00000) which is semantically wrong for a WARNING. - Cap backend error text to 4 KB and strip embedded NUL bytes before re-embedding in a NoticeResponse. Prevents memory-amplification if a compromised/rogue backend sends multi-MB error text, and prevents silent truncation at the first NUL (PG_pkt::put_string -> strlen). - Enrich the poison-event proxy_warning with hostgroup, backend addr:port, backend PID, client addr, user, dbname, query digest, and the full backend error code+message. Mirrors handler_minus1_LogErrorDuringQuery so operators can correlate poison events with the triggering query. Related to PR #5659 and issue #5658. |
2 months ago |
|
|
100e417179 |
test(pgsql): extended-query coverage for tx_poisoned behavior
New sibling to pgsql-tx_poisoned_recovery-t, plus a one-line semantics
fix in the extended-query poison gate.
Semantics fix (lib/PgSQL_Session.cpp):
Before, the pgsql_tx_poisoned_rejected_statements_total counter was
incremented on EVERY swallowed ext-query wire packet — one per Parse /
Bind / Describe / Close / Execute / Sync. That made a single logical
PQexecParams call count as 4+, inconsistent with the simple-query
path where a 'Q' packet bumps it exactly once.
Now P/B/D/C/E are silently swallowed (no counter bump), and Sync is
the single accounting point — the counter value matches the number
of client-visible "rejected statements" regardless of protocol.
New test pgsql-tx_poisoned_extended_query-t (plan(11)):
* Poison the session via a simple-query pg_sleep + mid-tx kill.
* While poisoned:
- PQexecParams("SELECT 42", 0 params) returns 25P02.
- PQprepare("stmt_poisoned", "SELECT 43") returns 25P02.
- PQexecPrepared on that name returns 25P02.
* The rejected counter increments by exactly 3 (one per logical
ext-query call), not by the 8-11 wire packets that libpq actually
sends. This assertion IS the mechanical proof that the semantics
fix above took effect.
* Simple-query ROLLBACK recovers the session.
* Post-recovery PQexecParams and PQprepare+PQexecPrepared both
execute cleanly and return rows.
Registered alongside the simple-query sibling in legacy-g2. Verified
against a live legacy-g2 infra: both tests pass (11/11 and 15/15),
counter deltas exactly match expected per-statement semantics.
|
2 months ago |
|
|
fb690e7095 |
feat(pgsql): preserve client session on mid-tx backend death
Previously, when a PgSQL backend died mid-transaction, ProxySQL's
handler_minus1_ClientLibraryError refused to silently retry the failed
statement on a fresh backend (correct, per
|
2 months ago |
|
|
8a6143c63c |
session_track_variables: add observability for misconfiguration and runtime backoff
Context ------- PR #5166 introduced a three-mode 'mysql-session_track_variables' variable (DISABLED / OPTIONAL / ENFORCED) whose behavior silently depends on two other MySQL capability toggles and on the capabilities of each backend: * In OPTIONAL mode the feature is a no-op when 'mysql-enable_server_deprecate_eof=false' or when the backend does not advertise CLIENT_DEPRECATE_EOF / CLIENT_SESSION_TRACKING. The operator sees the variable set to 1, but 'PROXYSQL INTERNAL SESSION' shows no tracked variables and there is no indication why. * In ENFORCED mode ProxySQL forces CLIENT_DEPRECATE_EOF on both the client and server sides of the protocol even if the operator explicitly set 'mysql-enable_client_deprecate_eof=false' / 'mysql-enable_server_deprecate_eof=false'. This is a protocol-visible behavior change for clients that did not negotiate CLIENT_DEPRECATE_EOF, and it was previously silent. * In ENFORCED mode backends that cannot honor tracking (missing CLIENT_SESSION_TRACKING or CLIENT_DEPRECATE_EOF) are placed in a 30-second selection backoff via 'MySrvC::session_track_backoff_until'. The server is NOT shunned and no connect-error counter is incremented, so in a mixed-version hostgroup an incompatible backend sits idle indefinitely with no log trail. Changes ------- * lib/Admin_FlushVariables.cpp: add a cross-variable validator at the tail of 'ProxySQL_Admin::flush_mysql_variables___database_to_runtime()', right next to the existing 'mysql-use_tcp_keepalive' warning. It runs once per 'LOAD MYSQL VARIABLES TO RUNTIME' and only when the feature is actually requested (mode != DISABLED). Emitted warnings: - ENFORCED + !enable_client_deprecate_eof -> "will force CLIENT_DEPRECATE_EOF on frontend connections; older clients may be affected" - ENFORCED + !enable_server_deprecate_eof -> equivalent for backends - OPTIONAL + !enable_server_deprecate_eof -> "has no effect while mysql-enable_server_deprecate_eof=false; backend session tracking will be skipped" The checks use the global 'GloMTH->get_variable_int()' view (same pattern as the TCP keepalive warning above it), and deliberately do not name any backend -- this is a global-configuration warning; per-backend capability mismatches are handled separately below. * lib/MySQL_Session.cpp: in 'handle_session_track_capabilities()', at the point where the code stores a new 'session_track_backoff_until' deadline for an incompatible backend, add a 'proxy_warning' naming the backend's address:port and the reason. Because this path only runs on new connection establishment -- and is itself gated by the 30-second backoff on the selection path -- the warning is naturally rate-limited to at most once per 30 seconds per backend. In a mixed fleet that rate is just enough for an operator to notice the incompatible server without drowning the log. Behaviour --------- * DISABLED mode (the default): no new log output; validator is skipped entirely. The hot selection path was already skipping the atomic read in the previous commit, so DISABLED remains free of new work. * OPTIONAL / ENFORCED: at most three warnings at 'LOAD MYSQL VARIABLES TO RUNTIME' time (cross-variable validator) plus one periodic warning per incompatible backend (ENFORCED path only). No per-query log output. |
2 months ago |
|
|
2a7bddca28 |
MySrvC: rename server_backoff_time to session_track_backoff_until, gate hot-path read
Context ------- PR #5166 introduced a per-'MySrvC' atomic timestamp that prevents the server from being chosen for new connections for a 30-second window. The timestamp is written in exactly one place, 'MySQL_Session::handle_session_track_capabilities()', and only when 'mysql-session_track_variables' is in ENFORCED mode and the backend is missing 'CLIENT_SESSION_TRACKING' or 'CLIENT_DEPRECATE_EOF'. The field was originally named 'server_backoff_time' and its only semantics were that ENFORCED-mode capability mismatches temporarily exclude the server from selection. Two issues with the original shape: 1. The name does not hint that the scope is session-tracking capability enforcement. A reader of 'MyHGC::get_random_MySrvC' or 'MySQL_Thread::get_MyConn_local' had no way to tell that this field is tied to one specific feature and not, say, a generic health backoff. 2. The atomic load was unconditional in both hot selection loops. Even though the atomic is relaxed (effectively a 'mov' on x86), per-iteration atomic reads inhibit hoisting and CSE that the compiler can otherwise apply to thread-local integer reads. 'get_random_MySrvC' runs the loop over every server in the hostgroup on every backend-connection acquisition, so this is the sort of place where small per-iteration costs accumulate. Changes ------- * Rename the field: MySrvC::server_backoff_time -> MySrvC::session_track_backoff_until Naming it after the feature makes it obvious that the field is specific to session-tracking enforcement and should not be overloaded for unrelated purposes (e.g. connect errors, monitor failures). If a future change needs a generic backoff mechanism, a new field -- or a rename at that point -- keeps intent clear rather than piggy-backing on this one silently. * Gate the atomic load on mode: In both 'MyHGC::get_random_MySrvC()' (lib/MyHGC.cpp) and 'MySQL_Thread::get_MyConn_local()' (lib/MySQL_Thread.cpp), the relaxed load is skipped entirely when 'mysql_thread___session_track_variables' is not ENFORCED. In 'get_MyConn_local' the check is computed once before the loop; in 'get_random_MySrvC' the check is inside the loop but is a thread-local int read, which is free. Correctness note: switching out of ENFORCED at runtime makes any stale 'session_track_backoff_until' values in the pool immediately ignored, which is the desired behavior -- servers rejected while ENFORCED was active become eligible again the moment the operator relaxes the mode, matching how ProxySQL handles other runtime-variable-dependent state. No explicit reset is required. * Updated the field documentation in include/MySQL_HostGroups_Manager.h with the monotonic-timestamp unit, the list of sites that read/write it, the explicit scope (ENFORCED mode only), and the reset policy (no explicit reset; deadline ages out by comparison against curtime). No functional change for DISABLED / OPTIONAL configurations (the default for all installations). ENFORCED mode behaviour is unchanged. |
2 months ago |
|
|
f815b77812 |
fix: make server_backoff_time atomic
Signed-off-by: Wazir Ahmed <wazir@proxysql.com> |
2 months ago |
|
|
a76be53785 |
fix: skip session variable handling when tracking is disabled
Signed-off-by: Wazir Ahmed <wazir@proxysql.com> |
2 months ago |
|
|
68c76eb425 |
fix(pgsql): mark transaction state unknown when backend connection is broken
When a backend connection breaks with an error (e.g. postmaster SIGKILL mid-query), libpq's PQtransactionStatus() returns PQTRANS_UNKNOWN rather than PQTRANS_INTRANS, so IsActiveTransaction() reports false for a connection that was in a transaction moments earlier. The retry path in PgSQL_Session::handler_minus1_ClientLibraryError() then replays the failed statement on a fresh backend connection as autocommit, breaking transactional semantics: the client's next COMMIT lands on a connection with no open transaction and PostgreSQL emits "WARNING: there is no transaction in progress". MySQL does not have this issue because mysql->server_status caches SERVER_STATUS_IN_TRANS from the last OK packet and survives the connection breaking. Fix compute_unknown_transaction_status() to force unknown_transaction_status=true when the backend is disconnected, so the retry guard in handler_minus1_ClientLibraryError() refuses to replay statements that were running inside an explicit transaction. |
2 months ago |
|
|
8d94919b22 |
fix: preserve EOF capability in enforced mode
- Keep `CLIENT_DEPRECATE_EOF` enabled during frontend handshake when `session_track_variables=ENFORCED`. - Avoid unsafe mysql_error() calls in session-track TAP tests. Signed-off-by: Wazir Ahmed <wazir@proxysql.com> |
2 months ago |
|
|
c723ede0cf |
Merge remote-tracking branch 'origin/plugin-chassis' into ProtocolX-rebased
# Conflicts: # lib/ProxySQL_PluginManager.cpp # plugins/mysqlx/Makefile # plugins/mysqlx/include/mysqlx_plugin.h # plugins/mysqlx/include/mysqlx_session.h # test/tap/groups/groups.json # test/tap/tests/test_mysqlx_listener_smoke-t.cpp # test/tap/tests/unit/Makefile |
2 months ago |
|
|
b3d4de0bc8 |
Merge remote-tracking branch 'origin/v3.0' into plugin-chassis
# Conflicts: # test/tap/groups/groups.json |
2 months ago |
|
|
e462bb0cb4 |
fix: address PR review feedback
Resolves in-scope review items on PR #5651. Items deemed out of scope for this PR (O(N) admin dispatch lookup, per-plugin mutex, worker backpressure, runtime-reload DDL semantics, E2E tests going through ProxySQL rather than directly to MySQL) are tracked elsewhere; items that were already fixed in earlier commits on this branch (Admin_Handler MYSQLX alias vectors, Admin_Bootstrap PROXYSQL40 gating) are acknowledged as stale. Changes: * .github/workflows/CI-mysqlx.yml: add `include/` and `src/proxysql_global.cpp` to both `sparse-checkout` blocks. The plugin Makefile discovers the repo root via `src/proxysql_global.cpp` and includes headers from `include/`; without these, `make` inside `plugins/mysqlx` climbs up to `/` or fails on missing includes. (coderabbit: "Checkout the files required by the plugin Makefile.") * .github/workflows/CI-mysqlx.yml: `fail-on-cache-miss` now depends on `github.event_name == 'workflow_run'`. Strict behavior is retained for the cache-driven pipeline that CI-trigger populates; manual `workflow_dispatch` runs no longer fail when the SHA-specific cache doesn't exist yet. (coderabbit: "Don't make manual runs depend on a pre-existing build cache.") * plugins/mysqlx/Makefile: replace the unbounded `while [ ! -f ./src/proxysql_global.cpp ]; do cd ..; done` with a bounded upward walk (up to 12 levels) that fails fast with a clear error message when the marker file is missing — e.g. on a sparse checkout that omits `src/proxysql_global.cpp`. Build output no longer appears to hang indefinitely. (coderabbit: "Prevent root discovery from hanging on sparse or invalid checkouts.") * doc/PLUGIN_API.md: update startup sequence from the old three-phase (load → init → start) description to the four-phase chassis lifecycle (load → register_schemas → admin materialize → init → start). Document the `register_schemas` descriptor field and the `abi_version` / `PROXYSQL_PLUGIN_ABI_VERSION` contract. Clarify that `stop()` pairs with `init()` (not `start()`) for teardown symmetry. (coderabbit: "Update plugin lifecycle documentation to reflect four-phase model and ABI versioning.") * plugins/mysqlx/include/mysqlx_session.h: default-initialize every field in `MysqlxCredentials`. `bool x_enabled` was indeterminate for a default-constructed instance, which would let a lookup miss produce a value that silently authenticates. In-class initializers give every field a deterministic default. (coderabbit: "Default- initialize credential fields used by auth decisions.") * plugins/mysqlx/src/mysqlx_backend_session.cpp: close `backend_fd_` when `authenticate_backend()` returns false. Previously `connect()` stored the newly-opened fd in `backend_fd_` and returned the auth result; a retry on the same `MysqlxBackendSession` would then overwrite `backend_fd_` and leak the previous socket. (coderabbit: "Close/reset `backend_fd_` when authentication fails.") * lib/Admin_Bootstrap.cpp: `materialize_plugin_tables()` now runs DDL only for the subset of plugin-declared tables that were freshly inserted into `tables_defs_*` (the rest are already materialized). DDL failures are startup-fatal: on `execute()` returning false, log the plugin kind + table name + SQL and `exit(EXIT_FAILURE)`. A partially materialized schema produces opaque runtime errors from the plugin later on, so aborting startup is the right default. (gemini + coderabbit: "Execute DDL only for newly materialized plugin tables" / "add error checking".) Not changed in this commit (deferred / out of scope / stale): * CI-mysqlx E2E tests going through ProxySQL (requires plumbing listener + admin config in the CI workflow). * O(N) admin dispatch scan → hash map. * Descriptor build-id hash for libstdc++ compat detection. * Per-plugin mutex on mutable plugin context. * Worker client-fd enqueue backpressure. * Stale plan-doc feedback on docs/superpowers/plans/*.md. Verification (clean tree): * `unset PROXYSQL*; make cleanall && make debug -j$(nproc) && make build_tap_test_debug -j$(nproc)` -- 0 errors. * `PROXYSQLGENAI=1 make cleanall && PROXYSQLGENAI=1 make debug -j$(nproc) && PROXYSQLGENAI=1 make build_tap_test_debug -j$(nproc)` -- 0 errors. * Plugin unit tests (PROXYSQL40): 48 + 52 + 26 + 46 + 10 = 182 pass. |
2 months ago |
|
|
3ba92815f8 |
fix(plugin-chassis): make chassis fully invisible in v3.x builds
Review feedback identified that the chassis was leaking into the v3.0
stable build via unfenced references in core code:
* Admin_Bootstrap.cpp had `if (auto* m = proxysql_get_plugin_manager())`
and a full materialize_plugin_tables() implementation outside any
PROXYSQL40 fence
* proxysql_admin.h declared materialize_plugin_tables()
* proxysql_glovars.hpp held GloVars.plugin_modules and
proxysql_load_plugin_modules_from_config()
* ProxySQL_GloVars.cpp had three unfenced .clear()/use sites
* ProxySQL_Admin.cpp::dispatch_plugin_admin_command (template + two
instantiations) was unfenced
* src/main.cpp's GloPluginManager, LoadConfiguredPlugins,
InitConfiguredPlugins, StartConfiguredPlugins, StopConfiguredPlugins,
UnloadPlugins teardown, the four-phase orchestration block, and the
proxysql_load_plugin_modules_from_config call were all unfenced
Separately, Admin_Handler.cpp held 16 hardcoded MYSQLX alias vectors
(LOAD_MYSQLX_USERS_FROM_MEMORY, etc.) and a 16-deep if-ladder that
called resolve_admin_alias_to_canonical(). This embedded plugin-specific
knowledge in core code -- core should know nothing about any specific
plugin's commands.
The agreed architecture: v3.0/v3.1 builds have NO plugin loader at all;
the entire plugin chassis (including its public manager surface, its
loader, and its dispatch helpers) only exists under PROXYSQL40. v3.0
core code therefore holds zero plugin-related code paths.
Changes:
* include/ProxySQL_Plugin.h, include/ProxySQL_PluginManager.h,
lib/ProxySQL_PluginManager.cpp: wrap entire file body in
`#ifdef PROXYSQL40 ... #endif`. Including the header from a v3.x
translation unit yields nothing; the .cpp compiles to an empty object
file. Removed redundant inner PROXYSQL40 fences inside the now-empty
v3.x body of Plugin.h (kept the few that are inside the file-wide
outer for clarity). PROXYSQL_PLUGIN_ABI_VERSION is now unconditionally
2 (the macro is no longer reachable from a v3.x compile, so the
PROXYSQL40-vs-1 split is moot).
* include/proxysql_glovars.hpp, lib/ProxySQL_GloVars.cpp: gate
GloVars.plugin_modules field, all .clear() sites, the
proxysql_load_plugin_modules_from_config declaration + definition.
* include/proxysql_admin.h: gate materialize_plugin_tables and
dispatch_plugin_admin_command<S> declarations.
* lib/Admin_Bootstrap.cpp: gate `#include "ProxySQL_PluginManager.h"`,
the `if (auto* m = proxysql_get_plugin_manager())` merge block, and
the materialize_plugin_tables definition.
* lib/Admin_Handler.cpp: delete the 16 LOAD_/SAVE_MYSQLX_* const
std::vector<std::string> declarations entirely. Delete the helper
resolve_admin_alias_to_canonical(). Delete the 16-deep if-ladder
+ the !PROXYSQL40 fallback dispatch branch. The remaining plugin
dispatch is one generic call to
proxysql_resolve_configured_plugin_admin_alias() inside an
#ifdef PROXYSQL40 block; v3.x compiles it out entirely. Core now
holds no plugin-specific knowledge of any kind.
* lib/ProxySQL_Admin.cpp: gate dispatch_plugin_admin_command template
body + both explicit instantiations.
* src/main.cpp: gate `#include "ProxySQL_PluginManager.h"`,
GloPluginManager declaration, all four wrapper functions
(LoadConfiguredPlugins / InitConfiguredPlugins /
StartConfiguredPlugins / StopConfiguredPlugins), the
StopConfiguredPlugins call inside UnloadPlugins, the four-phase
orchestration in ProxySQL_Main_init_phase2___not_started, the
GloVars.plugin_modules.clear() and the
proxysql_load_plugin_modules_from_config call in
ProxySQL_Main_process_global_variables. v3.x builds reduce that
whole block to a single ProxySQL_Main_init_Admin_module call.
* plugins/mysqlx/src/mysqlx_plugin.cpp,
plugins/mysqlx/src/mysqlx_admin_schema.cpp: drop the !PROXYSQL40
fallback paths. The plugin only loads under PROXYSQL40 now (no
loader exists in v3.x), so the legacy combined-init code path and
the 16 fallback register_command() calls were dead code. The
descriptor unconditionally wires register_schemas; mysqlx_init holds
only context setup.
* Makefile (top-level): the four build_src_* recipes that recurse into
plugins/mysqlx are wrapped in `$(if $(filter 1,$(PROXYSQL40)),...)`.
v3.x builds emit "[skip] mysqlx plugin (PROXYSQL40 not set)" instead
of building the .so. Clean targets stay unconditional.
* test/tap/tests/unit/mysqlx_admin_alias_resolution_unit-t.cpp:
delete the test (it covered resolve_admin_alias_to_canonical, which
is itself deleted). Remove from unit Makefile UNIT_TESTS list.
Verification:
* v3.0 stable build (`make`, no flags):
- Produces 147 MB src/proxysql binary
- `nm src/proxysql | grep -cE 'invoke_register_schemas_phase|GloPluginManager|materialize_plugin_tables'` -> 0
- mysqlx plugin .so is correctly skipped: "[skip] mysqlx plugin (PROXYSQL40 not set)"
* PROXYSQLGENAI=1 build (cascades PROXYSQL40/31/FFTO/TSDB):
- Produces 200 MB src/proxysql binary with chassis symbols
- mysqlx plugin .so built (9.2 MB), contains mysqlx_register_schemas
* All 5 plugin unit tests pass under PROXYSQL40:
plugin_config_unit-t 48/48
plugin_dispatch_unit-t 52/52
plugin_lifecycle_unit-t 26/26
plugin_query_hook_unit-t 46/46
plugin_prometheus_unit-t 10/10
|
2 months ago |
|
|
885712c679 |
Merge branch 'v3.0' into session-track-system-variable
|
2 months ago |
|
|
ab9d5a1036 |
fix(plugin-chassis): address deep-review findings
Follow-up to
|
2 months ago |
|
|
cd48c5613e |
feat(plugin-chassis): gate chassis ABI additions behind PROXYSQL40
The four-phase plugin lifecycle (Step 2.2), pre-execution query-hook ABI
(Step 2.1), shared Prometheus registry access (Step 2.3), and generic
admin-command alias dispatch (Step 2.5) introduce new plugin ABI surface
area — a new descriptor field (register_schemas), new services-struct
entries (register_query_hook, get_prometheus_registry,
register_command_alias), new public manager methods
(invoke_register_schemas_phase, dispatch_query_hook, etc.), and a new
startup ordering in main().
These changes should not appear in the v3.x stable/innovative tiers.
Wrap every chassis-only declaration, definition, and dispatch path in
`#ifdef PROXYSQL40` so the v3.0/v3.1 builds retain the pre-chassis
behavior: a single init()-only descriptor layout, two-phase load+init
lifecycle, and the hardcoded MYSQLX alias ladder in Admin_Handler.cpp.
Changes:
* Top-level Makefile: introduce PROXYSQL40 as a distinct tier above
PROXYSQL31. Cascade rule: PROXYSQLGENAI=1 implies PROXYSQL40=1
implies PROXYSQL31=1. Bump major version number on PROXYSQL40=1
(previously tied to PROXYSQLGENAI). Export PROXYSQL40 for recursive
submakes.
* lib/Makefile, src/Makefile: add -DPROXYSQL40 via $(PSQL40) so the
compile commands match the top-level tier selection.
* include/ProxySQL_Plugin.h: gate the register_schemas descriptor
field, query-hook types/enums/typedefs, the alias-registration
callback, and the trailing services-struct fields
(register_query_hook, get_prometheus_registry,
register_command_alias). The pre-chassis descriptor remains six
fields (name, abi_version, init, start, stop, status_json) so
plugins built against v3.x headers still link and load.
* include/ProxySQL_PluginManager.h: gate the chassis-exclusive public
methods and private members (schemas_registered, aliases,
services_phase_b_, query hook pointers). Provide two declarations
of proxysql_load_configured_plugins — the four-phase variant (paired
with proxysql_init_configured_plugins) under PROXYSQL40, the legacy
single-call variant otherwise.
* lib/ProxySQL_PluginManager.cpp: gate every method that belongs to
the chassis (invoke_register_schemas_phase, register_command_alias,
resolve_alias_to_canonical, register_query_hook, has_query_hook,
dispatch_query_hook), plus the services_phase_b_ initialization
(nullptr DB handle stubs), the proxysql_dispatch_configured_plugin_
query_hook entry point, and the alternate four-phase body of
proxysql_load_configured_plugins / proxysql_init_configured_plugins.
The legacy two-phase loader is restored under !PROXYSQL40.
get_admindb_service / get_configdb_service / get_statsdb_service
stay ungated because they back the unconditional services_ member.
* lib/Admin_Handler.cpp: gate the `#include \"ProxySQL_PluginManager.h\"`
and restore under !PROXYSQL40 the 16 hardcoded MYSQLX alias vectors
plus the 16-deep if-ladder in admin_handler_command_*(). Under
PROXYSQL40 the ladder is replaced by a single call to
proxysql_resolve_configured_plugin_admin_alias(), which is
plugin-agnostic.
* src/main.cpp: gate InitConfiguredPlugins and branch the startup
sequence in ProxySQL_Main_init_phase2___not_started:
- PROXYSQL40: LoadConfiguredPlugins (Phase A+B) -> admin init (C) ->
materialize_plugin_tables -> InitConfiguredPlugins (D) ->
StartConfiguredPlugins (E).
- !PROXYSQL40: admin init -> LoadConfiguredPlugins (combined
load+init) -> materialize_plugin_tables -> StartConfiguredPlugins,
matching the pre-chassis ordering.
* plugins/mysqlx/src/mysqlx_plugin.cpp: split init under PROXYSQL40
into mysqlx_register_schemas (schema + commands) and a minimal
mysqlx_init (context only). Under !PROXYSQL40 the original single
mysqlx_init remains. Descriptor conditionally wires
register_schemas.
* plugins/mysqlx/src/mysqlx_admin_schema.cpp: under PROXYSQL40 use
the reg() helper to register canonical+aliases via the new ABI;
under !PROXYSQL40 fall back to 16 direct register_command() calls
(aliases handled by Admin_Handler's hardcoded ladder).
* test/tap/test_helpers/fake_plugin.cpp: gate fake_query_hook,
fake_register_schemas, fake_descriptor_with_phase_b, the
REGISTER_QUERY_HOOK block in fake_init, and the ENABLE_PHASE_B
descriptor selection. The pre-chassis descriptor-returning stub is
the sole return path under !PROXYSQL40.
* test/tap/tests/unit/Makefile: autodetect PROXYSQL40 via
invoke_register_schemas_phase (the chassis-exclusive symbol) in the
library archive; add -DPROXYSQL40 to fake_plugin .so compilation so
the descriptor layout matches the loader's expectations; append the
three chassis-only tests (plugin_lifecycle, plugin_prometheus,
plugin_query_hook) to UNIT_TESTS only when PROXYSQL40=1.
* test/tap/tests/unit/plugin_config_unit-t.cpp,
test/tap/tests/unit/plugin_dispatch_unit-t.cpp: compile-time-gate
the chassis-only subtests, adjust plan() counts per mode
(48/50 with PROXYSQL40, 47/32 without), and provide a
proxysql_init_configured_plugins_compat() helper that is a no-op
returning true under !PROXYSQL40 so shared test helpers work in
both modes.
Verification — all plugin unit tests pass in both modes:
PROXYSQL40=1 build (chassis enabled):
plugin_config_unit-t: 48/48
plugin_dispatch_unit-t: 50/50
plugin_lifecycle_unit-t: 14/14
plugin_query_hook_unit-t: 41/41
PROXYSQL40=0 build (v3.x behavior):
plugin_config_unit-t: 47/47
plugin_dispatch_unit-t: 32/32
(plugin_lifecycle, plugin_query_hook not built)
|
2 months ago |
|
|
51535aaeca |
feat(plugin-abi): Step 2.5 — generic admin-command alias dispatch
Replaces the hardcoded 16-deep MYSQLX alias ladder in
lib/Admin_Handler.cpp with a table-driven lookup against
ProxySQL_PluginManager::registered_commands. New plugins register their
own canonical + alias spellings and admin dispatches them without any
core change.
ABI (include/ProxySQL_Plugin.h):
* New callback typedef proxysql_plugin_register_command_alias_cb.
* Appended register_command_alias field to ProxySQL_PluginServices.
Additive, backward-compatible; plugins built against the previous
layout don't read past the older tail (same tail-extension rule
used in Step 2.2 for register_schemas).
Manager (lib/ProxySQL_PluginManager.cpp):
* registered_command_t grows an `aliases` vector of normalized
alternate spellings.
* ProxySQL_PluginManager::register_command_alias(canonical, alias)
— rejects collision with any other command's canonical or alias;
idempotent for duplicate (canonical, alias) pairs.
* ProxySQL_PluginManager::resolve_alias_to_canonical(sql) — returns
the canonical c_str() if the normalized `sql` matches a registered
command's canonical or any of its aliases; nullptr otherwise.
* dispatch_admin_command extended to match aliases too, and now
passes the canonical form (not the incoming spelling) to the
plugin callback — plugins match their own canonical strings only.
* register_command_alias_service trampoline wires the callback into
the active manager during init. Phase-B services expose this as
well, consistent with register_command availability.
Core dispatch (lib/Admin_Handler.cpp):
* Deleted the 16 hardcoded MYSQLX alias vectors (LOAD/SAVE MYSQLX
{USERS,ROUTES,BACKEND ENDPOINTS,VARIABLES} {FROM MEMORY,TO DISK},
...) — ~65 lines.
* Replaced the 16-deep if-ladder of resolve_admin_alias_to_canonical
calls with a single proxysql_resolve_configured_plugin_admin_alias
call. No MYSQLX-specific code left in Admin_Handler; the
dispatcher is now plugin-agnostic.
mysqlx plugin (plugins/mysqlx/src/mysqlx_admin_schema.cpp):
* mysqlx_register_admin_schema now calls register_command_alias for
every user-friendly alternate spelling that previously lived in
Admin_Handler's hardcoded vectors:
LOAD MYSQLX * TO RUNTIME: "FROM MEMORY", "FROM MEM", "TO RUN"
SAVE MYSQLX * TO MEMORY: "TO MEM", "FROM RUNTIME", "FROM RUN"
(disk commands: no aliases, as before)
Tests:
* plugin_dispatch_unit-t gets one new case covering the spec's
requirement — two plugins registering non-conflicting commands,
each with its own alias set. Asserts: canonical dispatch routes
correctly; alias dispatch routes to the owning plugin's callback;
resolve_alias_to_canonical returns the canonical regardless of
which spelling was used to probe it; whitespace/case normalization;
cross-plugin alias shadowing is rejected; duplicate (canonical,
alias) registration is idempotent. plan(32) -> plan(50).
No behavior change for users: the exact same set of LOAD/SAVE MYSQLX
spellings continues to work; the routing now goes through the plugin
manager rather than through Admin_Handler-embedded tables. A future
plugin that wants to add admin commands can do so entirely from its
own source — no core touch required.
|
2 months ago |
|
|
f13463d334 |
feat(plugin-abi): Step 2.2 — four-phase plugin lifecycle
Adds the register_schemas() callback to the plugin descriptor and
wires the loader + main.cpp so that a plugin's schema is declared
BEFORE the admin module bootstraps, and the plugin's init() runs
AFTER the schema has been materialized.
Phase sequence (previously two-phase: load+init):
A. load() — dlopen each .so, read the descriptor.
B. register_schemas() — NEW, optional; plugin calls services->register_table
with its admin/config/stats table defs. Services
hand has register_table live but all DB-handle
getters stubbed to nullptr.
C. ProxySQL_Main_init_Admin_module + GloAdmin->materialize_plugin_tables
— admin bootstrap drains the pending-tables list
via merge_plugin_tables (first-boot == reload).
D. init() — plugin's startup logic with full services
(live DB handles pointing at a schema that
already contains its own tables).
E. start() — plugin launches threads / accept loops.
ABI (include/ProxySQL_Plugin.h):
* New callback typedef proxysql_plugin_register_schemas_cb.
* New descriptor field register_schemas — appended at the end of
ProxySQL_PluginDescriptor so existing 6-field initializers leave
it null (aggregate init value-initializes the trailing slot).
Plugins that left register_schemas null keep the pre-existing
two-phase behavior: Phase B is skipped and their init() is
responsible for both schema registration and startup work.
* Phase-by-phase services availability matrix documented next to
ProxySQL_PluginServices.
Manager (lib/ProxySQL_PluginManager.cpp):
* invoke_register_schemas_phase(err) — public method that runs
Phase B for every plugin whose descriptor sets the callback.
Uses a "services_phase_b_" variant where DB-handle getters are
nullptr stubs; register_query_hook returns false with a warning
(hooks cannot register this early).
* proxysql_load_configured_plugins() — now stops at Phase B. On
success, installs the built manager as the active one so that
ProxySQL_Admin::materialize_plugin_tables (which reads from the
active manager's pending-tables list) sees the registered
tables.
* proxysql_init_configured_plugins() — new entry point, calls
init_all() for Phase D. Separated from load so admin bootstrap
can run between the two.
Main (src/main.cpp):
* Reorder in ProxySQL_Main_init_phase2___not_started:
LoadConfiguredPlugins(); // Phase A + B
ProxySQL_Main_init_Admin_module(...); // Phase C
GloAdmin->materialize_plugin_tables(); // Phase C (materialize)
InitConfiguredPlugins(); // Phase D — NEW
StartConfiguredPlugins(); // Phase E
* Error messages updated to name the phase that failed.
mysqlx plugin:
* mysqlx_init split into:
- mysqlx_register_schemas() — calls mysqlx_register_admin_schema
(pure services->register_table use, no DB access);
- mysqlx_init() — wires the plugin context (services, config
store, started flag) and returns true.
* Descriptor wires the new callback, exercising the full four-
phase path end-to-end.
Tests:
* plugin_lifecycle_unit-t (new) — four cases: both callbacks fire in
order; register_schemas-null skips Phase B; Phase-B DB handles
return nullptr; Phase-B failure aborts init.
* plugin_config_unit-t, plugin_dispatch_unit-t, plugin_query_hook_unit-t
— existing tests updated to also call proxysql_init_configured_plugins
after proxysql_load_configured_plugins where init side-effects are
asserted. test_init_registration_failure_aborts_load reworked: the
fake plugin's invalid-table registration happens in init (not
register_schemas), so load now succeeds and the failure surfaces at
init time.
* test_mysqlx_admin_tables / test_mysqlx_listener_smoke — add an
explicit invoke_register_schemas_phase() call before init_all()
so the mysqlx plugin's schema gets registered under its new
split.
No behavior change for plugins that didn't opt into Phase B: they
still see the same services_ layout and the same {load, init, start,
stop} ordering. Opt-in plugins gain DB access from init() against a
schema that already contains their tables, eliminating the chicken-
and-egg that required ProtocolX's
|
2 months ago |
|
|
f104116926 |
fix(plugin-mgr): serialize lifecycle and always reset manager on stop
Add g_plugin_lifecycle_mutex to serialize load/start/stop transitions so two reload paths cannot race on g_registry_target and the registration-failure globals. g_active_plugin_manager_mutex remains the pointer-read guard for the dispatch path; the new mutex is held only for the duration of a lifecycle transition. In proxysql_stop_configured_plugins, always call manager.reset() so the .so is unmapped and no stale function pointers remain reachable, even when stop_all() reports a failure. stop_all() is idempotent across failure (each plugin is marked stopped after one attempt) so the destructor's stop_all() will be a no-op. Also fix the log-level table in PLUGIN_API.md: numeric levels are 3 (Error), 4 (Warning), and any-other (Info), matching the internal proxy_* severity scheme — not 1/2/3 as previously documented. |
2 months ago |
|
|
96c686dafa |
MySQL_Monitor: GR thread ignores cached ping state on first iteration
Why: mysql_server_ping_log can carry stale "unpingable" entries from before monitor_GR_thread_HG was (re)started (e.g. a previous test left those hostnames marked bad). With the cache filter applied on the very first cycle, find_resp_srvs() returns empty, the writer HG stays empty for a full healthcheck_interval, and GR-based tests flake on warm-up. Probe all configured hosts on the first iteration so subsequent cycles see a fresh ping_log. The filter resumes from iteration two onward. |
2 months ago |
|
|
398b833aeb |
feat(plugin-abi): Step 2.3 — shared Prometheus registry access
Second half of Step 2 from the GenAI plugin carve-out design. Lets plugins register their counters / gauges / histograms against the same prometheus::Registry core uses; their metrics surface alongside core's at the same /metrics endpoint scrapers already poll. ABI extension (include/ProxySQL_Plugin.h): - Forward-declare prometheus::Registry to avoid pulling prometheus-cpp into the plugin ABI header. - proxysql_plugin_get_prometheus_registry_cb typedef. - New field on ProxySQL_PluginServices: get_prometheus_registry, added at the end of the struct (additive -- older plugins stop reading at the previous member). Manager (lib/ProxySQL_PluginManager.cpp): - get_prometheus_registry_service() returns GloVars.prometheus_registry.get(). - Wired into services_ at construction. Lifetime is simpler than initially documented: GloVars (and its prometheus_registry shared_ptr, allocated in its constructor) exists before any plugin is loaded, so the service returns non-null for every callback the plugin will see (init, start, admin command callback, query hook). ABI-header docs updated to reflect this -- plugins MAY register metrics in init() if they want them visible from first scrape. New unit test: plugin_prometheus_unit-t (10 assertions) - after test_init_minimal, GloVars.prometheus_registry is non-null. - A counter registered through prometheus-cpp directly (BuildCounter + Register(*reg)) is visible in the registry's text serialisation by name; the counter's Value() reflects increments through the same prometheus-cpp API plugins will use. - The registry pointer captured BEFORE plugin load equals the pointer observed during init/start AND after stop -- the loader does not swap, replace, or null out the registry; it only installs a service callback that points at it. All 60 unit-test binaries pass. Note: this finishes the Step 2 ABI surface (query hook + Prometheus registry). Step 3 starts moving real GenAI subsystems -- Anomaly_Detector first -- which will be the first consumer of the query hook from inside the plugin and the first plugin to register a metric against the shared Prometheus registry. |
2 months ago |
|
|
55556979e0 |
feat(plugin-abi): Step 2.1 — pre-execution query-hook ABI + dispatch
First half of Step 2 from the GenAI plugin carve-out design (see
docs/superpowers/specs/2026-04-16-genai-plugin-carveout-design.md).
Adds the query-hook ABI surface and the manager-level dispatch, but
NOT yet the hot-path call sites in MySQL_Session / PgSQL_Session --
those are intentionally split into the next commit so the hot-path
diff stays small and reviewable on its own.
ABI extension (include/ProxySQL_Plugin.h):
- ProxySQL_PluginProtocol enum {mysql, pgsql}
- ProxySQL_PluginQueryHookPayload {user, client_ip, schema, query_text, query_len}
- ProxySQL_PluginQueryHookAction enum {allow, deny}
- ProxySQL_PluginQueryHookResult {action, message}
- proxysql_plugin_query_hook_cb typedef
- proxysql_plugin_register_query_hook_cb typedef
- new field on ProxySQL_PluginServices: register_query_hook
(additive at the end of the struct -- older plugins that were built
against the previous layout don't read past it; new plugins must
check non-null before calling, same convention used elsewhere)
Manager (lib/ProxySQL_PluginManager.cpp + .h):
- One hook per protocol per manager (nullptr means "no hook").
- register_query_hook(proto, cb): rejects null cb; rejects duplicate
registration for a protocol that already has a hook; surfaces
registration errors through the same note_registration_failure /
init-aborts path that register_table and register_command use.
- dispatch_query_hook(proto, payload, result): synchronous,
returns true if a hook fired and stored its result, false otherwise.
- has_query_hook(proto): cheap predicate for the hot-path predicate.
Global helpers (for the hot path, next commit):
- proxysql_dispatch_configured_plugin_query_hook(proto, payload, result):
takes the active-manager mutex, dispatches if there is one.
- proxysql_has_configured_plugin_query_hook(proto): lock-free atomic
read of the manager pointer + a pointer-sized field read. Designed
to be the gate the hot path checks first; the dispatch call can be
elided entirely on the no-plugin path. Documented as
spurious-true-tolerant -- the dispatch helper re-checks under the
lock.
Test plugin extension (test/tap/test_helpers/fake_plugin.cpp):
- New env vars PROXYSQL_FAKE_PLUGIN[2]_REGISTER_QUERY_HOOK,
REGISTER_QUERY_HOOK_PROTO ("mysql"|"pgsql"), HOOK_DENY.
- fake_query_hook echoes the SQL through the result message so tests
can verify the payload reached the callback intact.
New unit test: plugin_query_hook_unit-t (41 assertions)
- empty manager: neither protocol has a hook, dispatch returns false,
result struct untouched
- register MySQL allow hook → dispatch returns ALLOW with empty msg
- register MySQL deny hook → dispatch returns DENY with the message
- null callback rejected; duplicate registration rejected;
original hook still in effect after rejected duplicate
- protocols are independent (mysql allow + pgsql deny coexist)
- payload threaded through (echo hook reads user/ip/schema/sql)
- global dispatcher: false when no active manager, untouched result
- global dispatcher with active manager: routes to the fake plugin's
hook, propagates ALLOW vs DENY (env-flipped between calls), and
goes back to false after stop_configured_plugins
All 59 unit-test binaries pass.
|
2 months ago |
|
|
3d107c3bed |
chore: commit pre-existing plugin manager improvements
Improvements left uncommitted at the end of Step 0: - atomic g_active_plugin_manager pointer - reject plugin descriptors with null/empty name - assert single-threaded init phase - debug-log dispatched plugin commands - propagate mysqlx_register_admin_schema failure into init - additional plugin manager unit tests (init/start/stop fail, double load) - updated ABI doc/comments for table copy semantics, command context lifetime, services availability, and status_json storage |
2 months ago |