Skip to content

Conversation

@wazir-ahmed
Copy link
Member

@wazir-ahmed wazir-ahmed commented Oct 16, 2025

  • Configure session_track_system_variables and session_track_state_change in all backend connections.
  • Utilize notifications from backend servers to capture system variable changes that cannot be handled by MySQL_Set_Stmt_Parser
  • Update both client and server variable maps based on backend responses.

[skip-ci]

Summary by CodeRabbit

  • New Features

    • Session variable tracking with DISABLED/OPTIONAL/ENFORCED modes to sync client and backend session variables.
    • Server backoff to temporarily exclude servers from selection when required capabilities are missing.
    • Client capability advertisement adjusted to enable session-tracking support; automatic backend session-variable retrieval and propagation.
  • Tests

    • New TAP tests and test infra scripts validating session-variable tracking across MySQL versions and fast-forward scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

@gemini-code-assist
Copy link

Summary of Changes

Hello @wazir-ahmed, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces comprehensive support for tracking session-specific system variable changes within MySQL backend connections. It establishes a robust mechanism to configure the necessary session tracking settings, process notifications from the backend when variables change, and synchronize these changes across the internal client and server variable states. This enhancement improves the accuracy of ProxySQL's understanding of the backend session state, particularly for variables that are not easily parsed from SQL statements.

Highlights

  • Session Variable Tracking Configuration: Implemented logic to configure session_track_system_variables and session_track_state_change on all backend connections to enable server-side tracking of session variable changes.
  • Backend Notification Processing: Added mechanisms to utilize notifications from backend servers to capture system variable changes that cannot be reliably handled by statement parsing alone.
  • Variable Map Synchronization: Ensured that both client and server internal variable maps are accurately updated based on the session variable changes communicated from the backend responses, maintaining state consistency.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces tracking for session-specific system variables in MySQL by leveraging session_track_system_variables and session_track_state_change. The changes include new handlers and logic to set and process these variables, ensuring that changes are captured and synchronized between the client and server. My review focuses on improving code safety, clarity, and performance in the new implementations. I've suggested using safer string comparison functions, reducing variable scopes for better readability, and making minor performance optimizations in string handling. I also pointed out a commented-out line that should be removed.

@wazir-ahmed wazir-ahmed force-pushed the session-track-system-variable branch from d817bda to 346030e Compare October 17, 2025 07:19
@wazir-ahmed wazir-ahmed force-pushed the session-track-system-variable branch 2 times, most recently from a22aa90 to e059a67 Compare November 2, 2025 21:57
@wazir-ahmed wazir-ahmed force-pushed the session-track-system-variable branch 4 times, most recently from f1f57ac to 7709911 Compare November 11, 2025 07:56
@wazir-ahmed wazir-ahmed force-pushed the session-track-system-variable branch 3 times, most recently from 826fd70 to c5aed61 Compare November 24, 2025 10:39
@noizu noizu added this to the Release 3.0.4 milestone Nov 25, 2025
@renecannao
Copy link
Contributor

PR 5166: Comprehensive Review and Concerns - Detailed Analysis

First, excellent work on implementing session variable tracking! This is a valuable enhancement that addresses real limitations in ProxySQL's session state management. The architectural approach leveraging MySQL's native session tracking capabilities is sound and well-designed.

However, I have identified several critical issues and areas for improvement that should be addressed before merge. This analysis provides detailed technical background for each concern.

🔴 Critical Issues (Blockers)

1. Missing Capability Detection - Protocol Level Compatibility Gap

Problem: The implementation lacks proper capability detection for CLIENT_SESSION_TRACK, unlike the existing GTID tracking.

Technical Background:
In MySQL's client/server protocol, both client and server exchange capability flags during connection handshake. CLIENT_SESSION_TRACK is a capability flag that indicates the server supports MySQL 5.7+ session tracking features. When this flag is not set:

  • Server does not track session variable changes
  • Server cannot send session tracking information in OK packets
  • Attempting to SET session_track_system_variables will likely fail
  • MySQL client library functions like mysql_session_track_get_first() will not work

Current Implementation Gap:

// ✅ GTID tracking correctly checks capability:
bool MySQL_Session::handler_again___verify_backend_session_track_gtids() {
    if ((mybe->server_myds->myconn->mysql->server_capabilities & CLIENT_SESSION_TRACK) == 0) {
        // the backend doesn't support CLIENT_SESSION_TRACK
        return ret; // Gracefully handles unsupported backends
    }
    // ... proceed with configuration
}

// ❌ Variable tracking attempts SET commands without capability check:
bool MySQL_Session::handler_again___verify_backend_session_track_variables() {
    if (mysql_thread___session_track_variables == session_track_variables::DISABLED) {
        return false;
    }
    // ❌ No CLIENT_SESSION_TRACK check - will try to SET on unsupported servers!
    if (mybe->server_myds->myconn->options.session_track_variables_sent == false) {
        // This will fail on MySQL < 5.7, MariaDB, SQLite3, etc.
        NEXT_IMMEDIATE_NEW(SETTING_SESSION_TRACK_VARIABLES);
    }
}

Impact: This will cause connection failures on:

  • MySQL < 5.7: Session tracking was introduced in MySQL 5.7.4
  • MariaDB < 10.2: MariaDB implemented this later than MySQL
  • ProxySQL's own admin interface: Uses SQLite3 backend which doesn't support MySQL protocol extensions
  • sqlite3-server module: SQLite3 backend in ProxySQL
  • Any non-MySQL backend that uses the MySQL Protocol

Fix Required: Add the same capability check that GTID tracking uses before attempting any SET commands.

2. CLIENT_DEPRECATE_EOF Dependency Not Handled

Technical Background:
MySQL session tracking requires two capability flags to work properly:

  1. CLIENT_SESSION_TRACK : Enables session tracking protocol support
  2. CLIENT_DEPRECATE_EOF : Replaces EOF packets with OK packets, required for session tracking data

When CLIENT_DEPRECATE_EOF is not set, MySQL server sends EOF packets instead of OK packets with session tracking data. Session tracking information is only transmitted in OK packets, not EOF packets.

Current State: The implementation assumes backends support session tracking without verifying both required capabilities.

Impact: Even if a server supports CLIENT_SESSION_TRACK, without CLIENT_DEPRECATE_EOF the session tracking data cannot be transmitted to the client. I actually expect that CLIENT_DEPRECATE_EOF is a requirement for CLIENT_SESSION_TRACK.

🟡 Major Concerns

3. TAP Test Modifications Need Explanation

Observation: Two TAP tests were modified to explicitly disable session tracking:

// test_binlog_reader-t.cpp:
MYSQL_QUERY_T(proxysql_admin, "SET mysql-session_track_variables=0");
MYSQL_QUERY_T(proxysql_admin, "LOAD MYSQL VARIABLES TO RUNTIME");

// reg_test_4264-commit_rollback-t.cpp:
MYSQL_QUERY_T(admin, "SET mysql-session_track_variables=0");

Possible Root Causes:

  • Test Failures: Tests likely fail when session tracking is enabled due to the capability issues above
  • State Pollution: The SET commands might affect test state in unexpected ways
  • Variable Behavior Changes: Session tracking changes how variables are managed, potentially breaking test assumptions

Action Required: We need to understand the exact failure modes before merge. These test modifications suggest issues were encountered but worked around them instead of fixing the root cause.

4. Fast Forward Mode Compatibility

Technical Background:
Fast forward mode in ProxySQL provides high-performance query forwarding with minimal protocol overhead. It's designed for scenarios where ProxySQL acts as a transparent proxy with minimal processing.

Session Tracking Impact:

  • Packet Format Changes: Session tracking adds data to OK packets, changing the protocol stream
  • Client Capability Mismatch: Fast forward may assign connections to clients that don't support session tracking

Current Gap: No checking of client capabilities when assigning connections in fast forward mode.

Impact: Clients without session tracking support may receive packets they cannot parse or unexpected protocol overhead.

🔵 Enhancement Opportunities

5. Enhanced Configuration Modes (Production-Ready Design)

Current Limitation: Binary enable/disable (0/1) is too simplistic for production environments with mixed backends.

Proposed Three-Tier Configuration:

enum mode {
    DISABLED = 0,  // Current behavior: feature completely disabled
    OPTIONAL = 1,  // Graceful fallback with logging for mixed environments
    ENFORCED = 2   // Strict mode: fail if requirements cannot be met
};

Detailed Mode Behavior:

OPTIONAL Mode (1):

  • Attempts to enable session tracking on supported backends
  • Logs INFO message when backend doesn't support session tracking
  • Logs WARNING if mysql-enable_server_deprecate_eof is false (required capability)
  • Continues gracefully without session tracking on unsupported backends
  • Use Case: Gradual rollout in mixed-version environments

ENFORCED Mode (2):

  • Automatically enables CLIENT_DEPRECATE_EOF regardless of mysql-enable_server_deprecate_eof setting
  • Fails connection if session_track_system_variables or session_track_state_change cannot be set
  • Logs ERROR and drops backend connection if requirements not met
  • Use Case: Production environments requiring consistent behavior

6. Frontend Variable Propagation (Complete Implementation)

Current Gap: The implementation tracks variables internally for ProxySQL's state management but doesn't propagate changes to clients that support session tracking.

Technical Requirements:

  1. Client Capability Detection: Check if client connection has CLIENT_SESSION_TRACK flag
  2. OK Packet Enhancement: Add session tracking data to OK packets sent to supporting clients

Implementation Complexity:

  • OK packet format changes require careful protocol handling
  • Need to buffer session tracking data from backend and forward to client
  • Must handle cases where client and backend support different levels of tracking
  • session_track_system_variables or session_track_state_change themselves should become tracked variables

