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
- relates to
-
MDEV-742 LP:803649 - Xa recovery failed on client disconnection
- Closed
-
MDEV-31038 Parallel Replication Breaks if XA PREPARE Fails Updating Slave GTID State
- Closed
- split from
-
MDEV-21469 Implement crash-safe logging of the user XA
- Stalled