Make migrations compatible with GTID replication (#2615)

* Make migrations compatible with GTID replication

Fixes an issue some deployments encountered when migrations used a
statement that is unsupported in GTID replication mode (#2462).

Local dev MySQL now enforces this consistency, so it should be easier to
maintain compatibility going forward.

* Update docker-compose formatting

* if exists
This commit is contained in:
Zach Wasserman 2021-10-21 03:46:02 -07:00 committed by GitHub
parent b856f351b0
commit 35523017c9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 51 additions and 24 deletions

1
changes/gtid-replication Normal file
View file

@ -0,0 +1 @@
* Fix migration compatibility with MySQL GTID replication.

View file

@ -1,5 +1,5 @@
---
version: '2'
version: "2"
services:
# To test with MariaDB, set FLEET_MYSQL_IMAGE to mariadb:10.6 or the like.
mysql:
@ -7,7 +7,15 @@ services:
platform: linux/x86_64
volumes:
- mysql-persistent-volume:/tmp
command: mysqld --datadir=/tmp/mysqldata --event-scheduler=ON
command: [
"mysqld",
"--datadir=/tmp/mysqldata",
"--event-scheduler=ON",
# These 3 keys run MySQL with GTID consistency enforced to avoid issues with production deployments that use it.
"--enforce-gtid-consistency=ON",
"--log-bin=bin.log",
"--server-id=master-01",
]
environment: &mysql-default-environment
MYSQL_ROOT_PASSWORD: toor
MYSQL_DATABASE: fleet
@ -20,7 +28,19 @@ services:
image: ${FLEET_MYSQL_IMAGE:-mysql:5.7}
platform: linux/x86_64
# innodb-file-per-table=OFF gives ~20% speedup for test runs.
command: mysqld --datadir=/tmpfs --slow_query_log=1 --log_output=TABLE --log-queries-not-using-indexes --event-scheduler=ON --innodb-file-per-table=OFF
command: [
"mysqld",
"--datadir=/tmpfs",
"--slow_query_log=1",
"--log_output=TABLE",
"--log-queries-not-using-indexes",
"--event-scheduler=ON",
"--innodb-file-per-table=OFF",
# These 3 keys run MySQL with GTID consistency enforced to avoid issues with production deployments that use it.
"--enforce-gtid-consistency=ON",
"--log-bin=bin.log",
"--server-id=master-01",
]
environment: *mysql-default-environment
ports:
- "3307:3306"
@ -57,7 +77,7 @@ services:
image: redis:5
command: redis-server /usr/local/etc/redis/redis.conf
ports:
- '7001:7001'
- "7001:7001"
volumes:
- ./tools/redis-tests/redis-cluster-1.conf:/usr/local/etc/redis/redis.conf
networks:
@ -68,7 +88,7 @@ services:
image: redis:5
command: redis-server /usr/local/etc/redis/redis.conf
ports:
- '7002:7002'
- "7002:7002"
volumes:
- ./tools/redis-tests/redis-cluster-2.conf:/usr/local/etc/redis/redis.conf
networks:
@ -79,7 +99,7 @@ services:
image: redis:5
command: redis-server /usr/local/etc/redis/redis.conf
ports:
- '7003:7003'
- "7003:7003"
volumes:
- ./tools/redis-tests/redis-cluster-3.conf:/usr/local/etc/redis/redis.conf
networks:
@ -90,7 +110,7 @@ services:
image: redis:5
command: redis-server /usr/local/etc/redis/redis.conf
ports:
- '7004:7004'
- "7004:7004"
volumes:
- ./tools/redis-tests/redis-cluster-4.conf:/usr/local/etc/redis/redis.conf
networks:
@ -101,7 +121,7 @@ services:
image: redis:5
command: redis-server /usr/local/etc/redis/redis.conf
ports:
- '7005:7005'
- "7005:7005"
volumes:
- ./tools/redis-tests/redis-cluster-5.conf:/usr/local/etc/redis/redis.conf
networks:
@ -112,7 +132,7 @@ services:
image: redis:5
command: redis-server /usr/local/etc/redis/redis.conf
ports:
- '7006:7006'
- "7006:7006"
volumes:
- ./tools/redis-tests/redis-cluster-6.conf:/usr/local/etc/redis/redis.conf
networks:
@ -122,8 +142,8 @@ services:
saml_idp:
image: fleetdm/docker-idp:latest
environment:
SIMPLESAMLPHP_SP_ENTITY_ID: 'https://localhost:8080'
SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE: 'https://localhost:8080/api/v1/fleet/sso/callback'
SIMPLESAMLPHP_SP_ENTITY_ID: "https://localhost:8080"
SIMPLESAMLPHP_SP_ASSERTION_CONSUMER_SERVICE: "https://localhost:8080/api/v1/fleet/sso/callback"
volumes:
- ./tools/saml/users.php:/var/www/simplesamlphp/config/authsources.php
ports:

View file

@ -32,30 +32,30 @@ func Up_20210819131107(tx *sql.Tx) error {
// Clear any orphan software and host_software
// Note that we can't use CREATE TEMPORARY TABLE here as it caused problems in some MySQL
// configurations. See https://github.com/fleetdm/fleet/issues/2462.
_, err = tx.Exec(`CREATE TABLE temp_host_software AS SELECT * FROM host_software;`)
// configurations (GTID replication). See https://github.com/fleetdm/fleet/issues/2462.
_, err = tx.Exec(`CREATE TABLE IF NOT EXISTS temp_host_software LIKE host_software`)
if err != nil {
return errors.Wrap(err, "save current host software to a temp table")
return errors.Wrap(err, "")
}
_, err = tx.Exec(`DELETE FROM host_software;`)
if err != nil {
return errors.Wrap(err, "clear all host software")
}
if _, err := tx.Exec(`
ALTER TABLE host_software
ALTER TABLE temp_host_software
ADD FOREIGN KEY host_software_hosts_fk(host_id) REFERENCES hosts (id) ON DELETE CASCADE,
ADD FOREIGN KEY host_software_software_fk(software_id) REFERENCES software (id) ON DELETE CASCADE
`); err != nil {
return errors.Wrap(err, "add fk on host_software hosts & software")
}
_, err = tx.Exec(`INSERT IGNORE INTO host_software SELECT * FROM temp_host_software;`)
_, err = tx.Exec(`INSERT IGNORE INTO temp_host_software SELECT * FROM host_software`)
if err != nil {
return errors.Wrap(err, "reinserting host software")
return errors.Wrap(err, "reinsert host software")
}
_, err = tx.Exec(`DROP TABLE temp_host_software;`)
_, err = tx.Exec(`DROP TABLE IF EXISTS host_software`)
if err != nil {
return errors.Wrap(err, "clear all host software")
}
_, err = tx.Exec(`RENAME TABLE temp_host_software TO host_software`)
if err != nil {
return errors.Wrap(err, "dropping temp table")
}

View file

@ -52,6 +52,8 @@ func connectMySQL(t *testing.T, testName string, opts *DatastoreTestOptions) *Da
}
func setupReadReplica(t *testing.T, testName string, ds *Datastore, opts *DatastoreTestOptions) {
t.Helper()
// create the context that will cancel the replication goroutine on test exit
var cancel func()
ctx := context.Background()
@ -132,7 +134,11 @@ func setupReadReplica(t *testing.T, testName string, ds *Datastore, opts *Datast
t.Log(stmt)
_, err = replica.ExecContext(ctx, stmt)
require.NoError(t, err)
stmt = fmt.Sprintf(`CREATE TABLE %s.%s SELECT * FROM %s.%s`, replicaDB, tbl, testName, tbl)
stmt = fmt.Sprintf(`CREATE TABLE %s.%s LIKE %s.%s`, replicaDB, tbl, testName, tbl)
t.Log(stmt)
_, err = replica.ExecContext(ctx, stmt)
require.NoError(t, err)
stmt = fmt.Sprintf(`INSERT INTO %s.%s SELECT * FROM %s.%s`, replicaDB, tbl, testName, tbl)
t.Log(stmt)
_, err = replica.ExecContext(ctx, stmt)
require.NoError(t, err)