Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config/embed.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ var MycnfMySQL8026 string

//go:embed mycnf/mysql84.cnf
var MycnfMySQL84 string

//go:embed mycnf/mysql90.cnf
var MycnfMySQL90 string
39 changes: 39 additions & 0 deletions config/mycnf/mysql90.cnf
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# This file is auto-included when MySQL 9.0 or later is detected.

# all db instances should skip starting replication threads - that way we can do any
# additional configuration (like enabling semi-sync) before we connect to
# the source.
skip_replica_start

# MySQL 8.0 enables binlog by default with sync_binlog and TABLE info repositories
# It does not enable GTIDs or enforced GTID consistency

gtid_mode = ON
enforce_gtid_consistency
relay_log_recovery = 1
binlog_expire_logs_seconds = 259200

# disable mysqlx
mysqlx = 0

# Semi-sync replication is required for automated unplanned failover
# (when the primary goes away). Here we just load the plugin so it's
# available if desired, but it's disabled at startup.
#
# VTTablet will enable semi-sync at the proper time when replication is set up,
# or when a primary is promoted or demoted based on the durability policy configured.
plugin-load = rpl_semi_sync_source=semisync_source.so;rpl_semi_sync_replica=semisync_replica.so

# MySQL 8.0.26 and later will not load plugins during --initialize
# which makes these options unknown. Prefixing with --loose
# tells the server it's fine if they are not understood.
loose_rpl_semi_sync_source_timeout = 1000000000000000000
loose_rpl_semi_sync_source_wait_no_replica = 1

# In order to protect against any errand GTIDs we will start the mysql instance
# in super-read-only mode.
super-read-only

# Replication parameters to ensure reparents are fast.
replica_net_timeout = 8

5 changes: 5 additions & 0 deletions go/mysql/flavor.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ const (
// mysql8VersionPrefix is the prefix for 8.x mysql version, such as 8.0.19,
// but also newer ones like 8.4.0.
mysql8VersionPrefix = "8."
// mysql9VersionPrefix is the prefix for 9.x mysql version, such as 9.0.0,
// 9.1.0, 9.2.0, etc.
mysql9VersionPrefix = "9."
)

// flavor is the abstract interface for a flavor.
Expand Down Expand Up @@ -200,6 +203,8 @@ func GetFlavor(serverVersion string, flavorFunc func(serverVersion string) flavo
} else {
f = mysqlFlavor8Legacy{mysqlFlavorLegacy{mysqlFlavor{serverVersion: serverVersion}}}
}
case strings.HasPrefix(serverVersion, mysql9VersionPrefix):
f = mysqlFlavor9{mysqlFlavor{serverVersion: serverVersion}}
default:
// If unknown, return the most basic flavor: MySQL 57.
f = mysqlFlavor57{mysqlFlavorLegacy{mysqlFlavor{serverVersion: serverVersion}}}
Expand Down
7 changes: 7 additions & 0 deletions go/mysql/flavor_mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,13 @@ type mysqlFlavor82 struct {
mysqlFlavor
}

// mysqlFlavor9 is for MySQL 9.x.y and later. It's the most modern
// flavor but has an explicit name so that it's clear what versions
// it is for.
type mysqlFlavor9 struct {
mysqlFlavor
}

var _ flavor = (*mysqlFlavor8)(nil)
var _ flavor = (*mysqlFlavor82)(nil)

Expand Down
80 changes: 80 additions & 0 deletions go/mysql/flavor_mysql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,83 @@ func TestMysql82ResetReplicationParametersCommands(t *testing.T) {
queries := conn.ResetReplicationParametersCommands()
assert.Equal(t, []string{"RESET REPLICA ALL"}, queries)
}

func TestMysql9SetReplicationPositionCommands(t *testing.T) {
pos := replication.Position{GTIDSet: replication.Mysql56GTIDSet{}}
conn := &Conn{flavor: mysqlFlavor9{}}
queries := conn.SetReplicationPositionCommands(pos)
assert.Equal(t, []string{"RESET BINARY LOGS AND GTIDS", "SET GLOBAL gtid_purged = ''"}, queries)
}