📋 Required Testing

7. Comprehensive CI Testing with Feature Enabled

Current State: Tests run with feature disabled (see TAP test modifications above)

Required Testing Matrix:

A. Version Compatibility with mysql-session_track_variables=1 and mysql-session_track_variables=2:

  • MySQL 5.6 (no session tracking support)
  • MySQL 8.0+ (full session tracking support)

B. Protocol-Level Testing:

  • OK packet format validation
  • Client compatibility verification
  • Error handling for unsupported backends

🎯 Recommended Implementation Order

Phase 1 - Critical Fixes (Merge Blockers)

  1. Add CLIENT_SESSION_TRACKING capability detection before attempting SET commands
  2. Add CLIENT_DEPRECATE_EOF dependency handling
  3. Fix TAP test failures - understand root causes instead of disabling feature
  4. Add comprehensive error handling for unsupported backends

Phase 2 - Compatibility Hardening

  1. Fast forward mode compatibility checks
  2. Comprehensive CI testing with feature enabled
  3. Performance baseline measurement

Phase 3 - Production Enhancements

  1. Implement 0/1/2 configuration modes
  2. Frontend variable propagation
  3. Performance optimization for high-frequency workloads
  4. Add metrics and monitoring for session tracking usage

📝 Additional Technical Implementation Details

Error Handling Strategy Example:

bool MySQL_Session::handler_again___verify_backend_session_track_variables() {
    if (mysql_thread___session_track_variables == session_track_variables::DISABLED) {
        return false;
    }

    // Check if backend supports session tracking
    if ((mybe->server_myds->myconn->mysql->server_capabilities & CLIENT_SESSION_TRACK) == 0) {
        if (mysql_thread___session_track_variables == ENFORCED) {
            proxy_error("Backend %s:%d does not support required session tracking capabilities (CLIENT_SESSION_TRACK)",
                       mybe->host, mybe->port);
            return true; // Trigger connection failure
        } else if (mysql_thread___session_track_variables == OPTIONAL) {
            proxy_info("Backend %s:%d does not support session tracking, continuing without it",
                      mybe->host, mybe->port);
            return false; // Skip session tracking setup
        }
        return false;
    }

    // Check CLIENT_DEPRECATE_EOF requirement
    if ((mybe->server_myds->myconn->mysql->server_capabilities & CLIENT_DEPRECATE_EOF) == 0) {
        if (mysql_thread___session_track_variables == ENFORCED) {
            proxy_error("Backend %s:%d does not support CLIENT_DEPRECATE_EOF required for session tracking",
                       mybe->host, mybe->port);
            return true; // Trigger connection failure
        } else if (mysql_thread___session_track_variables == OPTIONAL) {
            proxy_info("Backend %s:%d does not support CLIENT_DEPRECATE_EOF, session tracking will be limited",
                      mybe->host, mybe->port);
            // Could proceed with limited functionality or skip entirely
        }
    }

    // Proceed with session tracking setup...
}

Summary

This PR implements valuable functionality that addresses real production needs. However, the current implementation has critical gaps that could cause connection failures in production environments.

Critical Issues Summary:

  1. Protocol Capability Gap: Missing CLIENT_SESSION_TRACK detection will cause failures on older MySQL/MariaDB versions
  2. Dependency Gap: Missing CLIENT_DEPRECATE_EOF handling

Recommendation:

  • Address critical issues first before merge consideration
  • Implement enhanced configuration modes for production robustness
  • Add comprehensive testing across different MySQL versions and configurations
  • Consider Phase 2 enhancements (frontend propagation) as follow-up PRs

The architectural foundation is solid and the approach is sound - we just need to harden the implementation for production use across diverse backend environments.

@wazir-ahmed wazir-ahmed force-pushed the session-track-system-variable branch from eb5a10c to 3f1c89f Compare December 11, 2025 08:54
return ret; // exit immediately
if ((mybe->server_myds->myconn->mysql->server_capabilities & CLIENT_SESSION_TRACKING) == 0
|| (mybe->server_myds->myconn->mysql->server_capabilities & CLIENT_DEPRECATE_EOF) == 0
|| mysql_thread___enable_server_deprecate_eof == false) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable may have been changed after connections were established, thus this check is unnecessary.
Also, we would implement a logic that if session_track_variables is enabled, enable_server_deprecate_eof is ignored and CLIENT_DEPRECATE_EOF is always enabled for backend connections


