Skip to content

Commit b3b50ec

Browse files
authored
Merge pull request #398 from Shopify/tyeh/make-mysql8.4-compatible
Update for MySQl 8.4 compatibility
2 parents 119c388 + 3616507 commit b3b50ec

13 files changed

Lines changed: 215 additions & 22 deletions

File tree

.github/workflows/start-mysql.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ set -xe
33

44
if [ "$MYSQL_VERSION" == "8.0" ]; then
55
docker compose -f docker-compose_8.0.yml up -d mysql-1 mysql-2
6+
elif [ "$MYSQL_VERSION" == "8.4" ]; then
7+
docker compose -f docker-compose_8.4.yml up -d mysql-1 mysql-2
68
else
79
docker compose up -d mysql-1 mysql-2
810
fi

.github/workflows/tests.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
gh-285:
1111
strategy:
1212
matrix:
13-
mysql: ["5.7", "8.0"]
13+
mysql: ["5.7", "8.0", "8.4"]
1414

1515
runs-on: ubuntu-latest
1616
continue-on-error: true
@@ -35,7 +35,7 @@ jobs:
3535
go-test:
3636
strategy:
3737
matrix:
38-
mysql: ["5.7", "8.0"]
38+
mysql: ["5.7", "8.0", "8.4"]
3939

4040
runs-on: ubuntu-latest
4141
timeout-minutes: 15
@@ -60,7 +60,7 @@ jobs:
6060
ruby-test:
6161
strategy:
6262
matrix:
63-
mysql: ["5.7", "8.0"]
63+
mysql: ["5.7", "8.0", "8.4"]
6464

6565
runs-on: ubuntu-latest
6666
timeout-minutes: 15

binlog_streamer.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -499,9 +499,19 @@ func idExistsOnServer(id uint32, db *sql.DB) (bool, error) {
499499
}
500500

