WL#17202: Allow/disallow asynchronous replication from a higher version source

Affects: Server-9.x   —   Status: Complete

EXECUTIVE SUMMARY
=================
Asynchronous Replication does not restrict from which source
versions a replica can replicate from.
If a higher version is configured as source, the replica will
connect to the source, fetch and apply data until eventually some
incompatible (only known on the source version) event comes and
stops replication with a error.

This worklog will introduce a global option to disallow asynchronous
replication from a higher version source into a lower version replica.
This global option will control all asynchronous channels.

When replication from a higher version source is disallowed, we can
have the following examples:

Example 1:
 source:  8.4.9
 replica: 9.7.1
Asynchronous replication is allowed.

Example 2:
 source:  10.0.1
 replica:  9.7.1
Asynchronous replication is not allowed.

The option will also consider the MySQL LTS Releases, that is, all
patches version within the same LTS version are considered
compatible.

Example 3:
 source: 9.7.5
 replica: 9.7.1
Source and replica are considered the same version: 9.7 LTS.


USER STORIES
============
* As a MySQL DBA I want to disallow asynchronous replication from a
  higher version source to prevent errors due to incompatible
  events.
# FUNCTIONAL REQUIREMENTS

FR-01. A new global configuration option replica_allow_higher_version_source
       shall be introduced to enable or disable asynchronous replication from
       a higher-version source server. This global option will control all
       asynchronous channels.

FR-02. The default value of replica_allow_higher_version_source shall be ON.
       This preserves the existing MySQL behavior.

FR-03. When replica_allow_higher_version_source = OFF:
       A replica must reject asynchronous replication when the source MySQL
       version is higher than the replica version.
       Version comparison must consider:
        - Major version
        - Minor version
        - Patch version

FR-04. When replica_allow_higher_version_source = OFF,
       for Long-Term Support (LTS) versions (e.g., 9.7):
       All patch versions within the same LTS release MUST be allowed regardless
       of higher/lower patch numbers.
       The LTS exception applies only when:
       - Source and replica are a LTS version and have the same major and same
         minor version.

FR-05. If replication is disallowed due to version mismatch under
       replica_allow_higher_version_source = OFF, the replica must:
        - Fail the connection handshake
        - Emit a descriptive error

FR-06. When asynchronous connection failover is enabled, if replication to one
       source fails due to a version-compatibility mismatch (triggered under
       replica_allow_higher_version_source = OFF), the replica must continue
       normal asynchronous connection failover behavior.
       Version-compatibility failures must be treated as a valid failover
       condition (similar to network) and must not stop the failover process.

FR-07. When replica_allow_higher_version_source = ON:
        - This represents the current behavior.
        - No version comparison-based restriction should prevent the replica
          from connecting.

# NON-FUNCTIONAL REQUIREMENTS

NFR-01. Backward Compatibility
        The implementation must remain fully backward compatible with existing
        MySQL deployments. When replica_allow_higher_version_source = ON
        (default), the behavior must be identical to current asynchronous
        replication behavior, with zero functional changes.

NFR-02. Error Reporting & Observability
        The system must provide clear, diagnosable error messages for
        version-compatibility failures, enabling DBAs to identify root causes
        quickly.
# Summary of the approach

We introduce a new global configuration variable that controls whether
asynchronous replication is allowed from a higher-version source to a
lower-version replica. This option governs all asynchronous replication
channels.

When the variable is ON (default), replication from higher-version sources
behaves as it does today.

When set to OFF, the replica rejects connections from any source running a
higher MySQL version, except in the case of designated Long-Term Support (LTS)
versions, where all patch versions within the same LTS series are allowed.

# Security context

No applicable changes on security context.

# Observability

This feature introduces additional observability points to help users
understand, diagnose, and audit replication version-compatibility behavior.

1. Error Logging

   When replication from a higher-version source is disallowed:
    A clear error message is written to the error log indicating:
        Source server version
        Replica server version
        The value of replica_allow_higher_version_source
        Reason for rejection

2. Replication Channel Status

   In replication_connection_status or related tables, the system will reflect
   the failure reason when a channel is stopped due to version incompatibility.

3. Variable descriptions in
   SHOW VARIABLES LIKE 'replica_allow_higher_version_source'

# Upgrade/downgrade and cross-version replication

Upgrade and Downgrade Path:
The presence of the new variable replica_allow_higher_version_source does not
require any special upgrade procedures.
Downgrade is only allowed within a LTS version. This option will be introduced
on 9.7.0, the first patch version of 9.7 LTS, thence no downgrade is allowed.

Cross-Version Replication Logic:
Depending on the value of replica_allow_higher_version_source, the following
rules apply:

1. Variable = ON (default)

   This matches existing MySQL behavior and places no version-based restriction
   on asynchronous replication.

2. Variable = OFF

	Replica rejects replication from a higher-version source except:
	When both source and replica are part of the same LTS release series,
    e.g.:
		Source: 9.7.5
		Replica: 9.7.1
	Patch differences in LTS are explicitly allowed.
	Non-LTS major/minor/patch increase causes rejection:
		Example: Source 10.0.5 -> Replica 9.7.1 -> Rejected

If the replica is upgraded later, compatibility is re-evaluated on the next I/O
thread reconnect.

# User interface

The behavior is controlled through a new global system variable that allows
the user to enable or disable asynchronous replication from a higher-version
source server.

 - command-Line Format:	--replica-allow-higher-version-source[={OFF|ON}]
 - system Variable Name:	replica_allow_higher_version_source
 - scope: Global
 - dynamic: Yes
 - type: Boolean
 - default: ON (preserves existing behavior)
 - Required Privilege to SET:
     SYSTEM_VARIABLES_ADMIN plus REPLICATION_SLAVE_ADMIN, or SUPER

