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