501501
func idsOnServer(db *sql.DB) ([]uint32, error) {
502-
rows, err := db.Query("SHOW SLAVE HOSTS")
502+
var query string
503+
var errorMsg string
504+
version, _ := db.QueryMySQLVersion()
505+
if isVersionAtLeast(version, "8.4.0") {
506+
query = "SHOW REPLICAS"
507+
errorMsg = "replicas"
508+
} else {
509+
query = "SHOW SLAVE HOSTS"
510+
errorMsg = "slave hosts"
511+
}
512+
rows, err := db.Query(query)
503513
if err != nil {
504-
return nil, fmt.Errorf("could not get slave hosts: %s", err)
514+
return nil, fmt.Errorf("could not get %s: %s", errorMsg, err)
505515
}
506516
defer rows.Close()
507517

@@ -517,16 +527,16 @@ func idsOnServer(db *sql.DB) ([]uint32, error) {
517527
// i.e MySQL/Percona have 5 columns as it includes slave_uuid for MariaDB slave_uuid is omitted
518528
// since all other values are not used check for the amount of columns and gather only what is possible
519529
if err != nil {
520-
return nil, fmt.Errorf("could not get columns from slave hosts: %v", err)
530+
return nil, fmt.Errorf("could not get columns from %s: %v", errorMsg, err)
521531
} else if len(columns) == 5 {
522532
err = rows.Scan(&server_id, &host, &port, &master_id, &slave_uuid)
523533
} else if len(columns) == 4 {
524534
err = rows.Scan(&server_id, &host, &port, &master_id)
525535
} else {
526-
return nil, fmt.Errorf("could not scan SHOW SLAVE HOSTS row, err: unknown result set with %d columns: %v", len(columns), columns)
536+
return nil, fmt.Errorf("could not scan %s row, err: unknown result set with %d columns: %v", query, len(columns), columns)
527537
}
528538
if err != nil {
529-
return nil, fmt.Errorf("could not scan SHOW SLAVE HOSTS row, err: %s", err.Error())
539+
return nil, fmt.Errorf("could not scan %s row, err: %s", query, err.Error())
530540
}
531541

532542
server_ids = append(server_ids, server_id)

copydb/test/copydb_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ func (t *CopydbTestSuite) TestCreateDatabaseCopiesTheRightCollation() {
140140
err = row.Scan(&characterSet, &collation)
141141
t.Require().Nil(err)
142142

143-
if os.Getenv("MYSQL_VERSION") == "8.0" {
143+
if os.Getenv("MYSQL_VERSION") == "8.0" || os.Getenv("MYSQL_VERSION") == "8.4" {
144144
t.Require().Equal(characterSet, "utf8mb3")
145145
t.Require().Equal(collation, "utf8mb3_general_ci")
146146
} else {

docker-compose_8.4.yml

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
version: "3"
2+
services:
3+
mysql-1:
4+
image: percona/percona-server:8.4.4-4.1
5+
command: --server-id=1
6+
--log-bin=mysql-bin
7+
--max-binlog-size=4096
8+
--binlog-format=ROW
9+
--sync-binlog=1
10+
--log-replica-updates=ON
11+
--gtid-mode=ON
12+
--enforce-gtid-consistency=ON
13+
--character-set-server=utf8mb4
14+
--collation-server=utf8mb4_unicode_ci
15+
--max-connections=1000
16+
--read-only=OFF
17+
--binlog-rows-query-log-events=ON
18+
environment:
19+
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
20+
MYSQL_ROOT_HOST: "%"
21+
volumes:
22+
- /var/lib/mysql
23+
ports:
24+
- "29291:3306"
25+
26+
mysql-2:
27+
image: percona/percona-server:8.4.4-4.1
28+
command: --server-id=2
29+
--log-bin=mysql-bin
30+
--binlog-format=ROW
31+
--max-binlog-size=4096
32+
--sync-binlog=1
33+
--log-replica-updates=ON
34+
--gtid-mode=ON
35+
--enforce-gtid-consistency=ON
36+
--character-set-server=utf8mb4
37+
--collation-server=utf8mb4_unicode_ci
38+
--max-connections=1000
39+
--binlog-rows-query-log-events=ON
40+
environment:
41+
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
42+
MYSQL_ROOT_HOST: "%"
43+
volumes:
44+
- /var/lib/mysql
45+
ports:
46+
- "29292:3306"
47+
48+
mysql-3:
49+
image: percona/percona-server:8.4.4-4.1
50+
command: --server-id=3
51+
--log-bin=mysql-bin
52+
--binlog-format=ROW
53+
--max-binlog-size=4096
54+
--sync-binlog=1
55+
--log-replica-updates=ON
56+
--gtid-mode=ON
57+
--enforce-gtid-consistency=ON
58+
--character-set-server=utf8mb4
59+
--collation-server=utf8mb4_unicode_ci
60+
--max-connections=1000
61+
--binlog-rows-query-log-events=ON
62+
environment:
63+
MYSQL_ALLOW_EMPTY_PASSWORD: "yes"
64+
MYSQL_ROOT_HOST: "%"
65+
volumes:
66+
- /var/lib/mysql
67+
ports:
68+
- "29293:3306"

test/go/lag_throttler_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ func TestThrottlerRunErrors(t *testing.T) {
145145
assert.NotNil(t, err)
146146
expectedError := "Error 1146: Table 'meta.lag_table' doesn't exist"
147147

148-
if os.Getenv("MYSQL_VERSION") == "8.0" {
148+
if os.Getenv("MYSQL_VERSION") == "8.0" || os.Getenv("MYSQL_VERSION") == "8.4" {
149149
expectedError = "Error 1049: Unknown database 'meta'"
150150
}
151151

test/integration/inline_verifier_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -590,7 +590,7 @@ def test_utfmb3_data_from_utfmb4_to_utfmb3
590590
# https://github.com/Shopify/ghostferry/pull/328#discussion_r791197939
591591
def test_utfmb4_data_from_utfmb4_to_utfmb3
592592
run_collation_test(UTF8MB4DATA, "utf8mb4", "utf8mb3", identical: false)
593-
end unless ENV['MYSQL_VERSION'] == '8.0'
593+
end unless ENV['MYSQL_VERSION'] == '8.0' || ENV['MYSQL_VERSION'] == '8.4'
594594

595595
private
596596

test/integration/interrupt_resume_test.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -311,8 +311,9 @@ def test_interrupt_resume_inline_verifier_will_verify_additional_rows_changed_on
311311
# originally taken from @kolbitsch-lastline in https://github.com/Shopify/ghostferry/pull/160
312312
def test_interrupt_resume_between_consecutive_rows_events
313313
ghostferry = new_ghostferry(MINIMAL_GHOSTFERRY, config: { verifier_type: "Inline" })
314-
315-
start_binlog_status = source_db.query('SHOW MASTER STATUS').first
314+
version, _ = source_db.query("SELECT @@version").first["@@version"]
315+
query = version < "8.4.0" ? "SHOW MASTER STATUS" : "SHOW BINARY LOG STATUS"
316+
start_binlog_status = source_db.query(query).first
316317

317318
# create a series of rows-events that do not have interleaved table-map
318319
# events. This is the case when multiple rows are affected in a single

test/integration/types_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ def test_decimal
412412

413413
def format_float_based_on_mysql_version(value)
414414
# mysql 5.7 removes the trailing zeros when `cast...as json` is used
415-
ENV["MYSQL_VERSION"] == "8.0" ? value.to_s : value.to_i.to_s
415+
ENV["MYSQL_VERSION"] == "8.0" || ENV["MYSQL_VERSION"] == "8.4" ? value.to_s : value.to_i.to_s
416416
end
417417

418418
def insert_json_on_source

utils.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
sqlorig "database/sql"
77
"encoding/binary"
88
"fmt"
9+
"strings"
910
"sync"
1011
"sync/atomic"
1112
"time"
@@ -188,10 +189,28 @@ func (c *StmtCache) getStmt(query string) (*sqlorig.Stmt, bool) {
188189
return stmt, exists
189190
}
190191

192+
func isVersionAtLeast(version string, targetVersion string) bool {
193+
result, err := mysql.CompareServerVersions(version, targetVersion)
194+
if err != nil {
195+
return false
196+
}
197+
return result >= 0
198+
}
199+
200+
func getBinlogStatusCommand(db *sql.DB) string {
201+
version, _ := db.QueryMySQLVersion()
202+
if isVersionAtLeast(version, "8.4.0") {
203+
return "SHOW BINARY LOG STATUS"
204+
} else {
205+
return "SHOW MASTER STATUS"
206+
}
207+
}
208+
191209
func ShowMasterStatusBinlogPosition(db *sql.DB) (mysql.Position, error) {
192-
rows, err := db.Query("SHOW MASTER STATUS")
210+
query := getBinlogStatusCommand(db)
211+
rows, err := db.Query(query)
193212
if err != nil {
194-
return NewMysqlPosition("", 0, err)
213+
return NewMysqlPosition("", 0, err, db)
195214
}
196215
defer rows.Close()
197216
var file string
@@ -201,7 +220,7 @@ func ShowMasterStatusBinlogPosition(db *sql.DB) (mysql.Position, error) {
201220
if rows.Next() {
202221
cols, err = rows.Columns()
203222
if err != nil {
204-
return NewMysqlPosition(file, position, err)
223+
return NewMysqlPosition(file, position, err, db)
205224
}
206225
switch len(cols) {
207226
case 4:
@@ -210,18 +229,20 @@ func ShowMasterStatusBinlogPosition(db *sql.DB) (mysql.Position, error) {
210229
err = rows.Scan(&file, &position, &binlog_do_db, &binlog_ignore_db, &executed_gtid_set)
211230
}
212231
}
213-
return NewMysqlPosition(file, position, err)
232+
return NewMysqlPosition(file, position, err, db)
214233
}
215234

216-
func NewMysqlPosition(file string, position uint32, err error) (mysql.Position, error) {
235+
func NewMysqlPosition(file string, position uint32, err error, db *sql.DB) (mysql.Position, error) {
236+
var binlogStatusCmd string
237+
binlogStatusCmd = getBinlogStatusCommand(db)
217238
switch {
218239
case err == sqlorig.ErrNoRows:
219-
return mysql.Position{}, fmt.Errorf("no results from show master status")
240+
return mysql.Position{}, fmt.Errorf("no results from %s", strings.ToLower(binlogStatusCmd))
220241
case err != nil:
221242
return mysql.Position{}, err
222243
default:
223244
if file == "" {
224-
return mysql.Position{}, fmt.Errorf("show master status does not show a file")
245+
return mysql.Position{}, fmt.Errorf("%s does not show a file", strings.ToLower(binlogStatusCmd))
225246
}
226247

227248
return mysql.Position{

0 commit comments

Comments
 (0)