From 78400743ff164b6e81c4af278ad087b75ee7bd3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sun, 22 Mar 2026 11:57:04 +0100 Subject: [PATCH 1/2] Extract PgSQL monitor health decisions (Phase 3.9, #5497) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New: include/PgSQLMonitorDecision.h, lib/PgSQLMonitorDecision.cpp Functions: - pgsql_should_shun_on_ping_failure(): threshold-based shunning (simpler than MySQL — always aggressive with kill_all) - pgsql_should_offline_for_readonly(): read-only server in writer hostgroup should go OFFLINE_SOFT Unshun recovery is already covered by MonitorHealthDecision.h can_unshun_server() (same logic for both protocols). --- include/PgSQLMonitorDecision.h | 44 ++++++++++++++++++++++++++++++++++ lib/Makefile | 1 + lib/PgSQLMonitorDecision.cpp | 27 +++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 include/PgSQLMonitorDecision.h create mode 100644 lib/PgSQLMonitorDecision.cpp diff --git a/include/PgSQLMonitorDecision.h b/include/PgSQLMonitorDecision.h new file mode 100644 index 000000000..ef309cb6c --- /dev/null +++ b/include/PgSQLMonitorDecision.h @@ -0,0 +1,44 @@ +/** + * @file PgSQLMonitorDecision.h + * @brief Pure decision functions for PgSQL monitor health state. + * + * PgSQL monitor is simpler than MySQL — it uses ping failure + * threshold directly with shun_and_killall() (always aggressive). + * Unshunning follows the same time-based recovery as MySQL + * (already covered by MonitorHealthDecision.h can_unshun_server). + * + * @see Phase 3.9 (GitHub issue #5497) + */ + +#ifndef PGSQL_MONITOR_DECISION_H +#define PGSQL_MONITOR_DECISION_H + +/** + * @brief Determine if a PgSQL server should be shunned based on ping failures. + * + * PgSQL monitor shuns servers when they miss N consecutive heartbeats. + * Unlike MySQL, PgSQL always uses aggressive shunning (kill all connections). + * + * @param missed_heartbeats Number of consecutive missed pings. + * @param max_failures_threshold Config: pgsql-monitor_ping_max_failures. + * @return true if the server should be shunned. + */ +bool pgsql_should_shun_on_ping_failure( + unsigned int missed_heartbeats, + unsigned int max_failures_threshold +); + +/** + * @brief Determine if a PgSQL server's read-only status indicates it should + * be taken offline for a writer hostgroup. + * + * @param is_read_only Whether the server reports read_only=true. + * @param is_writer_hg Whether this is a writer hostgroup. + * @return true if the server should be set to OFFLINE_SOFT. + */ +bool pgsql_should_offline_for_readonly( + bool is_read_only, + bool is_writer_hg +); + +#endif // PGSQL_MONITOR_DECISION_H diff --git a/lib/Makefile b/lib/Makefile index 7af5cd6ef..b5cf85df3 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -108,6 +108,7 @@ _OBJ_CXX := ProxySQL_GloVars.oo network.oo debug.oo configfile.oo Query_Cache.oo MonitorHealthDecision.oo \ TransactionState.oo \ HostgroupRouting.oo \ + PgSQLMonitorDecision.oo \ proxy_sqlite3_symbols.oo # TSDB object files diff --git a/lib/PgSQLMonitorDecision.cpp b/lib/PgSQLMonitorDecision.cpp new file mode 100644 index 000000000..3effd2419 --- /dev/null +++ b/lib/PgSQLMonitorDecision.cpp @@ -0,0 +1,27 @@ +/** + * @file PgSQLMonitorDecision.cpp + * @brief Implementation of PgSQL monitor health decisions. + * + * @see PgSQLMonitorDecision.h + * @see Phase 3.9 (GitHub issue #5497) + */ + +#include "PgSQLMonitorDecision.h" + +bool pgsql_should_shun_on_ping_failure( + unsigned int missed_heartbeats, + unsigned int max_failures_threshold) +{ + if (max_failures_threshold == 0) { + return false; // shunning disabled + } + return (missed_heartbeats >= max_failures_threshold); +} + +bool pgsql_should_offline_for_readonly( + bool is_read_only, + bool is_writer_hg) +{ + // A read-only server in a writer hostgroup should go OFFLINE_SOFT + return (is_read_only && is_writer_hg); +} From fe49380e3f5df78d68eab11e3e7cb45f89b2374f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Canna=C3=B2?= Date: Sun, 22 Mar 2026 11:57:04 +0100 Subject: [PATCH 2/2] Add PgSQL monitor unit tests (Phase 3.9, #5497) 11 test cases: ping shunning (threshold, boundary, disabled), read-only offline (writer/reader HG combinations). --- test/tap/tests/unit/Makefile | 3 +- test/tap/tests/unit/pgsql_monitor_unit-t.cpp | 51 ++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 test/tap/tests/unit/pgsql_monitor_unit-t.cpp diff --git a/test/tap/tests/unit/Makefile b/test/tap/tests/unit/Makefile index 999f3c30c..440a04822 100644 --- a/test/tap/tests/unit/Makefile +++ b/test/tap/tests/unit/Makefile @@ -235,7 +235,8 @@ UNIT_TESTS := smoke_test-t query_cache_unit-t query_processor_unit-t \ protocol_unit-t auth_unit-t connection_pool_unit-t \ rule_matching_unit-t hostgroups_unit-t monitor_health_unit-t \ hostgroup_routing_unit-t \ - transaction_state_unit-t + transaction_state_unit-t \ + pgsql_monitor_unit-t .PHONY: all all: $(UNIT_TESTS) diff --git a/test/tap/tests/unit/pgsql_monitor_unit-t.cpp b/test/tap/tests/unit/pgsql_monitor_unit-t.cpp new file mode 100644 index 000000000..a4734595b --- /dev/null +++ b/test/tap/tests/unit/pgsql_monitor_unit-t.cpp @@ -0,0 +1,51 @@ +/** + * @file pgsql_monitor_unit-t.cpp + * @brief Unit tests for PgSQL monitor health decisions. + * + * @see Phase 3.9 (GitHub issue #5497) + */ + +#include "tap.h" +#include "test_globals.h" +#include "test_init.h" +#include "proxysql.h" +#include "PgSQLMonitorDecision.h" + +static void test_ping_shunning() { + ok(pgsql_should_shun_on_ping_failure(3, 3) == true, + "shun: failures=3 meets threshold=3"); + ok(pgsql_should_shun_on_ping_failure(5, 3) == true, + "shun: failures=5 exceeds threshold=3"); + ok(pgsql_should_shun_on_ping_failure(2, 3) == false, + "no shun: failures=2 below threshold=3"); + ok(pgsql_should_shun_on_ping_failure(0, 3) == false, + "no shun: zero failures"); + ok(pgsql_should_shun_on_ping_failure(1, 1) == true, + "shun: threshold=1, single failure"); + ok(pgsql_should_shun_on_ping_failure(10, 0) == false, + "no shun: threshold=0 (disabled)"); +} + +static void test_readonly_offline() { + ok(pgsql_should_offline_for_readonly(true, true) == true, + "offline: read_only + writer HG"); + ok(pgsql_should_offline_for_readonly(true, false) == false, + "not offline: read_only + reader HG"); + ok(pgsql_should_offline_for_readonly(false, true) == false, + "not offline: not read_only + writer HG"); + ok(pgsql_should_offline_for_readonly(false, false) == false, + "not offline: not read_only + reader HG"); +} + +int main() { + plan(11); + int rc = test_init_minimal(); + ok(rc == 0, "test_init_minimal() succeeds"); + + test_ping_shunning(); // 6 + test_readonly_offline(); // 4 + // Total: 1+6+4 = 11 + + test_cleanup_minimal(); + return exit_status(); +}