The variable replica_allow_higher_version_source can be set using the following
commands:
SET GLOBAL replica_allow_higher_version_source=OFF;

This parameter can also be passed to the command line as follows:
mysqld --replica-allow-higher-version-source=[OFF|ON]

The parameter can also be written in a .cnf file:
[mysqld]
replica_allow_higher_version_source=[OFF|ON]

The current value of this parameter will be shown by the command:
SELECT @@GLOBAL.replica_allow_higher_version_source;

The current value of this parameter can also be queried by querying
performance_schema.global_variables:
SELECT * FROM PERFORMANCE_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAMES LIKE
'%replica_allow_higher_version_source%';

# Deployment and installation

The feature introduces a new global system variable:
replica_allow_higher_version_source.

No special installation or migration steps are required. The feature becomes
active immediately after upgrade.

Default Behavior:
For all new installations and upgrades, the default value is ON, which preserves
existing behavior (higher-version sources are allowed).

Backward Compatibility:
Existing deployments that upgrade to this version but do not specify
the variable will automatically inherit the default value ON.
This ensures that upgrading MySQL does not break existing replication setups.

# Protocol

No protocol changes.

# Failure Model Specification

When replica_allow_higher_version_source = OFF and a replica attempts to connect
to a source whose version is higher than the replica:
 - The replica rejects the connection attempt.
 - The replication channel does not start.
 - A clear error is emitted to the error log indicating:
	- The local server version
	- The source server version
	- That the connection is disallowed due to configuration.

A replication channel error status is also surfaced through:
 - SHOW REPLICA STATUS
 - Performance Schema table: replication_connection_status

LTS Exception Behavior:
If both source and replica are within the same LTS series
(e.g., 9.7.1 <-> 9.7.5):
  - Patch-level differences are ignored.
  - No error is raised, even if replica_allow_higher_version_source = OFF.

# Behavior Changes

When replica_allow_higher_version_source=OFF, the replica rejects any attempt
to connect to a higher-version source server.

A clear error message is written to the error log indicating the version
mismatch and the configuration that caused the rejection.

The replication channel does not start until the configuration is changed or
the version mismatch is resolved.

LTS Exception:
Patch-level differences within the same LTS version series
(e.g., 9.7.1 vs. 9.7.8) are always allowed, even when the variable is OFF.
This ensures LTS-to-LTS replication within the same series remains unaffected.

# Testing

1. Functional Tests

1.1 Basic Compatibility Cases
    - source > replica, variable OFF -> replica must reject the connection.
    - source > replica, variable ON -> replication must succeed.
    - source == replica -> replication must succeed.
    - source < replica -> replication must succeed.

1.2 LTS Exception
    - Source: 9.7.5, Replica: 9.7.1, variable OFF -> Allowed.
    - Source: 9.7.1, Replica: 9.7.5, variable OFF -> Allowed.
    - Source and replica both LTS but different versions -> Rejected.

2. Error Logging Tests

   - Error logs contain correct source and replica versions.
   - Correct error code is emitted.

3. Regression Tests

   - Ensure no regressions in existing replication behavior, failover logic,
     I/O thread handling, SQL thread behavior.
# Summary of changes

1. Create a new error code

   ER_RPL_REPLICA_VERSION_INCOMPATIBLE
   "Replication from higher version source (%s) to lower version replica (%s) is
    disallowed due replica_allow_higher_version_source=OFF."

2. Add a new global variable

   Name:replica_allow_higher_version_source
   Properties:
    Type: boolean
    Scope: GLOBAL
    Dynamic: YES
    Default: ON
    Required Privilege to SET:
       SYSTEM_VARIABLES_ADMIN plus REPLICATION_SLAVE_ADMIN, or SUPER

   static Sys_var_bool Sys_replica_allow_higher_version_source(
    "replica_allow_higher_version_source",
    "If disabled - replica rejects any attempt to connect to a higher-version "
    "source server. If enabled - no compatibility check.",
    GLOBAL_VAR(opt_replica_allow_higher_version_source),
    CMD_LINE(OPT_ARG),
    DEFAULT(true), NO_MUTEX_GUARD, NOT_IN_BINLOG,
    ON_CHECK(check_replica_allow_higher_version_source),
    ON_UPDATE(nullptr));

3. Implement the version-compatibility check helper function

    /*
      Function to check whether replication from a given source MySQL version
      is allowed for a replica running the specified local version.

      @param source_ver_str  Version string reported by the source server
      @param local_ver_str   Version string of the local replica server
      @return true  If the source version is compatible
      @return false Otherwise
    */
    static bool is_version_compatible(const char *source_ver_str,
                  const char *local_ver_str);

   Logic:
    - Parse both versions into: major, minor, patch.
    - LTS rule: if major and minor match and the version is LTS -> allow all
        patch versions.
    - Otherwise:
     - Encode versions as (major << 16 | minor << 8 | patch)
     - Allow only when source_version <= local_version.

4. Add compatibility error tracking in Master_info class

   Add a new member:
   `bool m_compatibility_error;`

   Add accessor methods:
   `is_compatibility_error()`
   `set_compatibility_error()`
   `reset_compatibility_error()`

   Purpose:
   Allows the I/O thread and the Async Failover Manager to detect when a
   connection failure was caused by version incompatibility rather than a
   network error.

5. Reset compatibility error state before each new attempt

   Ensures clean state per attempt.

6. Apply compatibility check inside `get_master_version_and_clock()`

   If the source version is not compatible and the option
   replica_allow_higher_version_source is disabled -> set the flag and return
   the new error.

7. Integrate with I/O thread failover
   Extend async failover retry logic to treat version-incompatibility errors as
   failover-eligible conditions.