// override 'mysql-enable_server_deprecate_eof' behavior if 'session_track_variables' is set to 'ENFORCED'
if (mysql_thread___session_track_variables == session_track_variables::ENFORCED) {
mysql->options.client_flag |= CLIENT_DEPRECATE_EOF;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that is what I mean in my previous comment:
"f session_track_variables is enabled, enable_server_deprecate_eof is ignored and CLIENT_DEPRECATE_EOF is always enabled for backend connections"


// override 'mysql-enable_server_deprecate_eof' behavior if 'session_track_variables' is set to 'ENFORCED'
if (mysql_thread___session_track_variables == session_track_variables::ENFORCED) {
mysql->options.client_flag |= CLIENT_DEPRECATE_EOF;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also add CLIENT_SESSION_TRACK

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CLIENT_SESSION_TRACK is set by mariadb-client, by default. It is part of CLIENT_CAPABILITIES macro.

@wazir-ahmed wazir-ahmed force-pushed the session-track-system-variable branch from 3f1c89f to 47f344f Compare December 17, 2025 08:21
wazir-ahmed and others added 5 commits December 22, 2025 12:13
- Add new mysql variable `mysql-session_track_variables`.
- Configure `session_track_system_variables` and `session_track_state_change`
  on backend connections if the mysql variable is enabled.
- Utilize notifications from backend servers to capture system variable changes
  that cannot be handled by `MySQL_Set_Stmt_Parser`
- Update both client and server variable maps based on backend responses.
- TAP test to verify this patch.

Signed-off-by: Wazir Ahmed <wazir@proxysql.com>
…acking

Add detailed architectural documentation for PR 5166 session variable tracking:

- High-level architecture overview in session_track_variables struct explaining the 3-phase workflow
- Detailed documentation for handler_rc0_Process_Variables explaining the core processing workflow
- Technical implementation details for MySQL_Connection::get_variables protocol interface
- Configuration logic documentation for handler_again___verify_backend_session_track_variables
- Added inline comments explaining why session tracking is needed and performance considerations

This documentation provides a complete understanding of how MySQL session variable tracking integrates with ProxySQL's existing state machine and leverages MySQL's native session tracking capabilities.
Signed-off-by: Wazir Ahmed <wazir@proxysql.com>
Signed-off-by: Wazir Ahmed <wazir@proxysql.com>
Signed-off-by: Wazir Ahmed <wazir@proxysql.com>
@wazir-ahmed wazir-ahmed force-pushed the session-track-system-variable branch from 415dc07 to 6e6a9bb Compare December 22, 2025 06:45
@renecannao
Copy link
Contributor

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jan 11, 2026

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link

coderabbitai bot commented Jan 11, 2026

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Adds session-variable tracking (DISABLED/OPTIONAL/ENFORCED) to ProxySQL: runtime variable and thread state, capability checks, backend session-variable extraction and propagation, server backoff for unsupported backends, new state-machine handlers and client capability negotiation tweaks, plus tests and infra additions.

Changes

Cohort / File(s) Summary
Data structures & thread state
include/MySQL_HostGroups_Manager.h, include/MySQL_Thread.h, include/proxysql_structs.h
Added MySrvC::server_backoff_time; introduced session_track_variables enum/struct and variables.session_track_variables; added SETTING_SESSION_TRACK_VARIABLES and SETTING_SESSION_TRACK_STATE to session_status; added thread-local mysql_thread___session_track_variables.
Session API & handlers
include/MySQL_Session.h, lib/MySQL_Session.cpp
Declared and implemented handlers and orchestration: backend-session verification, two new status handlers for SETTING_SESSION_TRACK_VARIABLES/STATE, handler_rc0_Process_Variables(MySQL_Connection*), and handle_session_track_capabilities(); integrated into state-machine flows.
Connection options & variable extraction
include/mysql_connection.h, lib/mysql_connection.cpp
Added connection option flags session_track_variables_sent and session_track_state_sent; added MySQL_Connection::get_variables(std::unordered_map<std::string,std::string>&) and its iteration over mysql_session_track_get_first/next; initialized/reset flags in ctor/reset.
Server selection & backoff
lib/MyHGC.cpp, lib/MySQL_Thread.cpp, lib/MySrvC.cpp
Skip servers in backoff (check server_backoff_time vs now) in random/pool selection; expose and wire runtime session_track_variables; initialize server_backoff_time.
Protocol & capability advertising
lib/MySQL_Protocol.cpp, lib/mysql_connection.cpp
Advertise CLIENT_DEPRECATE_EOF when deprecate_eof_active and (global deprecate EOF enabled OR session_track_variables == ENFORCED); ensure client flags set when ENFORCED.
Tests, infra & harness
test/tap/groups/groups.json, test/tap/groups/mysql56-single/pre-proxysql.bash, test/tap/tests/*.cpp, test/tap/tests/reg_test_4264-commit_rollback-t.cpp, test/tap/tests/test_binlog_reader-t.cpp
Added prep script, new test groups and multiple TAP C++ tests for OPTIONAL/ENFORCED and fast-forward modes; updated existing tests to set/reset session_track_variables; added admin SET/LOAD invocations in some tests.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client as Client (app)
  participant Proxy as ProxySQL
  participant HG as HostGroup Manager
  participant Backend as MySQL Server
  rect rgba(200,200,255,0.5)
    Client->>Proxy: Connect (may include CLIENT_DEPRECATE_EOF)
    Proxy->>HG: Request backend candidate
    HG-->>Proxy: Return MySrvC (skip if server_backoff_time > now)
  end
  rect rgba(200,255,200,0.5)
    Proxy->>Backend: Establish connection
    Backend-->>Proxy: Advertise capabilities (e.g., SERVER_SESSION_STATE_CHANGED)
    Proxy->>Proxy: handle_session_track_capabilities() (mode vs backend caps)
    alt ENFORCED and backend lacks caps
      Proxy->>HG: mark server_backoff_time = now + backoff
      Proxy-->>Client: try other backend / fail
    else Backend supports tracking
      Proxy->>Backend: Enable session tracking commands
      Backend-->>Proxy: SERVER_SESSION_STATE_CHANGED events
      Proxy->>Proxy: handler_rc0_Process_Variables() -> update proxy trackers
      Proxy->>Client: propagate/reflect session variable state as needed
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Poem

🐰
I hop through sockets, light and swift,
I stash a time when servers drift.
I sniff the vars and stitch the state,
Enforce or skip—I choose the gate.
A carrot cheer for sync complete!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'MySQL: Track session-specific system variables changes' directly and accurately summarizes the main functionality introduced in this PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Signed-off-by: René Cannaò <rene@proxysql.com>
@renecannao
Copy link
Contributor

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jan 12, 2026

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

🤖 Fix all issues with AI agents
In @include/MySQL_Thread.h:
- Around line 637-647: The comment for variables.session_track_variables
incorrectly references session_track_variables::ENABLED; update the docstring
for the int session_track_variables field to list the actual enum variants
(session_track_variables::DISABLED, session_track_variables::OPTIONAL,
session_track_variables::ENFORCED), explain what ENFORCED/OPTIONAL mean wrt
backend configuration tracking, and keep the default noted as
session_track_variables::DISABLED; locate the comment above the
variables.session_track_variables declaration and replace the incorrect enum
name with the correct three values and brief descriptions.

In @lib/MyHGC.cpp:
- Around line 40-44: Replace the plain unsigned long long backoff field with an
atomic and use atomic loads/stores to avoid data races: change the declaration
of server_backoff_time in MySQL_HostGroups_Manager.h to std::atomic<unsigned
long long>; in MyHGC.cpp replace direct reads of mysrvc->server_backoff_time
with mysrvc->server_backoff_time.load() for the backoff comparison (same spot as
the check around get_status() and the existing num_online_servers.load() usage);
and in MySQL_Session.cpp update assignments to use
server_backoff_time.store(new_value) (or assignment which is supported) so
writes are atomic.

In @lib/MySQL_Protocol.cpp:
- Around line 1114-1120: generate_pkt_initial_handshake advertises
CLIENT_DEPRECATE_EOF when mysql_thread___enable_client_deprecate_eof is true OR
mysql_thread___session_track_variables == session_track_variables::ENFORCED, but
PPHR_2 currently unconditionally strips CLIENT_DEPRECATE_EOF when
!mysql_thread___enable_client_deprecate_eof; update PPHR_2 so the removal of
CLIENT_DEPRECATE_EOF mirrors the advertise logic—only clear the flag if neither
mysql_thread___enable_client_deprecate_eof is true nor
mysql_thread___session_track_variables == session_track_variables::ENFORCED
(i.e., strip CLIENT_DEPRECATE_EOF only when both conditions are false) so
capability negotiation remains consistent between generate_pkt_initial_handshake
and PPHR_2.

In @lib/MySQL_Session.cpp:
- Around line 1969-1997: The flags session_track_variables_sent and
session_track_state_sent set in
handler_again___verify_backend_session_track_variables() must be reset when a
soft SQL error triggers a retry (or only set after the SET succeeds); update the
error path in handler_again___status_SETTING_GENERIC_VARIABLE to clear
mybe->server_myds->myconn->options.session_track_variables_sent and
session_track_state_sent when the handler pops back to the previous status (use
the ldap_user_variable_sent reset logic as a guide), or alternatively move
flag-setting to after the SET handlers return success so retries will re-send
the SETs.
- Around line 2798-2810: The handlers can trigger an out-of-bounds write when
handler_again___status_SETTING_GENERIC_VARIABLE() returns error 1193 because the
searched variable name (e.g., "session_track_system_variables" or
"session_track_state_change") is not in mysql_tracked_variables and idx ends up
as SQL_NAME_LAST_HIGH_WM; add a bounds check before writing to var_absent[idx]
(or adjust the search result) so you only write when idx is within [0,
SQL_NAME_LAST_HIGH_WM-1]; specifically update
handler_again___status_SETTING_GENERIC_VARIABLE() (or the two wrappers
handler_again___status_SETTING_SESSION_TRACK_VARIABLES and
handler_again___status_SETTING_SESSION_TRACK_STATE) to verify idx <
SQL_NAME_LAST_HIGH_WM (and != SQL_NAME_LAST_LOW_WM if used as sentinel) before
assigning var_absent[idx], and treat unknown-variable (1193) as a
non-fatal/no-op without touching var_absent.

In @test/tap/groups/groups.json:
- Around line 249-250: The JSON has a missing comma between two object entries
("mysql-session_track_variables_ff_enforced-t" and
"unit-strip_schema_from_query-t"), causing a parse error; add a trailing comma
after the first entry's closing bracket so the object entries are properly
separated (i.e., put a comma after the array for
"mysql-session_track_variables_ff_enforced-t").

In @test/tap/groups/mysql56-single/pre-proxysql.bash:
- Around line 46-52: The INSERT uses an undefined variable `${WHG}` inside the
for MYUSER loop which can produce an empty default_hostgroup; either define WHG
before the loop (e.g., WHG=0 or desired hostgroup) or replace `${WHG}` with the
literal value (e.g., 0) in the mysql -e INSERT statement so default_hostgroup is
explicit; update the for MYUSER ... mysql ${SSLOPT} -h127.0.0.1 -P6032 -uadmin
-padmin -e "INSERT OR IGNORE INTO mysql_users ... default_hostgroup,..."
accordingly.

In @test/tap/tests/mysql-session_track_variables_enforced-t.cpp:
- Around line 116-119: The code calls mysql_error(admin) (and likewise for
proxy) even when init_mysql_conn() returned NULL, causing undefined behavior;
update the admin and proxy error branches so you only call mysql_error(...) when
the MYSQL* is non-NULL—otherwise print a safe fallback (e.g., strerror(errno) or
a generic "failed to init connection" message) and then return exit_status();
ensure this change is applied to both the admin check and the proxy check
referencing init_mysql_conn, admin, proxy, mysql_error, and exit_status.

In @test/tap/tests/mysql-session_track_variables_ff_enforced-t.cpp:
- Around line 121-124: The check for a failed connection uses mysql_error(admin)
even when admin is NULL; avoid calling mysql_error with a NULL pointer. Update
the error branch around the admin variable so you only call mysql_error(admin)
when admin is non-NULL; if admin is NULL, print a safe fallback (e.g., a generic
"failed to initialize connection" message or errno/strerror output) instead of
passing NULL to mysql_error.

In @test/tap/tests/mysql-session_track_variables_ff_optional-t.cpp:
- Around line 121-124: The code calls mysql_error(admin) even when admin is
NULL; change it to avoid passing a NULL pointer to mysql_error by checking admin
first and using the original MYSQL handle used to initiate the connection (e.g.,
call mysql_error(&mysql) or mysql_error(mysql_init_handle) when admin is NULL),
or otherwise only call mysql_error(admin) when admin is non-NULL; update the
fprintf accordingly and keep the return exit_status() behavior.

In @test/tap/tests/mysql-session_track_variables_optional-t.cpp:
- Around line 116-119: Avoid calling mysql_error() with a NULL pointer: update
the error-handling for the `admin` (and similarly `proxy`) initialization so you
only call mysql_error(admin) when `admin` is non-NULL; if `admin` is NULL log a
clear fallback message like "mysql_init() returned NULL" (or include
strerror(errno)) instead. Locate the checks around the `admin` and `proxy`
variables in this test and replace the unconditional mysql_error(...) call with
a conditional that prints mysql_error(admin) when admin != NULL, otherwise
prints the fallback message; do the same pattern for `proxy`.
- Around line 1-6: The file header @file tag is incorrect: it says
"mysql-track_system_variables_optional-t.cpp" but the actual file is
"mysql-session_track_variables_optional-t.cpp"; update the @file comment in the
file header (the line starting with "@file") to exactly
"mysql-session_track_variables_optional-t.cpp" so the documentation and filename
match.

In @test/tap/tests/reg_test_4264-commit_rollback-t.cpp:
- Around line 1131-1136: The test disables session tracking (the MYSQL_QUERY_T
setting mysql-session_track_variables=0) which masks new connection-init SET/OK
traffic; instead re-enable session tracking and adjust reg_test_4264 to measure
or ignore init traffic by establishing the baseline counters after connection
initialization (e.g., perform the connection(s) and session-track setup, then
call a helper that reads current query/connection counters and uses those as the
starting point) or explicitly subtract the known internal setup queries from
assertions; update the places using MYSQL_QUERY_T and the LOAD MYSQL VARIABLES
TO RUNTIME step so the test asserts delta values relative to the post-init
baseline rather than absolute counts.
🧹 Nitpick comments (7)
include/MySQL_Thread.h (1)

339-381: Consider moving the long architecture block out of the header (keep the enum here, put narrative docs elsewhere).

The mode enum itself is useful, but the multi-paragraph “PR 5166 architecture” comment in include/MySQL_Thread.h is likely to drift over time and adds noise to a widely-included header. A Doxygen page or doc/ markdown would age better, with a short pointer comment here.

lib/mysql_connection.cpp (1)

3149-3187: Consider adding exception handling for JSON-like edge cases in session tracking data.

The implementation correctly follows the MySQL session tracking API pattern where get_first() returns a variable name and get_next() alternates between values and names. However, if the backend returns malformed tracking data (odd number of elements), the last variable name would be stored without its value.

While this is unlikely given MySQL's protocol guarantees, you may want to verify this is acceptable behavior or add a check.

Also, consider using std::stoi instead of relying on std::string constructor behavior for consistency with the rest of the codebase.

test/tap/tests/mysql-session_track_variables_enforced-t.cpp (2)

83-84: Consider adding exception handling for JSON parsing.

nlohmann::json::parse() can throw json::parse_error on malformed input. While PROXYSQL INTERNAL SESSION should always return valid JSON, defensive coding would catch parsing errors to provide better diagnostics.

💡 Suggested improvement
+	nlohmann::json j_session;
+	try {
+		j_session = nlohmann::json::parse(row[0]);
+	} catch (const nlohmann::json::parse_error& e) {
+		mysql_free_result(result);
+		diag("Failed to parse session JSON: %s", e.what());
+		return false;
+	}
-	auto j_session = nlohmann::json::parse(row[0]);
 	mysql_free_result(result);

18-39: Significant code duplication across test files.

The get_server_version() and test_session_variables() functions are duplicated across multiple test files (mysql-session_track_variables_enforced-t.cpp, mysql-session_track_variables_optional-t.cpp, mysql-session_track_variables_ff_enforced-t.cpp, mysql-session_track_variables_ff_optional-t.cpp).

Consider extracting these into a shared test utility header to reduce maintenance burden and ensure consistent behavior across tests.

Also applies to: 41-104

test/tap/tests/mysql-session_track_variables_ff_optional-t.cpp (1)

77-109: Same issue: test_session_variables() always returns true.

As noted in the ff_enforced test, this function unconditionally returns true. While the OPTIONAL mode test doesn't rely on the return value as heavily (it checks tracked_value == -1 directly), the inconsistent return value could cause confusion when the code is maintained.

For consistency across test files, consider returning whether tracking extraction succeeded.

lib/MySQL_Session.cpp (2)

2959-2962: Connect-flow integration is sensible, but avoid tight retry loops when mysql_thread___connect_retries_delay == 0.
On capability failure you set pause_until = curtime + delay*1000 and return; if delay is 0 and no servers qualify (e.g., all are backoff’d), this can become a hot loop.

  • Consider enforcing a small minimum sleep/yield when capability checks fail (even 1–10ms) or treating this like other “no conn” cases that rely on poll timing.

Also applies to: 2993-2998


8553-8605: Add logging when backing off a server due to missing session tracking capabilities.

The 30-second backoff silently excludes servers during host selection with no log output. While the time units are correct (30 * 1000000 microseconds), adding a one-time warning or info-level log when server_backoff_time is set would improve observability when a host group suddenly reports "no usable servers."

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9c3f6b0 and f82bcdf.

📒 Files selected for processing (19)
  • include/MySQL_HostGroups_Manager.h
  • include/MySQL_Session.h
  • include/MySQL_Thread.h
  • include/mysql_connection.h
  • include/proxysql_structs.h
  • lib/MyHGC.cpp
  • lib/MySQL_Protocol.cpp
  • lib/MySQL_Session.cpp
  • lib/MySQL_Thread.cpp
  • lib/MySrvC.cpp
  • lib/mysql_connection.cpp
  • test/tap/groups/groups.json
  • test/tap/groups/mysql56-single/pre-proxysql.bash
  • test/tap/tests/mysql-session_track_variables_enforced-t.cpp
  • test/tap/tests/mysql-session_track_variables_ff_enforced-t.cpp
  • test/tap/tests/mysql-session_track_variables_ff_optional-t.cpp
  • test/tap/tests/mysql-session_track_variables_optional-t.cpp
  • test/tap/tests/reg_test_4264-commit_rollback-t.cpp
  • test/tap/tests/test_binlog_reader-t.cpp
🧰 Additional context used
🧬 Code graph analysis (6)
test/tap/tests/mysql-session_track_variables_ff_enforced-t.cpp (3)
lib/mysql_connection.cpp (1)
  • var_name (3160-3160)
test/tap/tests/mysql-session_track_variables_enforced-t.cpp (4)
  • get_server_version (18-39)
  • get_server_version (18-18)
  • test_session_variables (41-104)
  • test_session_variables (41-41)
test/tap/tests/mysql-session_track_variables_optional-t.cpp (4)
  • get_server_version (18-39)
  • get_server_version (18-18)
  • test_session_variables (41-104)
  • test_session_variables (41-41)
test/tap/tests/mysql-session_track_variables_optional-t.cpp (2)
test/tap/tests/mysql-session_track_variables_ff_enforced-t.cpp (4)
  • get_server_version (15-36)
  • get_server_version (15-15)
  • test_session_variables (77-109)
  • test_session_variables (77-77)
test/tap/tests/mysql-session_track_variables_ff_optional-t.cpp (4)
  • get_server_version (15-36)
  • get_server_version (15-15)
  • test_session_variables (77-109)
  • test_session_variables (77-77)
test/tap/tests/mysql-session_track_variables_enforced-t.cpp (1)
test/tap/tests/mysql-session_track_variables_ff_enforced-t.cpp (4)
  • get_server_version (15-36)
  • get_server_version (15-15)
  • test_session_variables (77-109)
  • test_session_variables (77-77)
lib/mysql_connection.cpp (4)
test/tap/tests/set_testing-multi-t.cpp (1)
  • options (242-242)
include/MySQL_Monitor.hpp (1)
  • mysql (459-459)
test/tap/tests/test_session_status_flags-t.cpp (1)
  • length (253-253)
include/PgSQL_Connection.h (1)
  • string (425-433)
test/tap/tests/mysql-session_track_variables_ff_optional-t.cpp (3)
lib/mysql_connection.cpp (1)
  • var_name (3160-3160)
test/tap/tests/mysql-session_track_variables_ff_enforced-t.cpp (8)
  • current_var_name (48-48)
  • value_str (57-57)
  • extract_session_variable (38-75)
  • extract_session_variable (38-38)
  • get_server_version (15-36)
  • get_server_version (15-15)
  • test_session_variables (77-109)
  • test_session_variables (77-77)
test/tap/tests/mysql-session_track_variables_enforced-t.cpp (4)
  • get_server_version (18-39)
  • get_server_version (18-18)
  • test_session_variables (41-104)
  • test_session_variables (41-41)
include/mysql_connection.h (1)
lib/mysql_connection.cpp (2)
  • get_variables (3149-3187)
  • get_variables (3149-3149)
🪛 Clang (14.0.6)
test/tap/tests/mysql-session_track_variables_ff_enforced-t.cpp

[error] 8-8: 'stdio.h' file not found

(clang-diagnostic-error)

test/tap/tests/mysql-session_track_variables_optional-t.cpp

[error] 8-8: 'stdio.h' file not found

(clang-diagnostic-error)

test/tap/tests/mysql-session_track_variables_enforced-t.cpp

[error] 8-8: 'stdio.h' file not found

(clang-diagnostic-error)

test/tap/tests/mysql-session_track_variables_ff_optional-t.cpp

[error] 8-8: 'stdio.h' file not found

(clang-diagnostic-error)

🪛 Shellcheck (0.11.0)
test/tap/groups/mysql56-single/pre-proxysql.bash

[warning] 39-39: ShellCheck can't follow non-constant source. Use a directive to specify location.

(SC1090)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (100)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mysql9.1,mariadb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb10.6,mariadb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,asyncmy)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.5,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.4,mysqldb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb11.5,mysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.11,mysqldb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.5,pymysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.2,asyncmy)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mysql9.1,mariadb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb10.6,mariadb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,asyncmy)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.5,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.4,mysqldb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb11.5,mysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.11,mysqldb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.5,pymysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.2,asyncmy)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mysql9.1,mariadb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb10.6,mariadb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,asyncmy)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.5,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.4,mysqldb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb11.5,mysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.11,mysqldb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.5,pymysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.2,asyncmy)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mysql9.1,mariadb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb10.6,mariadb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,asyncmy)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.5,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.4,mysqldb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb11.5,mysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.11,mysqldb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.5,pymysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.2,asyncmy)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mysql9.1,mariadb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb10.6,mariadb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,asyncmy)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.5,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.4,mysqldb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb11.5,mysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.11,mysqldb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.5,pymysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.2,asyncmy)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mysql9.1,mariadb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb10.6,mariadb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,asyncmy)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.5,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.4,mysqldb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb11.5,mysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.11,mysqldb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.5,pymysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.2,asyncmy)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mysql9.1,mariadb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb10.6,mariadb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,asyncmy)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.5,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.4,mysqldb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb11.5,mysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.11,mysqldb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.5,pymysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.2,asyncmy)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mysql9.1,mariadb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb10.6,mariadb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,asyncmy)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.5,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.4,mysqldb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb11.5,mysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.11,mysqldb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.5,pymysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.2,asyncmy)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mysql9.1,mariadb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb10.6,mariadb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,asyncmy)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.5,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.4,mysqldb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb11.5,mysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.11,mysqldb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.5,pymysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.2,asyncmy)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mysql9.1,mariadb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb10.6,mariadb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,asyncmy)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.5,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.6,mariadbconnector)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.4,mysqldb)
  • GitHub Check: CI-3p-php-pdo-mysql / test (mariadb11.5,mysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.11,mysqldb)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb10.5,pymysql)
  • GitHub Check: CI-3p-sqlalchemy / test (mariadb11.2,asyncmy)
🔇 Additional comments (23)
test/tap/tests/test_binlog_reader-t.cpp (1)

262-264: Investigate root cause instead of disabling session tracking.

Disabling session_track_variables in this test masks potential compatibility issues rather than addressing them. Per the PR reviewer's feedback, the root cause of why binlog reader tests fail with session tracking enabled should be investigated.

If session tracking genuinely conflicts with binlog reader functionality, document this as an expected limitation. Otherwise, fix the underlying issue to ensure the test validates the feature properly.

include/mysql_connection.h (2)

91-93: Well-structured tracking flags for session configuration state.

The new session_track_variables_sent and session_track_state_sent flags provide clear tracking of whether session tracking configuration has been sent to backend connections, following the same pattern as the existing session_track_gtids_sent flag.


267-296: Comprehensive documentation for the session variable extraction interface.

The Doxygen documentation clearly explains the protocol flow, technical details, and integration points for the get_variables method. The implementation (per the relevant code snippet) correctly handles the variable/value pairing protocol from MySQL's session tracking system.

include/proxysql_structs.h (2)

314-316: New session states properly extend the state machine.

The SETTING_SESSION_TRACK_VARIABLES and SETTING_SESSION_TRACK_STATE states follow the existing pattern for session configuration states (similar to SETTING_SESSION_TRACK_GTIDS) and are correctly positioned before the session_status___NONE marker.


1310-1310: Thread-local session tracking variable properly defined with existing enum-based documentation.

The mysql_thread___session_track_variables thread-local variable is correctly defined alongside other MySQL thread variables and follows established conventions. The valid values are already documented via the session_track_variables enum (DISABLED=0, OPTIONAL=1, ENFORCED=2), which is actively used throughout the codebase to avoid magic numbers, including in MySQL_Session.cpp, MySQL_Protocol.cpp, and mysql_connection.cpp.

lib/MySrvC.cpp (1)

34-34: Proper initialization of backoff timestamp.

The server_backoff_time member is correctly initialized to zero, indicating no active backoff. This follows the same initialization pattern as other time-related members in the constructor.

lib/mysql_connection.cpp (3)

455-456: LGTM!

The new session tracking flags are properly initialized to false in the constructor, consistent with the existing session_track_gtids_sent pattern at line 454.


892-902: LGTM! Good defensive override for ENFORCED mode.

When session_track_variables is set to ENFORCED, forcing CLIENT_DEPRECATE_EOF is correct since session tracking data is transmitted in OK packets (which replace EOF packets when this capability is enabled). This ensures the feature works regardless of mysql-enable_server_deprecate_eof setting.


3113-3114: LGTM!

The reset of tracking flags in reset() is consistent with the pattern used for session_track_gtids_sent at line 3112 and ensures clean state for connection reuse.

test/tap/groups/mysql56-single/pre-proxysql.bash (1)

38-39: Non-constant source path - expected for dynamic environment loading.

The shellcheck warning (SC1090) about non-constant source is a known limitation when sourcing environment-specific files. This pattern is common in test infrastructure scripts and is acceptable here since JENKINS_SCRIPTS_PATH is expected to be set by the CI environment.

include/MySQL_Session.h (3)

244-273: Excellent documentation for the session tracking orchestration.

The doxygen block clearly explains the purpose, configuration logic, state machine integration, and design rationale. This level of documentation will be valuable for future maintainers understanding the session tracking flow.


351-379: Documentation note: Return type inconsistency.

The doxygen documentation describes the function but doesn't specify a return value, yet line 379 declares void handler_rc0_Process_Variables(MySQL_Connection *myconn) which is consistent with a procedure that updates state without returning a status.

However, I notice the AI summary mentions bool handler_rc0_Process_Variables(MySQL_Connection *myconn) - please verify the actual return type matches the declaration.


573-573: LGTM!

The handle_session_track_capabilities() declaration is appropriately placed in the public section and follows the naming convention of other capability-checking methods.

test/tap/tests/mysql-session_track_variables_ff_optional-t.cpp (1)

166-175: LGTM! Test assertions are appropriate for OPTIONAL mode.

The test correctly verifies:

  • MySQL 5.7+: Session tracking works (set_value == tracked_value)
  • MySQL 5.6 and below: Tracking gracefully disabled (tracked_value == -1)

This aligns with the expected OPTIONAL mode behavior where the feature is used when available but doesn't fail on unsupported servers.

lib/MySQL_Thread.cpp (5)

519-521: LGTM!

The new variable name is correctly added to the array before the NULL terminator, following the established pattern.


1184-1185: LGTM!

Safe default initialization with DISABLED ensures session tracking is off until explicitly enabled by the administrator.


4405-4406: LGTM!

Standard refresh pattern correctly propagates the global configuration value to the thread-local variable.


4744-4748: Server backoff check correctly placed.

The backoff check efficiently filters out connections from servers in backoff before performing more expensive validation. The server_backoff_time is only set when session_track_variables::ENFORCED mode is active and the server lacks session tracking capabilities, so the check does not inadvertently exclude servers in other scenarios.


2367-2368: LGTM!

Variable registration follows the established pattern. Bounds 0-2 correctly correspond to the session_track_variables::DISABLED (0), OPTIONAL (1), and ENFORCED (2) modes as defined in include/MySQL_Thread.h.

lib/MySQL_Session.cpp (4)

5197-5199: LGTM: verification chain integration is in the right place.
It’s correctly placed before the general variable verification so the tracking knobs are enabled early.


5281-5282: LGTM: post-query variable processing hook is correctly sequenced.
Running after GTID processing and before result wiring makes sense.


1918-1922: Capability gating for session_track_gtids is correct.

The code properly requires both CLIENT_SESSION_TRACKING and CLIENT_DEPRECATE_EOF capabilities from the backend server, plus the global mysql_thread___enable_server_deprecate_eof flag before enabling GTID tracking. The constant names are correct and used consistently throughout the codebase.


4965-4998: character_set_server is not in tracked variables; charset normalization coverage is complete.

The code correctly normalizes only the tracked charset variables: character_set_{results,connection,client,database} and collation_connection. character_set_server is not part of mysql_tracked_variables[] (confirmed in enum at include/proxysql_structs.h:189+), so it is never passed to this handler by get_variables(). The std::unordered_map allocation is already guarded by the if(myconn->get_variables(var_map)) early exit and is a reasonable per-connection cost for session variable tracking.

Comment on lines +231 to +238
// 'server_backoff_time' stores a timestamp that prevents the server from being
// considered for random selection ('MyHGC::get_random_MySrvC') until that time passes.
//
// This is primarily used when `session_track_variables::ENFORCED` mode is active.
// If a server lacks the required capabilities in this mode, it is temporarily
// excluded from selection for a specified duration.
unsigned long long server_backoff_time;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

rg -n "server_backoff_time" .

Repository: sysown/proxysql

Length of output: 704


🏁 Script executed:

rg -n "curtime|atomic_curtime" include lib | head -n 100

Repository: sysown/proxysql

Length of output: 10049


🏁 Script executed:

# Check the MySrvC class definition and surrounding code
fd "MySQL_HostGroups_Manager.h" --type f

Repository: sysown/proxysql

Length of output: 93


🏁 Script executed:

# Check context around the write in MySQL_Session.cpp
sed -n '8580,8605p' lib/MySQL_Session.cpp

Repository: sysown/proxysql

Length of output: 1180


🏁 Script executed:

# Check context around the read in MyHGC.cpp
sed -n '35,50p' lib/MyHGC.cpp

Repository: sysown/proxysql

Length of output: 985


🏁 Script executed:

# Check context around the read in MySQL_Thread.cpp
sed -n '5740,5755p' lib/MySQL_Thread.cpp

Repository: sysown/proxysql

Length of output: 886


🏁 Script executed:

# Check if MySrvC or parent access is protected by locks in the surrounding code
sed -n '8570,8600p' lib/MySQL_Session.cpp | head -40

Repository: sysown/proxysql

Length of output: 1413


🏁 Script executed:

# Look for mutex/lock patterns in the function containing the write
rg -B 30 "server_backoff_time = thread->curtime" lib/MySQL_Session.cpp | head -60

Repository: sysown/proxysql

Length of output: 1411


Make server_backoff_time thread-safe (atomic or lock-protected).

server_backoff_time is written without synchronization in MySQL_Session::handle_session_track_capabilities() (line 8595) and read without locking in MyHGC::get_random_MySrvC() (line 42) and MySQL_Thread.cpp (line 5746) from different threads. This creates a data race that could cause torn reads on some platforms.

The timebase is microseconds (confirmed by the write: thread->curtime + (30 * 1000000) for 30 seconds), which is consistent with curtime. The comment in the code adequately documents this, but the field itself needs synchronization.

Proposed fix
-	unsigned long long server_backoff_time;
+	std::atomic<unsigned long long> server_backoff_time { 0 };

Comment on lines +637 to 647
/**
* @brief Configuration flag to enable/disable session variable tracking.
*
* When set to session_track_variables::ENABLED (1), ProxySQL will configure
* backend connections to track system variable changes using MySQL's
* session_track_system_variables and session_track_state_change capabilities.
*
* Default: session_track_variables::DISABLED (0)
*/
int session_track_variables;
} variables;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix docs: session_track_variables::ENABLED doesn’t exist (values are DISABLED/OPTIONAL/ENFORCED).

The comment on variables.session_track_variables (Line 640) references a non-existent enum value; this will confuse operators and reviewers.

Proposed fix
-		 * When set to session_track_variables::ENABLED (1), ProxySQL will configure
+		 * When set to session_track_variables::OPTIONAL (1) or
+		 * session_track_variables::ENFORCED (2), ProxySQL will configure
 		 * backend connections to track system variable changes using MySQL's
 		 * session_track_system_variables and session_track_state_change capabilities.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/**
* @brief Configuration flag to enable/disable session variable tracking.
*
* When set to session_track_variables::ENABLED (1), ProxySQL will configure
* backend connections to track system variable changes using MySQL's
* session_track_system_variables and session_track_state_change capabilities.
*
* Default: session_track_variables::DISABLED (0)
*/
int session_track_variables;
} variables;
/**
* @brief Configuration flag to enable/disable session variable tracking.
*
* When set to session_track_variables::OPTIONAL (1) or
* session_track_variables::ENFORCED (2), ProxySQL will configure
* backend connections to track system variable changes using MySQL's
* session_track_system_variables and session_track_state_change capabilities.
*
* Default: session_track_variables::DISABLED (0)
*/
int session_track_variables;
} variables;
🤖 Prompt for AI Agents
In @include/MySQL_Thread.h around lines 637 - 647, The comment for
variables.session_track_variables incorrectly references
session_track_variables::ENABLED; update the docstring for the int
session_track_variables field to list the actual enum variants
(session_track_variables::DISABLED, session_track_variables::OPTIONAL,
session_track_variables::ENFORCED), explain what ENFORCED/OPTIONAL mean wrt
backend configuration tracking, and keep the default noted as
session_track_variables::DISABLED; locate the comment above the
variables.session_track_variables declaration and replace the incorrect enum
name with the correct three values and brief descriptions.