func TestMysql9ResetReplicationParametersCommands(t *testing.T) {
conn := &Conn{flavor: mysqlFlavor9{}}
queries := conn.ResetReplicationParametersCommands()
assert.Equal(t, []string{"RESET REPLICA ALL"}, queries)
}

func TestMysql9SetReplicationSourceCommand(t *testing.T) {
params := &ConnParams{
Uname: "username",
Pass: "password",
}
host := "localhost"
port := int32(123)
connectRetry := 1234
want := `CHANGE REPLICATION SOURCE TO
SOURCE_HOST = 'localhost',
SOURCE_PORT = 123,
SOURCE_USER = 'username',
SOURCE_PASSWORD = 'password',
SOURCE_CONNECT_RETRY = 1234,
GET_SOURCE_PUBLIC_KEY = 1,
SOURCE_AUTO_POSITION = 1`

conn := &Conn{flavor: mysqlFlavor9{}}
got := conn.SetReplicationSourceCommand(params, host, port, 0, connectRetry)
assert.Equal(t, want, got, "mysqlFlavor9.SetReplicationSourceCommand(%#v, %#v, %#v, %#v) = %#v, want %#v", params, host, port, connectRetry, got, want)

var heartbeatInterval = 5.4
want = `CHANGE REPLICATION SOURCE TO
SOURCE_HOST = 'localhost',
SOURCE_PORT = 123,
SOURCE_USER = 'username',
SOURCE_PASSWORD = 'password',
SOURCE_CONNECT_RETRY = 1234,
GET_SOURCE_PUBLIC_KEY = 1,
SOURCE_HEARTBEAT_PERIOD = 5.4,
SOURCE_AUTO_POSITION = 1`

got = conn.SetReplicationSourceCommand(params, host, port, heartbeatInterval, connectRetry)
assert.Equal(t, want, got, "mysqlFlavor9.SetReplicationSourceCommand(%#v, %#v, %#v, %#v, %#v) = %#v, want %#v", params, host, port, heartbeatInterval, connectRetry, got, want)
}

func TestMysql9SetReplicationSourceCommandSSL(t *testing.T) {
params := &ConnParams{
Uname: "username",
Pass: "password",
SslCa: "ssl-ca",
SslCaPath: "ssl-ca-path",
SslCert: "ssl-cert",
SslKey: "ssl-key",
}
params.EnableSSL()
host := "localhost"
port := int32(123)
connectRetry := 1234
want := `CHANGE REPLICATION SOURCE TO
SOURCE_HOST = 'localhost',
SOURCE_PORT = 123,
SOURCE_USER = 'username',
SOURCE_PASSWORD = 'password',
SOURCE_CONNECT_RETRY = 1234,
SOURCE_SSL = 1,
SOURCE_SSL_CA = 'ssl-ca',
SOURCE_SSL_CAPATH = 'ssl-ca-path',
SOURCE_SSL_CERT = 'ssl-cert',
SOURCE_SSL_KEY = 'ssl-key',
SOURCE_AUTO_POSITION = 1`

conn := &Conn{flavor: mysqlFlavor9{}}
got := conn.SetReplicationSourceCommand(params, host, port, 0, connectRetry)
assert.Equal(t, want, got, "mysqlFlavor9.SetReplicationSourceCommand(%#v, %#v, %#v, %#v) = %#v, want %#v", params, host, port, connectRetry, got, want)
}
176 changes: 176 additions & 0 deletions go/mysql/flavor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,30 @@ func TestServerVersionCapableOf(t *testing.T) {
capability: capabilities.CheckConstraintsCapability,
isCapable: true,
},
{
// MySQL 9.0.0 should support modern capabilities
version: "9.0.0",
capability: capabilities.InstantDDLFlavorCapability,
isCapable: true,
},
{
// MySQL 9.1.0 should support transactional GTID executed
version: "9.1.0",
capability: capabilities.TransactionalGtidExecutedFlavorCapability,
isCapable: true,
},
{
// MySQL 9.0.5 should support check constraints
version: "9.0.5",
capability: capabilities.CheckConstraintsCapability,
isCapable: true,
},
{
// MySQL 9.2.1-log should support performance schema data locks
version: "9.2.1-log",
capability: capabilities.PerformanceSchemaDataLocksTableCapability,
isCapable: true,
},
}
for _, tc := range testcases {
name := fmt.Sprintf("%s %v", tc.version, tc.capability)
Expand All @@ -129,3 +153,155 @@ func TestServerVersionCapableOf(t *testing.T) {
})
}
}

