Legends (use sub- and super- scripts in TeX notation) ----------------------------------------------------- {{G_i}} - committed in engine(s) gtid transaction with index {{i}} in binlog order {{g_k}} - prepared in engine(s) present in binlog with index {{k}} {{U_l}} - unsafe group of events present in binlog with index {{l}} {{g^t_(s)}} - the truncate position which be estimated multiple times until settled, {{(s)}} stands for approximation number, the superscript here stands for the first letter of the word 'truncate'. Also the superscript can be applied {{g_k^i}} to designate {{g_k}} belongs to the file {{i}}. Think of the subscripts as {{GTID::seq_no}}, the twi notions are equivalent indeed with one replication domain and the strict (binary log and/or apply) gtid mode. I drop either sub- or super- scripts when it does affect reading. Any instances from above are ordered/comparable with {{<>=}} in sense of their mutual position in binlog, like {{U_l < g^t}} indicates the unsafe group is within the remained part of the file, should it be truncated at {{g^t}}. {{B^*}} - recovery starting binlog file, is input for the recovery algorithm {{B_0}} - binlog file referred by {{BCP}} {{B_i}} - an {{i}}:th binlog file in a recovery binlog file sequence {{[B_0,B_i,..., B_i..., B^*_{n-1}]}}. The files are ordered by the binlog index file order. Binary log events: {{FD}} - Format Descriptor {{BCP}} - Binlog CheckPoint contains {{B_0}}, see below {{GLL}} - Gtid List contains approximation to binlog gtid state In a general case of a "normal" server recovery there are two rounds involving {{n}} binlog files {{B_0, B_1, ... B_{n-2}, B^*_{n-1}}}. The first round scans {{B^*}} to identify {{B_0}} in the binlog index, as well as transactions in doubt in {{B^*}}. When {{B^* = B^0}} the transactions in doubt gets recovered (committed). Otherwise the 2nd round is executed to collect the remained in {{B_0..B_n-2}} and decide about them in the end. The semisync slave recovery implements the following specifics - the 2nd round may require to process the starting binlog {{B^*_{n-1}}} for the 2nd time. - a 3rd round may be necessary to settle down the truncate position {{g^t}}. - the truncate position must be greater (in the binlog order) than the maximum unsafe group motivated by two factors: a. gtid binlog state now depends on {{g^t}}, and b. when transactions in doubt use multiple of different engines which is the reason of 'alternations', explained below. Let {{r_max}} is the maximum round in the following. Safe recovery ------------- Let's call the truncate-recovery is safe when a truncate position {{g^t}} is determined and there's no non-transactional data beyond it: {{\not\exists U_l: U_l > g^t}}. Depending on combination of {{G_i}},{{g_k}} in binlog event the semisync-slave recovery case set is as follows. Informally there are four major classes as combination choices of - one or few binlog files, and - whether or not the files have {{g_i, G_i+1}} sort of "alternations". The first case is not a 'major' one having no {{G}}. A. The only binlog file consists of prepared transactions: {{B^* = [ FD, GLL, BCP(B^*), g_1, g_2, ..., g_l ], \not exist G_k, 1 < k < l }} {{BCP(B^*)}} refer to the initial file itself. Recovery ends with the maximum round {{r_max = 1}}. B. As above, the only binlog file has a sub-sequence of {{[g_1, G_2, g_3]}} to indicate the truncate position must be guessed at least two times - {{g^t_{2}}} {{B^* = [ ..., BCP(B^*), g_1, G_2, g_3, ..., g_l ], \not \exist G_k, 3 < k < l}} {{r_max = 2}}, {{g^t}} gets set to {{g_3}} in the 2nd round. C. As above, the only binlog file starts with a committed {{G}}: {{B^* = [ ... BCP(B^*), G_1, g_2, ..., g_l ], \not \exist G_k, 2 < k < l }} {{r_max = 1}}, {{g^t}} is computed straight to {{g_2}} in the 1st round as is validated by the fact of no {{G_k, k > 2}}. D. General case recovery with {{n}} files D.1 {{B^*_{n-1}}} as in the case A, {{g_i}} is the first group in it {{B^* = [... BCP(B^*), g_i, g_i+1, ..., g_i+l ], \not \exist G_k, i+1 < k < l}} D.1.a other files {{B_0...B_n-2}} have a {{G}}-committed range in the beginning of their event sequence, before a {{g_m}} that starts the {{g}}-prepare sequence of length {{j}} {{G_1, G_2,..., G_i-j, g_i-j+1, ... g_i-1}} When {{j>0}} the assessment of {{g^t_(1) = g_i}} is dropped in favor {{g_i-j+1}}. Recovery ends with {{r_max = 2}}, including when {{j=0}}. D.1.b {{B_0...B_n-2}} have {{gGg}}-alternations like {{ g_1, G_2, g_3}} in the case B above. Then e.g {{n = 2}} {{[B_0,B_1^*]}}, in {{B_0 = [..., GLL, g_1^0, G_2^0, g_3^0]}} The assessment of {{g^t_(1)}} is dropped twice in the 2nd round, which forces {{r_max = 3}}. D.2 as in the case B there's at least one alternation {{B^*_n-1 = [ ..., BCP(B_0), g_1, G_2, g_3, ..., g_l ], \not \exist G_k, 3 < k < l}} When other files {{B_0...B_n-2}} are like in D.1.a the 1st round assessment of {{g^t = g_3}} is validated in the 2nd round where the algorithm ends. Otherwise (when there are {{gGg}}-alternations in {{[B_0..B_n-2]}} the 3rd round is needed similarly to D.1.b. Non-safe -------- While executing the rounds {{U_{\max}}} is computed. It's unsafe to truncate when at the end of the rounds {{g^t < U_{\max}}}. Binlog state ------------ In the normal server recovery the binlog state is computed as union of gtids in {{GLL}} from {{B^*}} (the initial state) and {{G,g}}:s found in {{B^*}}. In the semisync-slave recovery it'ss computed iteratively along with assessing {{g^t}} truncate position and its validation. No specical round is designed for that. Also iteratively is computed the binlog gtid initial state. First it's done as in the normal case recovery and if more rounds it gets refined to the value {{GLL}} from {{B_0}}. When {{g^t}} is refined by {{g^t_(i+1)}} the {{i+1}}-th approximation, the previous {{g^t_(i)}} changes to commit (except it was estimated in the 1st round), so it got to be added to the binlog state. When the current round ends to validate {{g^t_(last)}} assessment then the following round also validates the binlog state (along with refinements to commit by possible {{g_i < g^t_(last)}}.