Skip to content

Commit 48b34bc

Browse files
shaohkshaohoukuntimvaillancourtmeiji163
authored
Fix binlog end log pos over lost data (#1367)
* fix(fix binlog end-log-pos over 4g lost data): fix binlog end-log-pos over 4g lost data * fix(fix-binlog-end-log-pos-over-lost-data): fix-binlog-end-log-pos-over-lost-data Calculate whether the binlog log_pos overflows beyond 4G using end_log_pos and event_size. * fix(x): x x * test(fix-binlog-end-log-pos-over-lost-data): add unit test * fix(fix-binlog-end-log-pos-over-lost-data): x x * Update binlog.go change IsLogPosOverflowBeyond4Bytes comment. * Update binlog.go x * fix(fix-binlog-end-log-pos-over-lost-data): fix doc x --------- Co-authored-by: shaohoukun <[email protected]> Co-authored-by: Tim Vaillancourt <[email protected]> Co-authored-by: meiji163 <[email protected]>
1 parent 9ca2499 commit 48b34bc

File tree

3 files changed

+76
-2
lines changed

3 files changed

+76
-2
lines changed

go/binlog/gomysql_reader.go

+5
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ func (this *GoMySQLReader) GetCurrentBinlogCoordinates() *mysql.BinlogCoordinate
7878

7979
// StreamEvents
8080
func (this *GoMySQLReader) handleRowsEvent(ev *replication.BinlogEvent, rowsEvent *replication.RowsEvent, entriesChannel chan<- *BinlogEntry) error {
81+
if this.currentCoordinates.IsLogPosOverflowBeyond4Bytes(&this.LastAppliedRowsEventHint) {
82+
return fmt.Errorf("Unexpected rows event at %+v, the binlog end_log_pos is overflow 4 bytes", this.currentCoordinates)
83+
}
84+
8185
if this.currentCoordinates.SmallerThanOrEquals(&this.LastAppliedRowsEventHint) {
8286
this.migrationContext.Log.Debugf("Skipping handled query at %+v", this.currentCoordinates)
8387
return nil
@@ -141,6 +145,7 @@ func (this *GoMySQLReader) StreamEvents(canStopStreaming func() bool, entriesCha
141145
this.currentCoordinatesMutex.Lock()
142146
defer this.currentCoordinatesMutex.Unlock()
143147
this.currentCoordinates.LogPos = int64(ev.Header.LogPos)
148+
this.currentCoordinates.EventSize = int64(ev.Header.EventSize)
144149
}()
145150

146151
switch binlogEvent := ev.Event.(type) {

go/mysql/binlog.go

+27-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import (
1414

1515
// BinlogCoordinates described binary log coordinates in the form of log file & log position.
1616
type BinlogCoordinates struct {
17-
LogFile string
18-
LogPos int64
17+
LogFile string
18+
LogPos int64
19+
EventSize int64
1920
}
2021

2122
// ParseInstanceKey will parse an InstanceKey from a string representation such as 127.0.0.1:3306
@@ -74,3 +75,27 @@ func (this *BinlogCoordinates) SmallerThanOrEquals(other *BinlogCoordinates) boo
7475
}
7576
return this.LogFile == other.LogFile && this.LogPos == other.LogPos
7677
}
78+
79+
// IsLogPosOverflowBeyond4Bytes returns true if the coordinate endpos is overflow beyond 4 bytes.
80+
// The binlog event end_log_pos field type is defined as uint32, 4 bytes.
81+
// https://github.com/go-mysql-org/go-mysql/blob/master/replication/event.go
82+
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_replication_binlog_event.html#sect_protocol_replication_binlog_event_header
83+
// Issue: https://github.com/github/gh-ost/issues/1366
84+
func (this *BinlogCoordinates) IsLogPosOverflowBeyond4Bytes(preCoordinate *BinlogCoordinates) bool {
85+
if preCoordinate == nil {
86+
return false
87+
}
88+
if preCoordinate.IsEmpty() {
89+
return false
90+
}
91+
92+
if this.LogFile != preCoordinate.LogFile {
93+
return false
94+
}
95+
96+
if preCoordinate.LogPos+this.EventSize >= 1<<32 {
97+
// Unexpected rows event, the previous binlog log_pos + current binlog event_size is overflow 4 bytes
98+
return true
99+
}
100+
return false
101+
}

go/mysql/binlog_test.go

+44
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
package mysql
77

88
import (
9+
"math"
910
"testing"
1011

1112
"github.com/openark/golib/log"
@@ -52,3 +53,46 @@ func TestBinlogCoordinatesAsKey(t *testing.T) {
5253

5354
test.S(t).ExpectEquals(len(m), 3)
5455
}
56+
57+
func TestIsLogPosOverflowBeyond4Bytes(t *testing.T) {
58+
{
59+
var preCoordinates *BinlogCoordinates
60+
curCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00017", LogPos: 10321, EventSize: 1100}
61+
test.S(t).ExpectFalse(curCoordinates.IsLogPosOverflowBeyond4Bytes(preCoordinates))
62+
}
63+
{
64+
preCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00017", LogPos: 1100, EventSize: 1100}
65+
curCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00017", LogPos: int64(uint32(preCoordinates.LogPos + 1100)), EventSize: 1100}
66+
test.S(t).ExpectFalse(curCoordinates.IsLogPosOverflowBeyond4Bytes(preCoordinates))
67+
}
68+
{
69+
preCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00016", LogPos: 1100, EventSize: 1100}
70+
curCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00017", LogPos: int64(uint32(preCoordinates.LogPos + 1100)), EventSize: 1100}
71+
test.S(t).ExpectFalse(curCoordinates.IsLogPosOverflowBeyond4Bytes(preCoordinates))
72+
}
73+
{
74+
preCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00017", LogPos: math.MaxUint32 - 1001, EventSize: 1000}
75+
curCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00017", LogPos: int64(uint32(preCoordinates.LogPos + 1000)), EventSize: 1000}
76+
test.S(t).ExpectFalse(curCoordinates.IsLogPosOverflowBeyond4Bytes(preCoordinates))
77+
}
78+
{
79+
preCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00017", LogPos: math.MaxUint32 - 1000, EventSize: 1000}
80+
curCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00017", LogPos: int64(uint32(preCoordinates.LogPos + 1000)), EventSize: 1000}
81+
test.S(t).ExpectFalse(curCoordinates.IsLogPosOverflowBeyond4Bytes(preCoordinates))
82+
}
83+
{
84+
preCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00017", LogPos: math.MaxUint32 - 999, EventSize: 1000}
85+
curCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00017", LogPos: int64(uint32(preCoordinates.LogPos + 1000)), EventSize: 1000}
86+
test.S(t).ExpectTrue(curCoordinates.IsLogPosOverflowBeyond4Bytes(preCoordinates))
87+
}
88+
{
89+
preCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00017", LogPos: int64(uint32(math.MaxUint32 - 500)), EventSize: 1000}
90+
curCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00017", LogPos: int64(uint32(preCoordinates.LogPos + 1000)), EventSize: 1000}
91+
test.S(t).ExpectTrue(curCoordinates.IsLogPosOverflowBeyond4Bytes(preCoordinates))
92+
}
93+
{
94+
preCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00017", LogPos: math.MaxUint32, EventSize: 1000}
95+
curCoordinates := &BinlogCoordinates{LogFile: "mysql-bin.00017", LogPos: int64(uint32(preCoordinates.LogPos + 1000)), EventSize: 1000}
96+
test.S(t).ExpectTrue(curCoordinates.IsLogPosOverflowBeyond4Bytes(preCoordinates))
97+
}
98+
}

0 commit comments

Comments
 (0)