func TestGetFlavor(t *testing.T) {
testcases := []struct {
version string
expectedType string
description string
}{
// MySQL 5.7.x versions should get mysqlFlavor57
{
version: "5.7.29",
expectedType: "mysqlFlavor57",
description: "MySQL 5.7 should use mysqlFlavor57",
},
{
version: "5.7.38-log",
expectedType: "mysqlFlavor57",
description: "MySQL 5.7 with suffix should use mysqlFlavor57",
},
// MySQL 8.0.x versions with different capabilities
{
version: "8.0.11",
expectedType: "mysqlFlavor8Legacy",
description: "MySQL 8.0.11 should use mysqlFlavor8Legacy (no replica terminology)",
},
{
version: "8.0.22",
expectedType: "mysqlFlavor8Legacy",
description: "MySQL 8.0.22 should use mysqlFlavor8Legacy (no replica terminology, < 8.0.26)",
},
{
version: "8.0.26",
expectedType: "mysqlFlavor8",
description: "MySQL 8.0.26 should use mysqlFlavor8 (has replica terminology, < 8.2.0)",
},
{
version: "8.1.0",
expectedType: "mysqlFlavor8",
description: "MySQL 8.1.0 should use mysqlFlavor8 (has replica terminology, < 8.2.0)",
},
{
version: "8.2.0",
expectedType: "mysqlFlavor82",
description: "MySQL 8.2.0 should use mysqlFlavor82",
},
{
version: "8.3.0",
expectedType: "mysqlFlavor82",
description: "MySQL 8.3.0 should use mysqlFlavor82",
},
{
version: "8.0.30-log",
expectedType: "mysqlFlavor8",
description: "MySQL 8.0.30 with suffix should use mysqlFlavor8 (has replica terminology, < 8.2.0)",
},
// MySQL 9.x versions should get mysqlFlavor9
{
version: "9.0.0",
expectedType: "mysqlFlavor9",
description: "MySQL 9.0.0 should use mysqlFlavor9",
},
{
version: "9.1.0",
expectedType: "mysqlFlavor9",
description: "MySQL 9.1.0 should use mysqlFlavor9",
},
{
version: "9.0.5-log",
expectedType: "mysqlFlavor9",
description: "MySQL 9.0.5 with suffix should use mysqlFlavor9",
},
{
version: "9.2.1-debug",
expectedType: "mysqlFlavor9",
description: "MySQL 9.2.1 with debug suffix should use mysqlFlavor9",
},
{
version: "9.10.15",
expectedType: "mysqlFlavor9",
description: "MySQL 9.10.15 should use mysqlFlavor9",
},
// MariaDB versions
{
version: "5.5.5-10.1.48-MariaDB",
expectedType: "mariadbFlavor101",
description: "MariaDB 10.1 with replication hack prefix should use mariadbFlavor101",
},
{
version: "10.1.48-MariaDB",
expectedType: "mariadbFlavor101",
description: "MariaDB 10.1 should use mariadbFlavor101",
},
{
version: "10.2.0-MariaDB",
expectedType: "mariadbFlavor102",
description: "MariaDB 10.2 should use mariadbFlavor102",
},
{
version: "10.5.15-MariaDB-log",
expectedType: "mariadbFlavor102",
description: "MariaDB 10.5 with suffix should use mariadbFlavor102",
},
// Default/unknown versions should get mysqlFlavor57
{
version: "5.6.45",
expectedType: "mysqlFlavor57",
description: "MySQL 5.6 should default to mysqlFlavor57",
},
{
version: "unknown-version",
expectedType: "mysqlFlavor57",
description: "Unknown version should default to mysqlFlavor57",
},
{
version: "4.1.22",
expectedType: "mysqlFlavor57",
description: "Very old MySQL version should default to mysqlFlavor57",
},
}

for _, tc := range testcases {
t.Run(fmt.Sprintf("%s_%s", tc.version, tc.expectedType), func(t *testing.T) {
flavor, _, _ := GetFlavor(tc.version, nil)

// Check the flavor type matches expected
switch tc.expectedType {
case "mysqlFlavor57":
_, ok := flavor.(mysqlFlavor57)
assert.True(t, ok, "Expected mysqlFlavor57 for version %s, but got %T. %s", tc.version, flavor, tc.description)
case "mysqlFlavor8Legacy":
_, ok := flavor.(mysqlFlavor8Legacy)
assert.True(t, ok, "Expected mysqlFlavor8Legacy for version %s, but got %T. %s", tc.version, flavor, tc.description)
case "mysqlFlavor8":
_, ok := flavor.(mysqlFlavor8)
assert.True(t, ok, "Expected mysqlFlavor8 for version %s, but got %T. %s", tc.version, flavor, tc.description)
case "mysqlFlavor82":
_, ok := flavor.(mysqlFlavor82)
assert.True(t, ok, "Expected mysqlFlavor82 for version %s, but got %T. %s", tc.version, flavor, tc.description)
case "mysqlFlavor9":
_, ok := flavor.(mysqlFlavor9)
assert.True(t, ok, "Expected mysqlFlavor9 for version %s, but got %T. %s", tc.version, flavor, tc.description)
case "mariadbFlavor101":
_, ok := flavor.(mariadbFlavor101)
assert.True(t, ok, "Expected mariadbFlavor101 for version %s, but got %T. %s", tc.version, flavor, tc.description)
case "mariadbFlavor102":
_, ok := flavor.(mariadbFlavor102)
assert.True(t, ok, "Expected mariadbFlavor102 for version %s, but got %T. %s", tc.version, flavor, tc.description)
default:
t.Errorf("Unknown expected type: %s", tc.expectedType)
}
})
}
}
2 changes: 2 additions & 0 deletions go/vt/mysqlctl/mysqld.go
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,8 @@ func (mysqld *Mysqld) getMycnfTemplate() string {
} else {
versionConfig = config.MycnfMySQL80
}
case 9:
versionConfig = config.MycnfMySQL90
default:
log.Infof("this version of Vitess does not include built-in support for %v %v", mysqld.capabilities.flavor, mysqld.capabilities.version)
}
Expand Down
28 changes: 28 additions & 0 deletions go/vt/mysqlctl/mysqld_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,3 +322,31 @@ func TestHostMetrics(t *testing.T) {
assert.Equal(t, "datadir-used-ratio", metric.Name)
assert.Empty(t, metric.Error)
}