Comment on lines 40 to +44
if (mysrvc->get_status() == MYSQL_SERVER_STATUS_ONLINE) { // consider this server only if ONLINE
// skip servers that are in backoff period
if (mysrvc->server_backoff_time > sess->thread->curtime)
continue;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

git ls-files | head -20

Repository: sysown/proxysql

Length of output: 799


🏁 Script executed:

find . -name "*.h" -o -name "*.hpp" | grep -i "mysql\|hgc" | head -20

Repository: sysown/proxysql

Length of output: 612


🏁 Script executed:

ls -la lib/MyHGC.cpp 2>/dev/null && echo "File exists" || echo "Checking structure..."

Repository: sysown/proxysql

Length of output: 134


🏁 Script executed:

# Get codebase structure
fd --type f --name "*.cpp" --name "*.h" | grep -E "(MyHGC|mysql)" | head -30

Repository: sysown/proxysql

Length of output: 309


🏁 Script executed:

cat -n lib/MyHGC.cpp | head -60

Repository: sysown/proxysql

Length of output: 2925


🏁 Script executed:

# Search for server_backoff_time definition
rg "server_backoff_time" --type-list | head -5
rg "server_backoff_time" -B 2 -A 2 | head -100

Repository: sysown/proxysql

Length of output: 2462


🏁 Script executed:

# Find all writes to server_backoff_time
rg "server_backoff_time\s*=" -B 3 -A 3

Repository: sysown/proxysql

Length of output: 859


🏁 Script executed:

# Find the header definition with context
rg -A 5 -B 5 "unsigned long long server_backoff_time"

Repository: sysown/proxysql

Length of output: 919


🏁 Script executed:

# Check for any mutex/synchronization related to MySrvC
rg "MySrvC" include/MySQL_HostGroups_Manager.h | head -20

Repository: sysown/proxysql

Length of output: 915


🏁 Script executed:

# Search for mutex or lock context around MySrvC or server_backoff_time
rg -B 10 "server_backoff_time = " lib/MySQL_Session.cpp | head -40

Repository: sysown/proxysql

Length of output: 792


🏁 Script executed:

# Look for threading/locks in relation to MySrvC
rg "pthread_mutex\|lock\|rwlock" include/MySQL_HostGroups_Manager.h -A 2 -B 2

Repository: sysown/proxysql

Length of output: 41


🏁 Script executed:

# Check MyHGC context - who calls get_random_MySrvC
rg "get_random_MySrvC" -B 5 | head -50

Repository: sysown/proxysql

Length of output: 3145


🏁 Script executed:

# Check if there's a lock protecting MySrvC or mysrvs
rg -B 5 -A 5 "class MySrvC" include/MySQL_HostGroups_Manager.h | head -80

Repository: sysown/proxysql

Length of output: 800


🏁 Script executed:

# Look for locks in the hostgroup management
rg "pthread_rwlock\|pthread_mutex\|rwlock_write\|wrlock\|rdlock" include/MySQL_HostGroups_Manager.h | head -20

Repository: sysown/proxysql

Length of output: 41


🏁 Script executed:

# Check the broader context of MySQL_Session write
rg -B 20 "server_backoff_time = thread->curtime" lib/MySQL_Session.cpp | head -60

Repository: sysown/proxysql

Length of output: 1115


🏁 Script executed:

# Check if there are thread contexts for writes vs reads
rg "MySQL_Session\|MySQL_Thread" lib/MyHGC.cpp | head -20

Repository: sysown/proxysql

Length of output: 41


🏁 Script executed:

# Check BaseHGC for any locking mechanism
rg "class BaseHGC" -A 30 include/Base_HostGroups_Manager.h | head -60

Repository: sysown/proxysql

Length of output: 1817


🏁 Script executed:

# Look for rwlock or mutex protecting server lists
rg "rwlock\|pthread_mutex" include/MySQL_HostGroups_Manager.h -B 5 -A 5

Repository: sysown/proxysql

Length of output: 41


🏁 Script executed:

# Check MySQL_Thread structure and curtime
rg "class MySQL_Thread" -A 50 include/MySQL_Thread.h | head -80

Repository: sysown/proxysql

Length of output: 1908


🏁 Script executed:

# Check if there's synchronization around get_random_MySrvC calls
rg -B 10 "myhgc->get_random_MySrvC" lib/Base_HostGroups_Manager.cpp | head -50

Repository: sysown/proxysql

Length of output: 456


🏁 Script executed:

# Check MySrvList structure for any locks
rg "class MySrvList\|class BaseSrvList" -A 40 include/MySQL_HostGroups_Manager.h | head -80

Repository: sysown/proxysql

Length of output: 41


🏁 Script executed:

# Examine the actual threading - is get_random_MySrvC protected by any lock?
rg "MyHGM\|myhgc\|get_random_MySrvC" lib/MySQL_Session.cpp -B 3 -A 3 | head -80

Repository: sysown/proxysql

Length of output: 41


🏁 Script executed:

# Search more broadly for MySrvList
fd "MySQL_HostGroups_Manager" --type f

Repository: sysown/proxysql

Length of output: 126


🏁 Script executed:

# Try to find MySrvList definition
rg "class MySrvList" -A 50

Repository: sysown/proxysql

Length of output: 26083


🏁 Script executed:

# Check if there's a global lock protecting MyHGM or MyHGC access
rg "MyHGM\|MyHGC_lookup" include/*.h -B 2 -A 2 | head -100

Repository: sysown/proxysql

Length of output: 41


🏁 Script executed:

# Look for synchronization in get_random_MySrvC itself
cat -n lib/MyHGC.cpp | sed -n '14,120p'

Repository: sysown/proxysql

Length of output: 6052


🏁 Script executed:

# Final check: confirm multiple threads can access same MySrvC by searching for thread pool/worker threads
rg "worker.*thread\|pthread_create" include/MySQL_Thread.h -A 3 -B 3 | head -40

Repository: sysown/proxysql

Length of output: 41


Use atomic load to prevent data races in backoff check.

server_backoff_time is a plain unsigned long long that can be written by one thread (MySQL_Session.cpp) and read by another (MyHGC.cpp line 42) without synchronization. This creates a data race. The codebase already uses atomics elsewhere (e.g., num_online_servers.load() on line 45); apply the same pattern here:

Proposed fix
-				if (mysrvc->server_backoff_time > sess->thread->curtime)
+				if (mysrvc->server_backoff_time.load(std::memory_order_relaxed) > sess->thread->curtime)
 					continue;

Also update the field declaration in include/MySQL_HostGroups_Manager.h from unsigned long long server_backoff_time; to std::atomic<unsigned long long> server_backoff_time; and ensure the assignment in MySQL_Session.cpp uses .store() or assignment (which works with atomics).

🤖 Prompt for AI Agents
In @lib/MyHGC.cpp around lines 40 - 44, Replace the plain unsigned long long
backoff field with an atomic and use atomic loads/stores to avoid data races:
change the declaration of server_backoff_time in MySQL_HostGroups_Manager.h to
std::atomic<unsigned long long>; in MyHGC.cpp replace direct reads of
mysrvc->server_backoff_time with mysrvc->server_backoff_time.load() for the
backoff comparison (same spot as the check around get_status() and the existing
num_online_servers.load() usage); and in MySQL_Session.cpp update assignments to
use server_backoff_time.store(new_value) (or assignment which is supported) so
writes are atomic.

Comment on lines +1114 to +1120
if (deprecate_eof_active) {
if (mysql_thread___enable_client_deprecate_eof
|| mysql_thread___session_track_variables == session_track_variables::ENFORCED) {
extended_capabilities |= CLIENT_DEPRECATE_EOF;
}
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "MySQL_Protocol.cpp" -type f

Repository: sysown/proxysql

Length of output: 83


🏁 Script executed:

# Get context around the lines mentioned in the review
grep -n "deprecate_eof_active" . -r --include="*.cpp" | head -20

Repository: sysown/proxysql

Length of output: 2205


🏁 Script executed:

# Search for PPHR_2 and the strip logic mentioned
grep -n "PPHR_2" . -r --include="*.cpp" | head -10

Repository: sysown/proxysql

Length of output: 309


🏁 Script executed:

# Find the full function context around line 1114-1120
sed -n '1100,1130p' lib/MySQL_Protocol.cpp

Repository: sysown/proxysql

Length of output: 1899


🏁 Script executed:

# Search for the CLIENT_DEPRECATE_EOF strip logic mentioned
grep -n "CLIENT_DEPRECATE_EOF" lib/MySQL_Protocol.cpp

Repository: sysown/proxysql

Length of output: 967


🌐 Web query:

MySQL CLIENT_DEPRECATE_EOF capability flag semantics protocol specification

💡 Result:

Summary of CLIENT_DEPRECATE_EOF semantics (protocol-level)

  • What it is: a client capability bit (CLIENT_DEPRECATE_EOF = 1 << 24 = 16777216) advertised in the Handshake Response so the server will use OK_Packet instead of EOF_Packet to terminate text resultsets. [1][2]

  • Why it exists: EOF_Packet is not extensible (overlaps with resultset row bytes); OK_Packet is extensible (needed for session-state / CLIENT_SESSION_TRACK). EOF was deprecated in MySQL 5.7.5 for this reason. [2][3]

  • Effect on Text Resultset framing:

    • If CLIENT_DEPRECATE_EOF is NOT set: column defs and rows are terminated by EOF_Packet.
    • If CLIENT_DEPRECATE_EOF is set: column defs and rows are terminated by OK_Packet (OK replaces EOF as the terminator). [4]
  • How to distinguish OK vs EOF bytes (backwards compatibility): OK and EOF share header values; the rules used are:

    • OK: header = 0x00 (or 0xFE) and packet length > 7 → OK.
    • EOF: header = 0xFE and packet length < 9 → EOF.
      New clients advertise CLIENT_DEPRECATE_EOF so new servers may send OK-as-terminator; old servers (that don’t know the flag) won’t. [2]
  • Interaction with session tracking and server-status:

    • Using OK as terminator allows the server to include session-state and other status fields (SERVER_SESSION_STATE_CHANGED, info, session-state-info) in the terminator packet; EOF could not carry that reliably. [2][3][4]
  • Implementation notes / caveats:

    • The capability is part of the 32-bit capability mask; both sides must agree (server may choose behavior based on presence). [1]
    • Some client/connector behavior (e.g., code that inspects server_status immediately after a resultset EOF) changed because status flags and "more results" indicators move into the terminating OK; this has caused connector issues in practice. See related bug/discussion. [5]

Sources

  • MySQL server protocol capability flags (CLIENT_DEPRECATE_EOF description). [1]
  • OK_Packet (OK vs EOF rules; CLIENT_DEPRECATE_EOF background). [2]
  • EOF_Packet (deprecation note). [3]
  • Text Resultset framing (when EOF vs OK used). [4]
  • MySQL bug / discussion about connector issues with DEPRECATE_EOF. [5]

🌐 Web query:

MySQL session tracking ENFORCED mode CLIENT_DEPRECATE_EOF handling

💡 Result:

Do you mean the server/session-tracking "ENFORCED" SQL mode or something else? (I ask because I can’t find an authoritative MySQL doc that uses the exact phrase "session tracking ENFORCED mode".)

Meanwhile: brief summary of how CLIENT_DEPRECATE_EOF interacts with session-state tracking

  • New clients advertise CLIENT_DEPRECATE_EOF to tell the server they accept OK packets (not EOF packets) as end-of-result markers; when that flag is present, modern servers use OK packets to carry session-state / tracker info at result end. [1][2].
  • Session-state tracking (session_track_) data is sent in OK packets (when session tracking is enabled), so a client that uses CLIENT_DEPRECATE_EOF will receive session-state notifications inside OK packets rather than in legacy EOF packets. Enable/configure trackers with the session_track_ variables (e.g., session_track_state_change, session_track_system_variables, session_track_schema, session_track_gtids). [3][4].
  • Caveat / known issue: changing from EOF to OK-as-EOF affects where status flags (SERVER_MORE_RESULTS_EXIST, SERVER_PS_OUT_PARAMS, etc.) and intermediate EOF semantics appear; some connectors/clients experienced behavioral issues when CLIENT_DEPRECATE_EOF is set because formerly-available EOF-based signals are moved into the final OK packet. See MySQL bug reports/discussions. [2][5].

Sources

  1. MySQL OK_Packet documentation (explains OK used to indicate EOF and CLIENT_DEPRECATE_EOF flag). [dev.mysql.com]. [1]
  2. MySQL EOF_Packet / protocol notes. [dev.mysql.com]. [2]
  3. MySQL Session-State Tracking overview and session_track_* variables. [dev.mysql.com / Oracle MySQL manual]. [3][4]
  4. MySQL bug report re: CLIENT_DEPRECATE_EOF and connector behavior. [bugs.mysql.com]. [5]

If you want, I can: (a) show the exact packet layout examples, (b) point to where connectors (e.g., libmysqlclient, Connector/J, Connector/C) handle CLIENT_DEPRECATE_EOF, or (c) help debug a specific client/connector problem you’re seeing.


🏁 Script executed:

sed -n '1595,1615p' lib/MySQL_Protocol.cpp

Repository: sysown/proxysql

Length of output: 1225


🏁 Script executed:

# Get more context to understand the full condition
sed -n '1580,1620p' lib/MySQL_Protocol.cpp

Repository: sysown/proxysql

Length of output: 2270


ENFORCED mode advertises CLIENT_DEPRECATE_EOF, but PPHR_2 still strips it when mysql_thread___enable_client_deprecate_eof is false — breaks protocol negotiation.

In generate_pkt_initial_handshake(), you advertise CLIENT_DEPRECATE_EOF when either mysql_thread___enable_client_deprecate_eof is enabled OR mysql_thread___session_track_variables == ENFORCED (lines 1116–1117). However, in PPHR_2(), the flag is unconditionally stripped if !mysql_thread___enable_client_deprecate_eof (line 1606), without checking for ENFORCED mode.

This causes a protocol violation: the handshake advertises support for OK packets (not EOF packets) as result terminators, but the client handshake response processing removes that flag regardless. Since session-state tracking (ENFORCED mode) requires OK packets to carry state data, this inconsistency breaks the negotiated capability set.

The strip logic in PPHR_2 should mirror the advertise logic:

Proposed fix (in PPHR_2, around line 1606)
-	if (!mysql_thread___enable_client_deprecate_eof) {
+	if (
+		!mysql_thread___enable_client_deprecate_eof
+		&& mysql_thread___session_track_variables != session_track_variables::ENFORCED
+	) {
 		vars1.capabilities &= ~CLIENT_DEPRECATE_EOF;
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (deprecate_eof_active) {
if (mysql_thread___enable_client_deprecate_eof
|| mysql_thread___session_track_variables == session_track_variables::ENFORCED) {
extended_capabilities |= CLIENT_DEPRECATE_EOF;
}
}
if (
!mysql_thread___enable_client_deprecate_eof
&& mysql_thread___session_track_variables != session_track_variables::ENFORCED
) {
vars1.capabilities &= ~CLIENT_DEPRECATE_EOF;
}
🤖 Prompt for AI Agents
In @lib/MySQL_Protocol.cpp around lines 1114 - 1120,
generate_pkt_initial_handshake advertises CLIENT_DEPRECATE_EOF when
mysql_thread___enable_client_deprecate_eof is true OR
mysql_thread___session_track_variables == session_track_variables::ENFORCED, but
PPHR_2 currently unconditionally strips CLIENT_DEPRECATE_EOF when
!mysql_thread___enable_client_deprecate_eof; update PPHR_2 so the removal of
CLIENT_DEPRECATE_EOF mirrors the advertise logic—only clear the flag if neither
mysql_thread___enable_client_deprecate_eof is true nor
mysql_thread___session_track_variables == session_track_variables::ENFORCED
(i.e., strip CLIENT_DEPRECATE_EOF only when both conditions are false) so
capability negotiation remains consistent between generate_pkt_initial_handshake
and PPHR_2.

Comment on lines +1969 to +1997
bool MySQL_Session::handler_again___verify_backend_session_track_variables() {
int mode = mysql_thread___session_track_variables;

// skip enabling session variable tracking in the following cases
if (mode == session_track_variables::DISABLED) {
return false;
}
if ((mybe->server_myds->myconn->mysql->server_capabilities & CLIENT_SESSION_TRACKING) == 0
|| (mybe->server_myds->myconn->mysql->server_capabilities & CLIENT_DEPRECATE_EOF) == 0) {
return false;
}
if (!mysql_thread___enable_server_deprecate_eof && mode != session_track_variables::ENFORCED) {
return false;
}

// enable session tracking
if (mybe->server_myds->myconn->options.session_track_variables_sent == false) {
mybe->server_myds->myconn->options.session_track_variables_sent = true;
set_previous_status_mode3();
NEXT_IMMEDIATE_NEW(SETTING_SESSION_TRACK_VARIABLES);
}
if (mybe->server_myds->myconn->options.session_track_state_sent == false) {
mybe->server_myds->myconn->options.session_track_state_sent = true;
set_previous_status_mode3();
NEXT_IMMEDIATE_NEW(SETTING_SESSION_TRACK_STATE);
}

return false;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's search for all *_sent flag patterns in the relevant files
rg -n "options\.\w+_sent\s*=" lib/MySQL_Session.cpp lib/mysql_connection.cpp include/mysql_connection.h 2>/dev/null | head -50

Repository: sysown/proxysql

Length of output: 1664


🏁 Script executed:

# Look at the ldap_user_variable_sent handling which has a reset pattern
sed -n '2065,2095p' lib/MySQL_Session.cpp | cat -n

Repository: sysown/proxysql

Length of output: 1616


🏁 Script executed:

# Look at the context around the flag resets in mysql_connection.cpp
sed -n '3090,3120p' lib/mysql_connection.cpp | cat -n

Repository: sysown/proxysql

Length of output: 1191


🏁 Script executed:

# Find the function containing the flag reset code
sed -n '3050,3095p' lib/mysql_connection.cpp | cat -n

Repository: sysown/proxysql

Length of output: 1749


🏁 Script executed:

# Find calls to reset() function
rg -n "\.reset\(\)" lib/MySQL_Session.cpp lib/mysql_connection.cpp | head -30

Repository: sysown/proxysql

Length of output: 41


🏁 Script executed:

# Search for reset() with different patterns
rg -n "reset\(\)" lib/MySQL_Session.cpp lib/mysql_connection.cpp | head -30

Repository: sysown/proxysql

Length of output: 494


🏁 Script executed:

# Check context around line 1668 and 3170 - likely error handling
echo "=== Context around line 1668 ===" && sed -n '1660,1675p' lib/MySQL_Session.cpp | cat -n && \
echo "=== Context around line 3170 ===" && sed -n '3160,3175p' lib/MySQL_Session.cpp | cat -n

Repository: sysown/proxysql

Length of output: 1756


🏁 Script executed:

# Look for error handling in the handler functions - search for error conditions after NEXT_IMMEDIATE_NEW
sed -n '1969,2000p' lib/MySQL_Session.cpp | cat -n

Repository: sysown/proxysql

Length of output: 1534


🏁 Script executed:

# Search for the handler function that processes SETTING_SESSION_TRACK_VARIABLES
rg -n "handler_again___status_SETTING_SESSION_TRACK_VARIABLES|SETTING_SESSION_TRACK_VARIABLES" lib/MySQL_Session.cpp | head -20

Repository: sysown/proxysql

Length of output: 329


🏁 Script executed:

# View the handler function
sed -n '2798,2850p' lib/MySQL_Session.cpp | cat -n

Repository: sysown/proxysql

Length of output: 2545


🏁 Script executed:

# Find the handler_again___status_SETTING_GENERIC_VARIABLE function
rg -n "bool MySQL_Session::handler_again___status_SETTING_GENERIC_VARIABLE" lib/MySQL_Session.cpp

Repository: sysown/proxysql

Length of output: 225


🏁 Script executed:

# View the handler_again___status_SETTING_GENERIC_VARIABLE function
sed -n '2539,2650p' lib/MySQL_Session.cpp | cat -n

Repository: sysown/proxysql

Length of output: 5367


🏁 Script executed:

# Continue viewing the error handling
sed -n '2650,2700p' lib/MySQL_Session.cpp | cat -n

Repository: sysown/proxysql

Length of output: 2431


Session track variable flags should be reset on soft SQL errors to allow retry.

The session_track_variables_sent and session_track_state_sent flags are set to true before the SET commands execute. In the error handler (handler_again___status_SETTING_GENERIC_VARIABLE), soft SQL errors (like 1193 - variable not supported) allow retry by popping back to the previous status, but these flags are never reset. This means the next time handler_again___verify_backend_session_track_variables() is called, the SET command is skipped because the flag check if (session_track_variables_sent == false) will be false, preventing retry.

The ldap_user_variable_sent flag in the same codebase demonstrates that flags can be reset on specific conditions (line 2082), showing this pattern is feasible. Consider resetting the *_sent flags when soft SQL errors occur and retry is triggered, or alternatively reset them only after the handler returns success.

🤖 Prompt for AI Agents
In @lib/MySQL_Session.cpp around lines 1969 - 1997, The flags
session_track_variables_sent and session_track_state_sent set in
handler_again___verify_backend_session_track_variables() must be reset when a
soft SQL error triggers a retry (or only set after the SET succeeds); update the
error path in handler_again___status_SETTING_GENERIC_VARIABLE to clear
mybe->server_myds->myconn->options.session_track_variables_sent and
session_track_state_sent when the handler pops back to the previous status (use
the ldap_user_variable_sent reset logic as a guide), or alternatively move
flag-setting to after the SET handlers return success so retries will re-send
the SETs.

Comment on lines +121 to +124
if (!admin) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin));
return exit_status();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Same NULL pointer issue with mysql_error() on failed connection.

As noted in other test files, calling mysql_error(admin) when admin is NULL is undefined behavior.

🤖 Prompt for AI Agents
In @test/tap/tests/mysql-session_track_variables_ff_enforced-t.cpp around lines
121 - 124, The check for a failed connection uses mysql_error(admin) even when
admin is NULL; avoid calling mysql_error with a NULL pointer. Update the error
branch around the admin variable so you only call mysql_error(admin) when admin
is non-NULL; if admin is NULL, print a safe fallback (e.g., a generic "failed to
initialize connection" message or errno/strerror output) instead of passing NULL
to mysql_error.

Comment on lines +121 to +124
if (!admin) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin));
return exit_status();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Same NULL pointer issue with mysql_error() on failed connection.

As noted in other test files, calling mysql_error(admin) when admin is NULL is undefined behavior.

🤖 Prompt for AI Agents
In @test/tap/tests/mysql-session_track_variables_ff_optional-t.cpp around lines
121 - 124, The code calls mysql_error(admin) even when admin is NULL; change it
to avoid passing a NULL pointer to mysql_error by checking admin first and using
the original MYSQL handle used to initiate the connection (e.g., call
mysql_error(&mysql) or mysql_error(mysql_init_handle) when admin is NULL), or
otherwise only call mysql_error(admin) when admin is non-NULL; update the
fprintf accordingly and keep the return exit_status() behavior.

Comment on lines +1 to +6
/**
* @file mysql-track_system_variables_optional-t.cpp
* @brief This test verifies that ProxySQL properly handles session variable tracking
* in OPTIONAL mode based on MySQL server version. Session tracking should work
* on MySQL 5.7+ and gracefully degrade on 5.6 and below.
*/
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Documentation filename mismatch.

The @file comment on line 2 says mysql-track_system_variables_optional-t.cpp but the actual filename is mysql-session_track_variables_optional-t.cpp. This should be corrected for consistency.

🔧 Suggested fix
 /**
- * @file mysql-track_system_variables_optional-t.cpp
+ * @file mysql-session_track_variables_optional-t.cpp
  * @brief This test verifies that ProxySQL properly handles session variable tracking
🤖 Prompt for AI Agents
In @test/tap/tests/mysql-session_track_variables_optional-t.cpp around lines 1 -
6, The file header @file tag is incorrect: it says
"mysql-track_system_variables_optional-t.cpp" but the actual file is
"mysql-session_track_variables_optional-t.cpp"; update the @file comment in the
file header (the line starting with "@file") to exactly
"mysql-session_track_variables_optional-t.cpp" so the documentation and filename
match.

Comment on lines +116 to +119
if (!admin) {
fprintf(stderr, "File %s, line %d, Error: %s\n", __FILE__, __LINE__, mysql_error(admin));
return exit_status();
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Same NULL pointer issue as in enforced test.

The mysql_error() call on a potentially NULL admin pointer (and similarly for proxy at line 127) should be fixed as noted in the enforced test review.

🤖 Prompt for AI Agents
In @test/tap/tests/mysql-session_track_variables_optional-t.cpp around lines 116
- 119, Avoid calling mysql_error() with a NULL pointer: update the
error-handling for the `admin` (and similarly `proxy`) initialization so you
only call mysql_error(admin) when `admin` is non-NULL; if `admin` is NULL log a
clear fallback message like "mysql_init() returned NULL" (or include
strerror(errno)) instead. Locate the checks around the `admin` and `proxy`
variables in this test and replace the unconditional mysql_error(...) call with
a conditional that prints mysql_error(admin) when admin != NULL, otherwise
prints the fallback message; do the same pattern for `proxy`.

Comment on lines 1131 to 1136
MYSQL_QUERY_T(admin, "SET mysql-auto_increment_delay_multiplex=0");
MYSQL_QUERY_T(admin, "SET mysql-default_session_track_gtids='OFF'");
MYSQL_QUERY_T(admin, "SET mysql-session_track_variables=0");

MYSQL_QUERY_T(admin, "LOAD MYSQL VARIABLES TO RUNTIME");

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t “fix” regressions by disabling session tracking in this test; adjust the test to tolerate/measure the new init traffic instead.

reg_test_4264 relies on exact query/connection counters, so turning off mysql-session_track_variables (Line 1133) likely just sidesteps new backend SET/OK behavior introduced by this PR. That makes the test less meaningful and can hide real regressions in transaction routing.

Suggested direction: establish the baseline after any connection-init/session-track setup (or explicitly subtract/ignore the internal setup queries), so the test can run with tracking enabled and still assert routing correctly.

🤖 Prompt for AI Agents
In @test/tap/tests/reg_test_4264-commit_rollback-t.cpp around lines 1131 - 1136,
The test disables session tracking (the MYSQL_QUERY_T setting
mysql-session_track_variables=0) which masks new connection-init SET/OK traffic;
instead re-enable session tracking and adjust reg_test_4264 to measure or ignore
init traffic by establishing the baseline counters after connection
initialization (e.g., perform the connection(s) and session-track setup, then
call a helper that reads current query/connection counters and uses those as the
starting point) or explicitly subtract the known internal setup queries from
assertions; update the places using MYSQL_QUERY_T and the LOAD MYSQL VARIABLES
TO RUNTIME step so the test asserts delta values relative to the post-init
baseline rather than absolute counts.

Signed-off-by: René Cannaò <rene@proxysql.com>
@mirostauder
Copy link
Collaborator

retest this please

Signed-off-by: Miro Stauder <miro@proxysql.com>
@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Track session-specific system variables using session_track_system_variables

5 participants