-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathbinlog_file.go
More file actions
145 lines (126 loc) · 4.7 KB
/
binlog_file.go
File metadata and controls
145 lines (126 loc) · 4.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/*
Copyright 2015 Shlomi Noach, courtesy Booking.com
Copyright 2022 GitHub Inc.
See https://github.com/github/gh-ost/blob/master/LICENSE
*/
package mysql
import (
"fmt"
"strconv"
"strings"
)
// FileBinlogCoordinates described binary log coordinates in the form of a binlog file & log position.
type FileBinlogCoordinates struct {
LogFile string
LogPos int64
EventSize int64
}
func NewFileBinlogCoordinates(logFile string, logPos int64) *FileBinlogCoordinates {
return &FileBinlogCoordinates{
LogFile: logFile,
LogPos: logPos,
}
}
// ParseFileBinlogCoordinates parses a log file/position string into a *BinlogCoordinates struct.
func ParseFileBinlogCoordinates(logFileLogPos string) (*FileBinlogCoordinates, error) {
tokens := strings.SplitN(logFileLogPos, ":", 2)
if len(tokens) != 2 {
return nil, fmt.Errorf("ParseFileBinlogCoordinates: Cannot parse BinlogCoordinates from %s. Expected format is file:pos", logFileLogPos)
}
if logPos, err := strconv.ParseInt(tokens[1], 10, 0); err != nil {
return nil, fmt.Errorf("ParseFileBinlogCoordinates: invalid pos: %s", tokens[1])
} else {
return &FileBinlogCoordinates{LogFile: tokens[0], LogPos: logPos}, nil
}
}
// DisplayString returns a user-friendly string representation of these coordinates
func (this *FileBinlogCoordinates) DisplayString() string {
return fmt.Sprintf("%s:%d", this.LogFile, this.LogPos)
}
// String returns a user-friendly string representation of these coordinates
func (this FileBinlogCoordinates) String() string {
return this.DisplayString()
}
// Equals tests equality of this coordinate and another one.
func (this *FileBinlogCoordinates) Equals(other BinlogCoordinates) bool {
coord, ok := other.(*FileBinlogCoordinates)
if !ok || other == nil {
return false
}
return this.LogFile == coord.LogFile && this.LogPos == coord.LogPos
}
// IsEmpty returns true if the log file is empty, unnamed
func (this *FileBinlogCoordinates) IsEmpty() bool {
return this.LogFile == ""
}
// SmallerThan returns true if this coordinate is strictly smaller than the other.
func (this *FileBinlogCoordinates) SmallerThan(other BinlogCoordinates) bool {
coord, ok := other.(*FileBinlogCoordinates)
if !ok || other == nil {
return false
}
fileNumberDist := this.FileNumberDistance(coord)
if fileNumberDist == 0 {
return this.LogPos < coord.LogPos
}
return fileNumberDist > 0
}
// SmallerThanOrEquals returns true if this coordinate is the same or equal to the other one.
// We do NOT compare the type so we can not use this.Equals()
func (this *FileBinlogCoordinates) SmallerThanOrEquals(other BinlogCoordinates) bool {
coord, ok := other.(*FileBinlogCoordinates)
if !ok || other == nil {
return false
}
if this.SmallerThan(other) {
return true
}
return this.LogFile == coord.LogFile && this.LogPos == coord.LogPos // No Type comparison
}
// FileNumberDistance returns the numeric distance between this coordinate's file number and the other's.
// Effectively it means "how many rotates/FLUSHes would make these coordinates's file reach the other's"
func (this *FileBinlogCoordinates) FileNumberDistance(other *FileBinlogCoordinates) int {
thisNumber, _ := this.FileNumber()
otherNumber, _ := other.FileNumber()
return otherNumber - thisNumber
}
// FileNumber returns the numeric value of the file, and the length in characters representing the number in the filename.
// Example: FileNumber() of mysqld.log.000789 is (789, 6)
func (this *FileBinlogCoordinates) FileNumber() (int, int) {
tokens := strings.Split(this.LogFile, ".")
numPart := tokens[len(tokens)-1]
numLen := len(numPart)
fileNum, err := strconv.Atoi(numPart)
if err != nil {
return 0, 0
}
return fileNum, numLen
}
func (this *FileBinlogCoordinates) Clone() BinlogCoordinates {
return &FileBinlogCoordinates{
LogPos: this.LogPos,
LogFile: this.LogFile,
EventSize: this.EventSize,
}
}
// IsLogPosOverflowBeyond4Bytes returns true if the coordinate endpos is overflow beyond 4 bytes.
// The binlog event end_log_pos field type is defined as uint32, 4 bytes.
// https://github.com/go-mysql-org/go-mysql/blob/master/replication/event.go
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_replication_binlog_event.html#sect_protocol_replication_binlog_event_header
// Issue: https://github.com/github/gh-ost/issues/1366
func (this *FileBinlogCoordinates) IsLogPosOverflowBeyond4Bytes(preCoordinate *FileBinlogCoordinates) bool {
if preCoordinate == nil {
return false
}
if preCoordinate.IsEmpty() {
return false
}
if this.LogFile != preCoordinate.LogFile {
return false
}
if preCoordinate.LogPos+this.EventSize >= 1<<32 {
// Unexpected rows event, the previous binlog log_pos + current binlog event_size is overflow 4 bytes
return true
}
return false
}