Uploaded image for project: 'MariaDB Server'
  1. MariaDB Server
  2. MDEV-21777

Implement crash-safe execution the user XA on binlog-less slave

    XMLWordPrintable

Details

    • Task
    • Status: Open (View Workflow)
    • Major
    • Resolution: Unresolved
    • None
    • None
    • None

    Description

      Non-binlog based Slave crash-recovery in presence of XA load

      This task is ensued by MDEV-742 and implements a non-binlog based Slave crash-recovery
      in presence of XA load. For the binlog-based one see MDEV-33168.
      Both parts fully cover the agenda of the upstream's Bug#76233.

      In case of no binlog set on the slave server updates to the slave's gtid state
      may be lost when XA PREPAREs or completes with COMMIT or ROLLBACK.
      That is because unlike the normal transaction case the gtid_slave_pos table
      updates can't be simply bundled into a XA transaction.
      And that can not be done because XA-PREPARE part of the XA won't commit at the end.

      A solution therefore has to involve a 2nd transaction to be 2-phase committed
      with either of the replicated XA-PREPARE or XA-COMPLETE parts of an XA.

      Let the slave gtid state is described by GTID, simplified into a number in the following.
      Let 'xid' be the name of a replicated XA transaction (see XA specification,
      and our implementation of xid_t of sql/handler.h), which is its unique identifier
      provided by the user.
      Let 'slave_xid' be yet another name for the internal transaction coupled
      with the replicated XA for 2-phase commit.

      Consider an example where the initial slave state be GTID=99, and
      XA-PREPARE('xid') group of events having gtid=100.
      Its execution breaks down into the following steps (leaving only relevant queries):

      0. XA START xid; update t set.. ;         # replicated XA
       
      1. XA START 'slave_xid';                        # internal branch
                 set statement for sql_log_bin = 0 \
                        INSERT INTO gtid_slave_pos(gtid=100)
         XA END 'slave_xid';
         XA PREPARE 'slave_xid' ;  # runs  innobase_xa_prepare()    with flush_log_later=true
       
      2. "select" wait for prior commit;
       
      3. XA PREPARE 'xid';
            // runs innobase_xa_prepare()    with normal durability to incur p.1's durability *now*
            //      binlog_write()
      4. XA COMMIT 'slave_xid';                    # internal branch
            // runs innobase_commit_by_xid('slave_xid')
      

      Effectively two transactions comprise an internal two branch XA, with one branch
      gets committed in the good case.
      In which the replicated one will be seen as prepared:

      5  XA RECOVER; =>
           'xid'
       
       and the slave internal having updated the slave state:
       
         select max(gtid) from gtid_slave_pos; =>
         100
      

      When server crash and restarts depending on whether 'slave_xid' and 'xid' are found
      the engine the following decisions are computed, call it at step 6:

      6 crash-in-between decision
        0-4 G = 99
        4-... G = 100

      In other words when crash occurs before XA-PREPARE has prepared durably (p.4 had not reached)
      it will not be remembered by Engine and its satellite 'slave_xid' transaction
      will be rolled back. Otherwise two transactions will be found as prepared
      and through their naming find each other at recovery.

      To identify two xids as belonging to the internal XA branches
      the internal slave transaction's 'slave_xid' has to be named closely to the "parent"
      replicated 'xid'.
      Also 'slave_xid' has to hint at what XA OPERATION was intended at the crash time.
      So 'slave_xid' is set to be equal to to `xid` in all members of the struct

      struct xid_t {
        long formatID;
        long gtrid_length;
        long bqual_length;
        char data[XIDDATASIZE];  // not \0-terminated !
       ...
      }
      

      except XID::formatID.
      In this member two last bits could
      be reserved to express the actual operation on the replicated XA:

      1. PREPARE     1 << 31
      2. COMMIT      1 << 32
      3. ROLLBACK    both bits up.
      
      

      XA-COMMIT,ROLLBACK are handled similarly.

      A use case of a replicated prepared XA's `xid` found at recovery
      without a matching `slave_xid` is about the XA successfully prepared
      to have passed through 1-5, so not in doubt.

      Attachments

        Issue Links

          Activity

            People

              serg Sergei Golubchik
              Elkin Andrei Elkin
              Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

              Dates

                Created:
                Updated:

                Git Integration

                  Error rendering 'com.xiplink.jira.git.jira_git_plugin:git-issue-webpanel'. Please contact your Jira administrators.