doc/PROMETHEUS_PROTOCOL_LABELS.md
This document describes the features introduced in the v3.0-5069 branch that resolve prometheus metric collisions between MySQL and PostgreSQL modules by adding protocol labels to distinguish metrics from different database protocols.
Prior to this branch, ProxySQL used identical metric names for both MySQL and PostgreSQL modules. When both modules tried to register prometheus metrics with the same name and labels, they would either:
This resulted in users seeing confusing behavior where:
proxysql_client_connections_total{status="aborted"} 4 (MySQL metrics only)Client_Connections_aborted: 91233 (PostgreSQL stats)The metric collision occurred because both modules used identical metric names with identical labels:
// MySQL_HostGroups_Manager.cpp
"proxysql_client_connections_total", metric_tags { { "status", "created" } }
// PgSQL_HostGroups_Manager.cpp
"proxysql_client_connections_total", metric_tags { { "status", "created" } } // IDENTICAL!
When both instances called BuildCounter().Register(...).Add(metric_tags) with the same name and labels, they shared the same counter.
The solution adds a protocol label to all prometheus metrics to distinguish MySQL and PostgreSQL sources:
proxysql_client_connections_total{protocol="mysql",status="created"} 1000
proxysql_client_connections_total{protocol="pgsql",status="created"} 500
This follows prometheus best practices where metrics that represent different entities should be distinguished using labels.
| File | Lines Changed | Description |
|---|---|---|
lib/MySQL_HostGroups_Manager.cpp | +25/-10 | Added protocol="mysql" labels to MyHGM metrics |
lib/MySQL_Thread.cpp | +340/-71 | Added protocol="mysql" labels to 65 MTH metrics |
lib/PgSQL_HostGroups_Manager.cpp | +25/-10 | Added protocol="pgsql" labels to PgHGM metrics |
lib/PgSQL_Thread.cpp | +310/-42 | Added protocol="pgsql" labels to 60 PTH metrics |
lib/ProxySQL_Admin.cpp | +18/-18 | Enabled GloPTH and GloPgQC metrics |
lib/Query_Cache.cpp | +133/-15 | Added protocol labels with compile-time type selection |
| Total | +784/-147 | 6 files modified |
Added protocol="mysql" labels to the following metrics:
proxysql_server_connections_total (created/delayed/aborted)proxysql_client_connections_total (created/aborted)proxysql_access_denied_wrong_password_totalproxysql_access_denied_max_connections_totalproxysql_access_denied_max_user_connections_totalproxysql_server_connections_connectedproxysql_client_connections_connectedAdded protocol="pgsql" labels to the same metrics for PostgreSQL.
Added protocol="mysql" labels to 65 thread handler metrics including:
proxysql_queries_backends_bytes_total (sent/received)proxysql_queries_frontends_bytes_total (sent/received)proxysql_query_processor_time_seconds_totalproxysql_backend_query_time_seconds_totalproxysql_com_backend_stmt_total (prepare/execute/close)proxysql_com_frontend_stmt_total (prepare/execute/close)proxysql_questions_totalproxysql_slow_queries_totalAdded protocol="pgsql" labels to the same 60 metrics for PostgreSQL.
This module presented a unique challenge because it uses a template class:
template <typename QC_DERIVED>
class Query_Cache : public /* ... */ { ... }
Both MySQL_Query_Cache and PgSQL_Query_Cache inherit from this template and share the same metrics map.
Solution: Created two separate metric maps:
qc_metrics_map_mysql - with { "protocol", "mysql" } labelsqc_metrics_map_pgsql - with { "protocol", "pgsql" } labelsThe constructor uses if constexpr (std::is_same_v<QC_DERIVED, MySQL_Query_Cache>) to select the appropriate map at compile time:
template <typename QC_DERIVED>
Query_Cache<QC_DERIVED>::Query_Cache() {
// ... existing code ...
// Select metrics map based on derived type
if constexpr (std::is_same_v<QC_DERIVED, MySQL_Query_Cache>) {
init_prometheus_counter_array<qc_metrics_map_idx, p_qc_counter>(
qc_metrics_map_mysql, this->metrics.p_counter_array);
init_prometheus_gauge_array<qc_metrics_map_idx, p_qc_gauge>(
qc_metrics_map_mysql, this->metrics.p_gauge_array);
} else {
init_prometheus_counter_array<qc_metrics_map_idx, p_qc_counter>(
qc_metrics_map_pgsql, this->metrics.p_counter_array);
init_prometheus_gauge_array<qc_metrics_map_idx, p_qc_gauge>(
qc_metrics_map_pgsql, this->metrics.p_gauge_array);
}
}
Added protocol labels to 8 Query Cache metrics:
proxysql_query_cache_count_get_total (status=err/ok)proxysql_query_cache_count_set_totalproxysql_query_cache_bytes_total (op=written/read)proxysql_query_cache_purged_totalproxysql_query_cache_entries_totalproxysql_query_cache_memory_bytesEnabled the following metrics updates in update_modules_metrics():
void update_modules_metrics() {
// ... existing MySQL metrics updates ...
// PostgreSQL host groups manager metrics
if (PgHGM) {
PgHGM->p_update_metrics();
}
// PostgreSQL threads handler metrics
if (GloPTH) {
GloPTH->p_update_metrics();
}
// PostgreSQL query cache metrics
if (GloPgQC) {
GloPgQC->p_update_metrics();
}
// ... rest of metrics updates ...
}
The Query Cache implementation uses C++17 type traits for zero-overhead compile-time dispatch:
#include <type_traits>
// Returns true at compile time when QC_DERIVED is MySQL_Query_Cache
if constexpr (std::is_same_v<QC_DERIVED, MySQL_Query_Cache>) {
// Use MySQL metrics map
} else {
// Use PostgreSQL metrics map
}
Benefits:
if constexpr branch is resolved at compile timeAll metrics now follow this label structure:
Before:
metric_tags { { "status", "created" } }
After:
metric_tags { { "status", "created" }, { "protocol", "mysql" } }
For metrics without existing labels:
metric_tags { { "protocol", "mysql" } }
# MySQL Thread Handler Metrics
proxysql_queries_backends_bytes_total{protocol="mysql",traffic_flow="sent"} 1234567
proxysql_queries_backends_bytes_total{protocol="mysql",traffic_flow="received"} 2345678
proxysql_com_backend_stmt_total{protocol="mysql",op="prepare"} 100
proxysql_com_backend_stmt_total{protocol="mysql",op="execute"} 1000
proxysql_questions_total{protocol="mysql"} 50000
# PostgreSQL Thread Handler Metrics
proxysql_queries_backends_bytes_total{protocol="pgsql",traffic_flow="sent"} 345678
proxysql_queries_backends_bytes_total{protocol="pgsql",traffic_flow="received"} 456789
proxysql_com_backend_stmt_total{protocol="pgsql",op="prepare"} 50
proxysql_com_backend_stmt_total{protocol="pgsql",op="execute"} 500
proxysql_questions_total{protocol="pgsql"} 25000
# MySQL Host Groups Manager Metrics
proxysql_client_connections_total{protocol="mysql",status="created"} 1000000
proxysql_client_connections_total{protocol="mysql",status="aborted"} 5000
proxysql_server_connections_total{protocol="mysql",status="created"} 200000
proxysql_access_denied_wrong_password_total{protocol="mysql"} 100
proxysql_server_connections_connected{protocol="mysql"} 500
# PostgreSQL Host Groups Manager Metrics
proxysql_client_connections_total{protocol="pgsql",status="created"} 500000
proxysql_client_connections_total{protocol="pgsql",status="aborted"} 2500
proxysql_server_connections_total{protocol="pgsql",status="created"} 100000
proxysql_access_denied_wrong_password_total{protocol="pgsql"} 50
proxysql_server_connections_connected{protocol="pgsql"} 250
# MySQL Query Cache Metrics
proxysql_query_cache_count_get_total{protocol="mysql",status="err"} 100
proxysql_query_cache_count_get_total{protocol="mysql",status="ok"} 50000
proxysql_query_cache_count_set_total{protocol="mysql"} 25000
proxysql_query_cache_bytes_total{protocol="mysql",op="read"} 104857600
proxysql_query_cache_bytes_total{protocol="mysql",op="written"} 52428800
proxysql_query_cache_entries_total{protocol="mysql"} 1000
proxysql_query_cache_memory_bytes{protocol="mysql"} 2097152
# PostgreSQL Query Cache Metrics
proxysql_query_cache_count_get_total{protocol="pgsql",status="err"} 50
proxysql_query_cache_count_get_total{protocol="pgsql",status="ok"} 25000
proxysql_query_cache_count_set_total{protocol="pgsql"} 12500
proxysql_query_cache_bytes_total{protocol="pgsql",op="read"} 52428800
proxysql_query_cache_bytes_total{protocol="pgsql",op="written"} 26214400
proxysql_query_cache_entries_total{protocol="pgsql"} 500
proxysql_query_cache_memory_bytes{protocol="pgsql"} 1048576
None. The changes only add new labels to existing metrics. Existing prometheus queries that don't filter by protocol will continue to work but will now see duplicate time series.
Before: Queries would return a single time series per metric:
proxysql_client_connections_total{status="created"}
After: Queries will return multiple time series (one per protocol):
proxysql_client_connections_total{status="created"}
To maintain existing behavior, users should add a protocol filter:
proxysql_client_connections_total{status="created",protocol="mysql"}
Total connections across all protocols:
sum(proxysql_client_connections_total{status="created"})
MySQL-only metrics:
proxysql_client_connections_total{status="created",protocol="mysql"}
PostgreSQL-only metrics:
proxysql_client_connections_total{status="created",protocol="pgsql"}
To verify the implementation:
Build ProxySQL:
make clean && make -j4
Check prometheus metrics:
curl localhost:6070/metrics | grep -E "protocol=.*(mysql|pgsql)"
Verify separate metrics exist:
curl localhost:6070/metrics | grep "proxysql_client_connections_total"
Expected output:
proxysql_client_connections_total{protocol="mysql",status="aborted"} X
proxysql_client_connections_total{protocol="mysql",status="created"} Y
proxysql_client_connections_total{protocol="pgsql",status="aborted"} Z
proxysql_client_connections_total{protocol="pgsql",status="created"} W
fa35bda62 - Merge pull request #5069
2b44aaa58 - Add protocol labels for shared metrics between mysql and psql
949eda1cc - Generate postgres metrics in addition to mysql metrics
778e01174 - Add protocol labels to Thread Handler metrics and enable PostgreSQL metrics
3ab964010 - Add protocol labels to Query Cache metrics and enable PostgreSQL QC metrics
#include <type_traits>This implementation fully resolves Issue #5068 by adding protocol labels to all prometheus metrics, allowing MySQL and PostgreSQL modules to coexist without metric collisions. The solution:
Users can now monitor MySQL and PostgreSQL metrics independently using protocol filters, providing complete visibility into both database protocols.