@@ -421,8 +421,28 @@ func (this *Applier) ReadMigrationMaxValues(uniqueKey *sql.UniqueKey) error {
421
421
return err
422
422
}
423
423
424
- // ReadMigrationRangeValues reads min/max values that will be used for rowcopy
424
+ // ReadMigrationRangeValues reads min/max values that will be used for rowcopy.
425
+ // Before read min/max, write a changelog state into the ghc table to avoid lost data in mysql two-phase commit.
426
+ /*
427
+ Detail description of the lost data in mysql two-phase commit issue by @Fanduzi:
428
+ When using semi-sync and setting rpl_semi_sync_master_wait_point=AFTER_SYNC,
429
+ if an INSERT statement is being committed but blocks due to an unmet ack count,
430
+ the data inserted by the transaction is not visible to ReadMigrationRangeValues,
431
+ so the copy of the existing data in the table does not include the new row inserted by the transaction.
432
+ However, the binlog event for the transaction is already written to the binlog,
433
+ so the addDMLEventsListener only captures the binlog event after the transaction,
434
+ and thus the transaction's binlog event is not captured, resulting in data loss.
435
+
436
+ If write a changelog into ghc table before ReadMigrationRangeValues, and the transaction commit blocks
437
+ because the ack is not met, then the changelog will not be able to write, so the ReadMigrationRangeValues
438
+ will not be run. When the changelog writes successfully, the ReadMigrationRangeValues will read the
439
+ newly inserted data, thus Avoiding data loss due to the above problem.
440
+ */
425
441
func (this * Applier ) ReadMigrationRangeValues () error {
442
+ if _ , err := this .WriteChangelogState (string (ReadMigrationRangeValues )); err != nil {
443
+ return err
444
+ }
445
+
426
446
if err := this .ReadMigrationMinValues (this .migrationContext .UniqueKey ); err != nil {
427
447
return err
428
448
}
0 commit comments