func TestGetMycnfTemplateMySQL9(t *testing.T) {
db := fakesqldb.New(t)
defer db.Close()

params := db.ConnParams()
cp := *params
dbc := dbconfigs.NewTestDBConfigs(cp, cp, "fakesqldb")

testMysqld := NewMysqld(dbc)
defer testMysqld.Close()

// Test MySQL 9.0
testMysqld.capabilities = newCapabilitySet(FlavorMySQL, ServerVersion{Major: 9, Minor: 0, Patch: 0})
template := testMysqld.getMycnfTemplate()
assert.Contains(t, template, "[mysqld]")
// Should use MySQL 9.0 config for MySQL 9.x
assert.Contains(t, template, "# This file is auto-included when MySQL 9.0 or later is detected.")
assert.NotContains(t, template, "mysql_native_password = ON")

// Test MySQL 9.1
testMysqld.capabilities = newCapabilitySet(FlavorMySQL, ServerVersion{Major: 9, Minor: 1, Patch: 5})
template = testMysqld.getMycnfTemplate()
assert.Contains(t, template, "[mysqld]")
// Should use MySQL 9.0 config for MySQL 9.x
assert.Contains(t, template, "# This file is auto-included when MySQL 9.0 or later is detected.")
assert.NotContains(t, template, "mysql_native_password = ON")
}
Loading