diff --git a/.github/workflows/upgrade_downgrade_test_backups_e2e.yml b/.github/workflows/upgrade_downgrade_test_backups_e2e.yml
index bde1a3283f7..e7e815050e5 100644
--- a/.github/workflows/upgrade_downgrade_test_backups_e2e.yml
+++ b/.github/workflows/upgrade_downgrade_test_backups_e2e.yml
@@ -161,8 +161,10 @@ jobs:
run: |
source build.env
- rm -f $PWD/bin/vttablet
+ rm -f $PWD/bin/vttablet $PWD/bin/mysqlctl $PWD/bin/mysqlctld
cp /tmp/vitess-build-other/bin/vttablet $PWD/bin/vttablet
+ cp /tmp/vitess-build-other/bin/mysqlctl $PWD/bin/mysqlctl
+ cp /tmp/vitess-build-other/bin/mysqlctld $PWD/bin/mysqlctld
vttablet --version
# Run test with VTTablet at version N-1 and VTBackup at version N
@@ -181,9 +183,11 @@ jobs:
run: |
source build.env
- rm -f $PWD/bin/vtbackup $PWD/bin/vttablet
+ rm -f $PWD/bin/vtbackup $PWD/bin/vttablet $PWD/bin/mysqlctl $PWD/bin/mysqlctld
cp /tmp/vitess-build-current/bin/vtbackup $PWD/bin/vtbackup
cp /tmp/vitess-build-other/bin/vttablet $PWD/bin/vttablet
+ cp /tmp/vitess-build-other/bin/mysqlctl $PWD/bin/mysqlctl
+ cp /tmp/vitess-build-other/bin/mysqlctld $PWD/bin/mysqlctld
vtbackup --version
vttablet --version
diff --git a/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml b/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml
index d92da4088e0..a6cf20d0ed7 100644
--- a/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml
+++ b/.github/workflows/upgrade_downgrade_test_backups_e2e_next_release.yml
@@ -164,8 +164,10 @@ jobs:
run: |
source build.env
- rm -f $PWD/bin/vttablet
+ rm -f $PWD/bin/vttablet $PWD/bin/mysqlctl $PWD/bin/mysqlctld
cp /tmp/vitess-build-other/bin/vttablet $PWD/bin/vttablet
+ cp /tmp/vitess-build-other/bin/mysqlctl $PWD/bin/mysqlctl
+ cp /tmp/vitess-build-other/bin/mysqlctld $PWD/bin/mysqlctld
vttablet --version
# Run test with VTTablet at version N+1 and VTBackup at version N
@@ -184,9 +186,11 @@ jobs:
run: |
source build.env
- rm -f $PWD/bin/vtbackup $PWD/bin/vttablet
- cp /tmp/vitess-build-current/bin/vtbackup $PWD/bin/vtbackup
- cp /tmp/vitess-build-other/bin/vttablet $PWD/bin/vttablet
+ rm -f $PWD/bin/vtbackup $PWD/bin/vttablet $PWD/bin/mysqlctl $PWD/bin/mysqlctld
+ cp /tmp/vitess-build-other/bin/vtbackup $PWD/bin/vtbackup
+ cp /tmp/vitess-build-current/bin/vttablet $PWD/bin/vttablet
+ cp /tmp/vitess-build-current/bin/mysqlctl $PWD/bin/mysqlctl
+ cp /tmp/vitess-build-current/bin/mysqlctld $PWD/bin/mysqlctld
vtbackup --version
vttablet --version
diff --git a/.github/workflows/upgrade_downgrade_test_backups_manual.yml b/.github/workflows/upgrade_downgrade_test_backups_manual.yml
index 6201d9b49ba..1cacf55a22e 100644
--- a/.github/workflows/upgrade_downgrade_test_backups_manual.yml
+++ b/.github/workflows/upgrade_downgrade_test_backups_manual.yml
@@ -234,8 +234,10 @@ jobs:
run: |
source build.env
- rm -f $PWD/bin/vttablet
+ rm -f $PWD/bin/vttablet $PWD/bin/mysqlctl $PWD/bin/mysqlctld
cp /tmp/vitess-build-other/bin/vttablet $PWD/bin/vttablet
+ cp /tmp/vitess-build-other/bin/mysqlctl $PWD/bin/mysqlctl
+ cp /tmp/vitess-build-other/bin/mysqlctld $PWD/bin/mysqlctld
vttablet --version
# Starting the tablets again, they will automatically start restoring the last backup.
@@ -290,8 +292,10 @@ jobs:
run: |
source build.env
- rm -f $PWD/bin/vttablet
+ rm -f $PWD/bin/vttablet $PWD/bin/mysqlctl $PWD/bin/mysqlctld
cp /tmp/vitess-build-current/bin/vttablet $PWD/bin/vttablet
+ cp /tmp/vitess-build-current/bin/mysqlctl $PWD/bin/mysqlctl
+ cp /tmp/vitess-build-current/bin/mysqlctld $PWD/bin/mysqlctld
vttablet --version
# Starting the tablets again and restoring the previous backup.
diff --git a/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml b/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml
index dc0ad902478..87eda3d9648 100644
--- a/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml
+++ b/.github/workflows/upgrade_downgrade_test_backups_manual_next_release.yml
@@ -237,8 +237,10 @@ jobs:
run: |
source build.env
- rm -f $PWD/bin/vttablet
+ rm -f $PWD/bin/vttablet $PWD/bin/mysqlctl $PWD/bin/mysqlctld
cp /tmp/vitess-build-other/bin/vttablet $PWD/bin/vttablet
+ cp /tmp/vitess-build-other/bin/mysqlctl $PWD/bin/mysqlctl
+ cp /tmp/vitess-build-other/bin/mysqlctld $PWD/bin/mysqlctld
vttablet --version
# Starting the tablets again, they will automatically start restoring the last backup.
@@ -293,8 +295,10 @@ jobs:
run: |
source build.env
- rm -f $PWD/bin/vttablet
+ rm -f $PWD/bin/vttablet $PWD/bin/mysqlctl $PWD/bin/mysqlctld
cp /tmp/vitess-build-current/bin/vttablet $PWD/bin/vttablet
+ cp /tmp/vitess-build-current/bin/mysqlctl $PWD/bin/mysqlctl
+ cp /tmp/vitess-build-current/bin/mysqlctld $PWD/bin/mysqlctld
vttablet --version
# Starting the tablets again and restoring the next backup.
diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml b/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml
index 2d8aceb6ceb..c0c240b0944 100644
--- a/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml
+++ b/.github/workflows/upgrade_downgrade_test_query_serving_queries.yml
@@ -209,9 +209,11 @@ jobs:
run: |
source build.env
- rm -f $PWD/bin/vtgate $PWD/bin/vttablet
+ rm -f $PWD/bin/vtgate $PWD/bin/vttablet $PWD/bin/mysqlctl $PWD/bin/mysqlctld
cp /tmp/vitess-build-current/bin/vtgate $PWD/bin/vtgate
cp /tmp/vitess-build-other/bin/vttablet $PWD/bin/vttablet
+ cp /tmp/vitess-build-other/bin/mysqlctl $PWD/bin/mysqlctl
+ cp /tmp/vitess-build-other/bin/mysqlctld $PWD/bin/mysqlctld
vtgate --version
vttablet --version
diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_queries_next_release.yml b/.github/workflows/upgrade_downgrade_test_query_serving_queries_next_release.yml
index 7de07a3892d..c2c06e274b8 100644
--- a/.github/workflows/upgrade_downgrade_test_query_serving_queries_next_release.yml
+++ b/.github/workflows/upgrade_downgrade_test_query_serving_queries_next_release.yml
@@ -212,9 +212,11 @@ jobs:
run: |
source build.env
- rm -f $PWD/bin/vtgate $PWD/bin/vttablet
+ rm -f $PWD/bin/vtgate $PWD/bin/vttablet $PWD/bin/mysqlctl $PWD/bin/mysqlctld
cp /tmp/vitess-build-current/bin/vtgate $PWD/bin/vtgate
cp /tmp/vitess-build-other/bin/vttablet $PWD/bin/vttablet
+ cp /tmp/vitess-build-other/bin/mysqlctl $PWD/bin/mysqlctl
+ cp /tmp/vitess-build-other/bin/mysqlctld $PWD/bin/mysqlctld
vtgate --version
vttablet --version
diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml b/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml
index 5b8e3107757..f7820c59739 100644
--- a/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml
+++ b/.github/workflows/upgrade_downgrade_test_query_serving_schema.yml
@@ -209,9 +209,11 @@ jobs:
run: |
source build.env
- rm -f $PWD/bin/vtgate $PWD/bin/vttablet
+ rm -f $PWD/bin/vtgate $PWD/bin/vttablet $PWD/bin/mysqlctl $PWD/bin/mysqlctld
cp /tmp/vitess-build-current/bin/vtgate $PWD/bin/vtgate
cp /tmp/vitess-build-other/bin/vttablet $PWD/bin/vttablet
+ cp /tmp/vitess-build-other/bin/mysqlctl $PWD/bin/mysqlctl
+ cp /tmp/vitess-build-other/bin/mysqlctld $PWD/bin/mysqlctld
vtgate --version
vttablet --version
diff --git a/.github/workflows/upgrade_downgrade_test_query_serving_schema_next_release.yml b/.github/workflows/upgrade_downgrade_test_query_serving_schema_next_release.yml
index 18075b62e0f..7e44b3e0e54 100644
--- a/.github/workflows/upgrade_downgrade_test_query_serving_schema_next_release.yml
+++ b/.github/workflows/upgrade_downgrade_test_query_serving_schema_next_release.yml
@@ -212,9 +212,11 @@ jobs:
run: |
source build.env
- rm -f $PWD/bin/vtgate $PWD/bin/vttablet
+ rm -f $PWD/bin/vtgate $PWD/bin/vttablet $PWD/bin/mysqlctl $PWD/bin/mysqlctld
cp /tmp/vitess-build-current/bin/vtgate $PWD/bin/vtgate
cp /tmp/vitess-build-other/bin/vttablet $PWD/bin/vttablet
+ cp /tmp/vitess-build-other/bin/mysqlctl $PWD/bin/mysqlctl
+ cp /tmp/vitess-build-other/bin/mysqlctld $PWD/bin/mysqlctld
vtgate --version
vttablet --version
diff --git a/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml b/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml
index 6cc0f1d9882..b5e5d67b5e8 100644
--- a/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml
+++ b/.github/workflows/upgrade_downgrade_test_reparent_new_vttablet.yml
@@ -182,8 +182,10 @@ jobs:
run: |
source build.env
- rm -f $PWD/bin/vttablet
+ rm -f $PWD/bin/vttablet $PWD/bin/mysqlctl $PWD/bin/mysqlctld
cp /tmp/vitess-build-other/bin/vttablet $PWD/bin/vttablet
+ cp /tmp/vitess-build-other/bin/mysqlctl $PWD/bin/mysqlctl
+ cp /tmp/vitess-build-other/bin/mysqlctld $PWD/bin/mysqlctld
vtctl --version
vttablet --version
diff --git a/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml b/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml
index bbb17c43e99..4029a082069 100644
--- a/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml
+++ b/.github/workflows/upgrade_downgrade_test_reparent_old_vttablet.yml
@@ -179,8 +179,10 @@ jobs:
run: |
source build.env
- rm -f $PWD/bin/vttablet
+ rm -f $PWD/bin/vttablet $PWD/bin/mysqlctl $PWD/bin/mysqlctld
cp /tmp/vitess-build-other/bin/vttablet $PWD/bin/vttablet
+ cp /tmp/vitess-build-other/bin/mysqlctl $PWD/bin/mysqlctl
+ cp /tmp/vitess-build-other/bin/mysqlctld $PWD/bin/mysqlctld
vtctl --version
vttablet --version
diff --git a/config/init_db.sql b/config/init_db.sql
index 7be4de6f7ea..d04960633de 100644
--- a/config/init_db.sql
+++ b/config/init_db.sql
@@ -1,5 +1,4 @@
-# This file is executed immediately after mysql_install_db,
-# to initialize a fresh data directory.
+# This file is executed immediately after initializing a fresh data directory.
###############################################################################
# WARNING: This sql is *NOT* safe for production use,
@@ -11,6 +10,12 @@
###############################################################################
# Equivalent of mysql_secure_installation
###############################################################################
+# We need to ensure that super_read_only is disabled so that we can execute
+# these commands. Note that disabling it does NOT disable read_only.
+# We save the current value so that we only re-enable it at the end if it was
+# enabled before.
+SET @original_super_read_only=IF(@@global.super_read_only=1, 'ON', 'OFF');
+SET GLOBAL super_read_only='OFF';
# Changes during the init db should not make it to the binlog.
# They could potentially create errant transactions on replicas.
@@ -77,3 +82,9 @@ FLUSH PRIVILEGES;
RESET SLAVE ALL;
RESET MASTER;
+
+# custom sql is used to add custom scripts like creating users/passwords. We use it in our tests
+# {{custom_sql}}
+
+# We need to set super_read_only back to what it was before
+SET GLOBAL super_read_only=IFNULL(@original_super_read_only, 'ON');
diff --git a/config/mycnf/default.cnf b/config/mycnf/default.cnf
index 0a375cb69c7..c17165f9959 100644
--- a/config/mycnf/default.cnf
+++ b/config/mycnf/default.cnf
@@ -14,9 +14,6 @@ port = {{.MysqlPort}}
secure-file-priv = {{.SecureFilePriv}}
{{end}}
-# all db instances should start in read-only mode - once the db is started and
-# fully functional, we'll push it into read-write mode
-read-only
server-id = {{.ServerID}}
# all db instances should skip starting replication threads - that way we can do any
diff --git a/config/mycnf/mariadb10.cnf b/config/mycnf/mariadb10.cnf
index 1912cd8e154..120bd7b7d00 100644
--- a/config/mycnf/mariadb10.cnf
+++ b/config/mycnf/mariadb10.cnf
@@ -39,3 +39,5 @@ slave_net_timeout = 60
character_set_server = utf8
collation_server = utf8_general_ci
+# All MariaDB instances should start in read-only mode
+read-only
diff --git a/config/mycnf/mysql57.cnf b/config/mycnf/mysql57.cnf
index 7a8c45a187c..44c462749a7 100644
--- a/config/mycnf/mysql57.cnf
+++ b/config/mycnf/mysql57.cnf
@@ -32,3 +32,7 @@ plugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisy
rpl_semi_sync_master_timeout = 1000000000000000000
rpl_semi_sync_master_wait_no_slave = 1
+# In order to protect against any errand GTIDs we will start the mysql instance
+# in super-read-only mode.
+super-read-only
+
diff --git a/config/mycnf/mysql80.cnf b/config/mycnf/mysql80.cnf
index 39fab576533..13447a7de0a 100644
--- a/config/mycnf/mysql80.cnf
+++ b/config/mycnf/mysql80.cnf
@@ -28,3 +28,7 @@ plugin-load = rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisy
loose_rpl_semi_sync_master_timeout = 1000000000000000000
loose_rpl_semi_sync_master_wait_no_slave = 1
+# In order to protect against any errand GTIDs we will start the mysql instance
+# in super-read-only mode.
+super-read-only
+
diff --git a/config/mycnf/test-suite.cnf b/config/mycnf/test-suite.cnf
index e57368a41db..e6d0992f6e6 100644
--- a/config/mycnf/test-suite.cnf
+++ b/config/mycnf/test-suite.cnf
@@ -23,3 +23,6 @@ sql_mode = STRICT_TRANS_TABLES
# set a short heartbeat interval in order to detect failures quickly
slave_net_timeout = 4
+# Disabling `super-read-only`. `test-suite` is mainly used for `vttestserver`. Since `vttestserver` uses a single MySQL for primary and replicas,
+# so it is not possible to run it with `super-read-only`. Therefore, we are disabling it.
+super-read-only = false
diff --git a/doc/releasenotes/17_0_0_summary.md b/doc/releasenotes/17_0_0_summary.md
index bacc18506e6..7b84459a42d 100644
--- a/doc/releasenotes/17_0_0_summary.md
+++ b/doc/releasenotes/17_0_0_summary.md
@@ -11,6 +11,9 @@
- [Detailed backup and restore stats](#detailed-backup-and-restore-stats)
- **[Deprecations and Deletions](#deprecations-and-deletions)**
- [Deprecated Stats](#deprecated-stats)
+ - **[VTTablet](#vttablet)**
+ - [VTTablet: Initializing all replicas with super_read_only](#vttablet-initialization)
+ - [Deprecated Flags](#deprecated-flags)
## Major Changes
@@ -197,4 +200,15 @@ These stats are deprecated in v17.
| Deprecated stat | Supported alternatives |
|-|-|
| `backup_duration_seconds` | `BackupDurationNanoseconds` |
-| `restore_duration_seconds` | `RestoreDurationNanoseconds` |
\ No newline at end of file
+| `restore_duration_seconds` | `RestoreDurationNanoseconds` |
+### VTTablet
+#### Initializing all replicas with super_read_only
+In order to prevent SUPER privileged users like `root` or `vt_dba` from producing errant GTIDs on replicas, all the replica MySQL servers are initialized with the MySQL
+global variable `super_read_only` value set to `ON`. During failovers, we set `super_read_only` to `OFF` for the promoted primary tablet. This will allow the
+primary to accept writes. All of the shard's tablets, except the current primary, will still have their global variable `super_read_only` set to `ON`. This will make sure that apart from
+MySQL replication no other component, offline system or operator can write directly to a replica.
+
+Reference PR for this change is [PR #12206](https://github.com/vitessio/vitess/pull/12206)
+
+#### Deprecated Flags
+The flag `use_super_read_only` is deprecated and will be removed in a later release.
diff --git a/go/cmd/vtbackup/vtbackup.go b/go/cmd/vtbackup/vtbackup.go
index 5ea15f728fd..8f5a39e8d0c 100644
--- a/go/cmd/vtbackup/vtbackup.go
+++ b/go/cmd/vtbackup/vtbackup.go
@@ -312,6 +312,19 @@ func takeBackup(ctx context.Context, topoServer *topo.Server, backupStorage back
if err := mysqld.ResetReplication(ctx); err != nil {
return fmt.Errorf("can't reset replication: %v", err)
}
+ // We need to switch off super_read_only before we create the database.
+ resetFunc, err := mysqld.SetSuperReadOnly(false)
+ if err != nil {
+ return fmt.Errorf("failed to disable super_read_only during backup: %v", err)
+ }
+ if resetFunc != nil {
+ defer func() {
+ err := resetFunc()
+ if err != nil {
+ log.Error("Failed to set super_read_only back to its original value during backup")
+ }
+ }()
+ }
cmd := mysqlctl.GenerateInitialBinlogEntry()
if err := mysqld.ExecuteSuperQueryList(ctx, []string{cmd}); err != nil {
return err
@@ -404,6 +417,8 @@ func takeBackup(ctx context.Context, topoServer *topo.Server, backupStorage back
return err
}
+ log.Infof("takeBackup: primary position is: %s", primaryPos.String())
+
// Remember the time when we fetched the primary position, not when we caught
// up to it, so the timestamp on our backup is honest (assuming we make it
// to the goal position).
diff --git a/go/flags/endtoend/vttablet.txt b/go/flags/endtoend/vttablet.txt
index ec9cdb1a277..0b0ab67a7aa 100644
--- a/go/flags/endtoend/vttablet.txt
+++ b/go/flags/endtoend/vttablet.txt
@@ -348,7 +348,6 @@ Usage of vttablet:
--tx_throttler_config string The configuration of the transaction throttler as a text formatted throttlerdata.Configuration protocol buffer message (default "target_replication_lag_sec: 2\nmax_replication_lag_sec: 10\ninitial_rate: 100\nmax_increase: 1\nemergency_decrease: 0.5\nmin_duration_between_increases_sec: 40\nmax_duration_between_increases_sec: 62\nmin_duration_between_decreases_sec: 20\nspread_backlog_across_sec: 20\nage_bad_rate_after_sec: 180\nbad_rate_increase: 0.1\nmax_rate_approach_threshold: 0.9\n")
--tx_throttler_healthcheck_cells strings A comma-separated list of cells. Only tabletservers running in these cells will be monitored for replication lag by the transaction throttler.
--unhealthy_threshold duration replication lag after which a replica is considered unhealthy (default 2h0m0s)
- --use_super_read_only Set super_read_only flag when performing planned failover.
--v Level log level for V logs
-v, --version print binary version
--vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging
diff --git a/go/mysql/fakesqldb/server.go b/go/mysql/fakesqldb/server.go
index 5b00b2c2e01..e40d092e244 100644
--- a/go/mysql/fakesqldb/server.go
+++ b/go/mysql/fakesqldb/server.go
@@ -783,3 +783,40 @@ func (db *DB) MockQueriesForTable(table string, result *sqltypes.Result) {
cols...,
))
}
+
+// GetRejectedQueryResult checks if we should reject the query.
+func (db *DB) GetRejectedQueryResult(key string) error {
+ if err, ok := db.rejectedData[key]; ok {
+ return err
+ }
+
+ return nil
+}
+
+// GetQueryResult checks for explicit queries add through AddQuery().
+func (db *DB) GetQueryResult(key string) *ExpectedResult {
+ result, ok := db.data[key]
+ if ok {
+ return result
+ }
+ return nil
+}
+
+// GetQueryPatternResult checks if a query matches any pattern previously added using AddQueryPattern().
+func (db *DB) GetQueryPatternResult(key string) (func(string), ExpectedResult, bool, error) {
+ for _, pat := range db.patternData {
+ if pat.expr.MatchString(key) {
+ userCallback, ok := db.queryPatternUserCallback[pat.expr]
+ if ok {
+ if pat.err != "" {
+ return userCallback, ExpectedResult{pat.result, nil}, true, fmt.Errorf(pat.err)
+ }
+ return userCallback, ExpectedResult{pat.result, nil}, true, nil
+ }
+
+ return nil, ExpectedResult{nil, nil}, false, nil
+ }
+ }
+
+ return nil, ExpectedResult{nil, nil}, false, nil
+}
diff --git a/go/test/endtoend/backup/pitr/backup_mysqlctld_pitr_test.go b/go/test/endtoend/backup/pitr/backup_mysqlctld_pitr_test.go
index f93dfa475b6..dfb869b4de2 100644
--- a/go/test/endtoend/backup/pitr/backup_mysqlctld_pitr_test.go
+++ b/go/test/endtoend/backup/pitr/backup_mysqlctld_pitr_test.go
@@ -138,9 +138,9 @@ func TestIncrementalBackupMysqlctld(t *testing.T) {
if tc.writeBeforeBackup {
backup.InsertRowOnPrimary(t, "")
}
- // we wait for 1 second because backups ar ewritten to a directory named after the current timestamp,
- // in 1 second resolution. We want to aoid two backups that have the same pathname. Realistically this
- // is only ever a problem in this endtoend test, not in production.
+ // we wait for 1 second because backups are written to a directory named after the current timestamp,
+ // in 1 second resolution. We want to avoid two backups that have the same pathname. Realistically this
+ // is only ever a problem in this end-to-end test, not in production.
// Also, we gie the replica a chance to catch up.
time.Sleep(1100 * time.Millisecond)
waitForReplica(t)
diff --git a/go/test/endtoend/backup/vtbackup/backup_only_test.go b/go/test/endtoend/backup/vtbackup/backup_only_test.go
index 3730a1fa586..408cc64a21b 100644
--- a/go/test/endtoend/backup/vtbackup/backup_only_test.go
+++ b/go/test/endtoend/backup/vtbackup/backup_only_test.go
@@ -18,6 +18,7 @@ package vtbackup
import (
"context"
+ "encoding/json"
"fmt"
"os"
"path"
@@ -56,18 +57,29 @@ func TestTabletInitialBackup(t *testing.T) {
// - list the backups, remove them
defer cluster.PanicHandler(t)
+ waitForReplicationToCatchup([]cluster.Vttablet{*replica1, *replica2})
+
vtBackup(t, true, false, false)
verifyBackupCount(t, shardKsName, 1)
// Initialize the tablets
initTablets(t, false, false)
- // Restore the Tablets
+ vtTabletVersion, err := cluster.GetMajorVersion("vttablet")
+ require.NoError(t, err)
+ // For all version at or above v17.0.0, each replica will start in super_read_only mode. Let's verify that is working correctly.
+ if vtTabletVersion >= 17 {
+ err := primary.VttabletProcess.CreateDB("testDB")
+ require.ErrorContains(t, err, "The MySQL server is running with the --super-read-only option so it cannot execute this statement")
+ err = replica1.VttabletProcess.CreateDB("testDB")
+ require.ErrorContains(t, err, "The MySQL server is running with the --super-read-only option so it cannot execute this statement")
+ }
+ // Restore the Tablet
restore(t, primary, "replica", "NOT_SERVING")
// Vitess expects that the user has set the database into ReadWrite mode before calling
// TabletExternallyReparented
- err := localCluster.VtctlclientProcess.ExecuteCommand(
+ err = localCluster.VtctlclientProcess.ExecuteCommand(
"SetReadWrite", primary.Alias)
require.Nil(t, err)
err = localCluster.VtctlclientProcess.ExecuteCommand(
@@ -254,19 +266,15 @@ func initTablets(t *testing.T, startTablet bool, initShardPrimary bool) {
func restore(t *testing.T, tablet *cluster.Vttablet, tabletType string, waitForState string) {
// Erase mysql/tablet dir, then start tablet with restore enabled.
-
log.Infof("restoring tablet %s", time.Now())
resetTabletDirectory(t, *tablet, true)
- err := tablet.VttabletProcess.CreateDB(keyspaceName)
- require.Nil(t, err)
-
// Start tablets
tablet.VttabletProcess.ExtraArgs = []string{"--db-credentials-file", dbCredentialFile}
tablet.VttabletProcess.TabletType = tabletType
tablet.VttabletProcess.ServingStatus = waitForState
tablet.VttabletProcess.SupportsBackup = true
- err = tablet.VttabletProcess.Setup()
+ err := tablet.VttabletProcess.Setup()
require.Nil(t, err)
}
@@ -294,6 +302,12 @@ func resetTabletDirectory(t *testing.T, tablet cluster.Vttablet, initMysql bool)
func tearDown(t *testing.T, initMysql bool) {
// reset replication
+ for _, db := range []string{"_vt", "vt_insert_test"} {
+ _, err := primary.VttabletProcess.QueryTablet(fmt.Sprintf("drop database if exists %s", db), keyspaceName, true)
+ require.Nil(t, err)
+ }
+ caughtUp := waitForReplicationToCatchup([]cluster.Vttablet{*replica1, *replica2})
+ require.True(t, caughtUp, "Timed out waiting for all replicas to catch up")
promoteCommands := "STOP SLAVE; RESET SLAVE ALL; RESET MASTER;"
disableSemiSyncCommands := "SET GLOBAL rpl_semi_sync_master_enabled = false; SET GLOBAL rpl_semi_sync_slave_enabled = false"
for _, tablet := range []cluster.Vttablet{*primary, *replica1, *replica2} {
@@ -301,10 +315,6 @@ func tearDown(t *testing.T, initMysql bool) {
require.Nil(t, err)
_, err = tablet.VttabletProcess.QueryTablet(disableSemiSyncCommands, keyspaceName, true)
require.Nil(t, err)
- for _, db := range []string{"_vt", "vt_insert_test"} {
- _, err = tablet.VttabletProcess.QueryTablet(fmt.Sprintf("drop database if exists %s", db), keyspaceName, true)
- require.Nil(t, err)
- }
}
// TODO: Ideally we should not be resetting the mysql.
@@ -367,3 +377,39 @@ func verifyDisableEnableRedoLogs(ctx context.Context, t *testing.T, mysqlSocket
}
}
}
+
+// This helper function wait for all replicas to catch-up the replication.
+// It does this by querying the status detail url of each replica and find the lag.
+func waitForReplicationToCatchup(tablets []cluster.Vttablet) bool {
+ endTime := time.Now().Add(time.Second * 30)
+ timeout := time.After(time.Until(endTime))
+ // key-value structure returned by status url.
+ type kv struct {
+ Key string
+ Class string
+ Value string
+ }
+ // defining a struct instance
+ var statuslst []kv
+ for {
+ select {
+ case <-timeout:
+ return false
+ default:
+ var replicaCount = 0
+ for _, tablet := range tablets {
+ status := tablet.VttabletProcess.GetStatusDetails()
+ json.Unmarshal([]byte(status), &statuslst)
+ for _, obj := range statuslst {
+ if obj.Key == "Replication Lag" && obj.Value == "0s" {
+ replicaCount++
+ }
+ }
+ if replicaCount == len(tablets) {
+ return true
+ }
+ }
+ time.Sleep(time.Second * 1)
+ }
+ }
+}
diff --git a/go/test/endtoend/backup/vtbackup/main_test.go b/go/test/endtoend/backup/vtbackup/main_test.go
index 069f83fbba5..36bfae123d8 100644
--- a/go/test/endtoend/backup/vtbackup/main_test.go
+++ b/go/test/endtoend/backup/vtbackup/main_test.go
@@ -25,6 +25,7 @@ import (
"testing"
"vitess.io/vitess/go/test/endtoend/cluster"
+ "vitess.io/vitess/go/test/endtoend/utils"
"vitess.io/vitess/go/vt/log"
)
@@ -86,11 +87,16 @@ func TestMain(m *testing.M) {
// Create a new init_db.sql file that sets up passwords for all users.
// Then we use a db-credentials-file with the passwords.
+ // TODO: We could have operated with empty password here. Create a separate test for --db-credentials-file functionality (@rsajwani)
dbCredentialFile = cluster.WriteDbCredentialToTmp(localCluster.TmpDirectory)
initDb, _ := os.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql"))
sql := string(initDb)
+ // The original init_db.sql does not have any passwords. Here we update the init file with passwords
+ sql, err = utils.GetInitDBSQL(sql, cluster.GetPasswordUpdateSQL(localCluster), "")
+ if err != nil {
+ return 1, err
+ }
newInitDBFile = path.Join(localCluster.TmpDirectory, "init_db_with_passwords.sql")
- sql = sql + cluster.GetPasswordUpdateSQL(localCluster)
err = os.WriteFile(newInitDBFile, []byte(sql), 0666)
if err != nil {
return 1, err
@@ -112,7 +118,11 @@ func TestMain(m *testing.M) {
tablet.VttabletProcess.ExtraArgs = commonTabletArg
tablet.VttabletProcess.SupportsBackup = true
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory)
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory)
+ if err != nil {
+ return 1, err
+ }
+ tablet.MysqlctlProcess = *mysqlctlProcess
tablet.MysqlctlProcess.InitDBFile = newInitDBFile
tablet.MysqlctlProcess.ExtraArgs = extraArgs
proc, err := tablet.MysqlctlProcess.StartProcess()
@@ -127,13 +137,6 @@ func TestMain(m *testing.M) {
}
}
- // Create database
- for _, tablet := range []cluster.Vttablet{*primary, *replica1} {
- if err := tablet.VttabletProcess.CreateDB(keyspaceName); err != nil {
- return 1, err
- }
- }
-
if localCluster.VtTabletMajorVersion >= 16 {
// If vttablets are any lower than version 16, then they are running the replication manager.
// Running VTOrc and replication manager sometimes creates the situation where VTOrc has set up semi-sync on the primary,
diff --git a/go/test/endtoend/backup/vtctlbackup/backup_utils.go b/go/test/endtoend/backup/vtctlbackup/backup_utils.go
index 87e854b7d64..f6afcc131ac 100644
--- a/go/test/endtoend/backup/vtctlbackup/backup_utils.go
+++ b/go/test/endtoend/backup/vtctlbackup/backup_utils.go
@@ -28,19 +28,19 @@ import (
"testing"
"time"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
"vitess.io/vitess/go/mysql"
"vitess.io/vitess/go/sqltypes"
+ "vitess.io/vitess/go/test/endtoend/cluster"
+ "vitess.io/vitess/go/test/endtoend/utils"
"vitess.io/vitess/go/textutil"
"vitess.io/vitess/go/vt/mysqlctl"
"vitess.io/vitess/go/vt/proto/topodata"
"vitess.io/vitess/go/vt/proto/vtrpc"
"vitess.io/vitess/go/vt/sqlparser"
"vitess.io/vitess/go/vt/vterrors"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- "vitess.io/vitess/go/test/endtoend/cluster"
)
// constants for test variants
@@ -115,11 +115,18 @@ func LaunchCluster(setupType int, streamMode string, stripes int, cDetails *Comp
}
shard := &localCluster.Keyspaces[0].Shards[0]
+ // Create a new init_db.sql file that sets up passwords for all users.
+ // Then we use a db-credentials-file with the passwords.
+ // TODO: We could have operated with empty password here. Create a separate test for --db-credentials-file functionality (@rsajwani)
dbCredentialFile = cluster.WriteDbCredentialToTmp(localCluster.TmpDirectory)
initDb, _ := os.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql"))
sql := string(initDb)
+ // The original init_db.sql does not have any passwords. Here we update the init file with passwords
+ sql, err = utils.GetInitDBSQL(sql, cluster.GetPasswordUpdateSQL(localCluster), "")
+ if err != nil {
+ return 1, err
+ }
newInitDBFile = path.Join(localCluster.TmpDirectory, "init_db_with_passwords.sql")
- sql = sql + cluster.GetPasswordUpdateSQL(localCluster)
err = os.WriteFile(newInitDBFile, []byte(sql), 0666)
if err != nil {
return 1, err
@@ -163,7 +170,11 @@ func LaunchCluster(setupType int, streamMode string, stripes int, cDetails *Comp
tablet.VttabletProcess.SupportsBackup = true
if setupType == Mysqlctld {
- tablet.MysqlctldProcess = *cluster.MysqlCtldProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory)
+ mysqlctldProcess, err := cluster.MysqlCtldProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory)
+ if err != nil {
+ return 1, err
+ }
+ tablet.MysqlctldProcess = *mysqlctldProcess
tablet.MysqlctldProcess.InitDBFile = newInitDBFile
tablet.MysqlctldProcess.ExtraArgs = extraArgs
tablet.MysqlctldProcess.Password = tablet.VttabletProcess.DbPassword
@@ -174,7 +185,11 @@ func LaunchCluster(setupType int, streamMode string, stripes int, cDetails *Comp
continue
}
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory)
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory)
+ if err != nil {
+ return 1, err
+ }
+ tablet.MysqlctlProcess = *mysqlctlProcess
tablet.MysqlctlProcess.InitDBFile = newInitDBFile
tablet.MysqlctlProcess.ExtraArgs = extraArgs
proc, err := tablet.MysqlctlProcess.StartProcess()
@@ -207,9 +222,6 @@ func LaunchCluster(setupType int, streamMode string, stripes int, cDetails *Comp
}
for _, tablet := range []cluster.Vttablet{*primary, *replica1} {
- if err := tablet.VttabletProcess.CreateDB(keyspaceName); err != nil {
- return 1, err
- }
if err := tablet.VttabletProcess.Setup(); err != nil {
return 1, err
}
@@ -679,8 +691,6 @@ func restartPrimaryAndReplica(t *testing.T) {
for _, tablet := range []*cluster.Vttablet{primary, replica1} {
err := localCluster.VtctlclientProcess.InitTablet(tablet, cell, keyspaceName, hostname, shardName)
require.Nil(t, err)
- err = tablet.VttabletProcess.CreateDB(keyspaceName)
- require.Nil(t, err)
err = tablet.VttabletProcess.Setup()
require.Nil(t, err)
}
diff --git a/go/test/endtoend/cellalias/cell_alias_test.go b/go/test/endtoend/cellalias/cell_alias_test.go
index 47f3108e6c7..3b6aa8c6bda 100644
--- a/go/test/endtoend/cellalias/cell_alias_test.go
+++ b/go/test/endtoend/cellalias/cell_alias_test.go
@@ -132,7 +132,11 @@ func TestMain(m *testing.M) {
var mysqlProcs []*exec.Cmd
for _, tablet := range []*cluster.Vttablet{shard1Primary, shard1Replica, shard1Rdonly, shard2Primary, shard2Replica, shard2Rdonly} {
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory)
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory)
+ if err != nil {
+ return 1, err
+ }
+ tablet.MysqlctlProcess = *mysqlctlProcess
tablet.VttabletProcess = cluster.VttabletProcessInstance(tablet.HTTPPort,
tablet.GrpcPort,
tablet.TabletUID,
diff --git a/go/test/endtoend/cluster/cluster_process.go b/go/test/endtoend/cluster/cluster_process.go
index 82c667f95ae..e7df056fa56 100644
--- a/go/test/endtoend/cluster/cluster_process.go
+++ b/go/test/endtoend/cluster/cluster_process.go
@@ -352,7 +352,11 @@ func (cluster *LocalProcessCluster) startKeyspace(keyspace Keyspace, shardNames
}
// Start Mysqlctl process
log.Infof("Starting mysqlctl for table uid %d, mysql port %d", tablet.TabletUID, tablet.MySQLPort)
- tablet.MysqlctlProcess = *MysqlCtlProcessInstanceOptionalInit(tablet.TabletUID, tablet.MySQLPort, cluster.TmpDirectory, !cluster.ReusingVTDATAROOT)
+ mysqlctlProcess, err := MysqlCtlProcessInstanceOptionalInit(tablet.TabletUID, tablet.MySQLPort, cluster.TmpDirectory, !cluster.ReusingVTDATAROOT)
+ if err != nil {
+ return err
+ }
+ tablet.MysqlctlProcess = *mysqlctlProcess
proc, err := tablet.MysqlctlProcess.StartProcess()
if err != nil {
log.Errorf("error starting mysqlctl process: %v, %v", tablet.MysqlctldProcess, err)
@@ -499,7 +503,11 @@ func (cluster *LocalProcessCluster) StartKeyspaceLegacy(keyspace Keyspace, shard
}
// Start Mysqlctl process
log.Infof("Starting mysqlctl for table uid %d, mysql port %d", tablet.TabletUID, tablet.MySQLPort)
- tablet.MysqlctlProcess = *MysqlCtlProcessInstanceOptionalInit(tablet.TabletUID, tablet.MySQLPort, cluster.TmpDirectory, !cluster.ReusingVTDATAROOT)
+ mysqlctlProcess, err := MysqlCtlProcessInstanceOptionalInit(tablet.TabletUID, tablet.MySQLPort, cluster.TmpDirectory, !cluster.ReusingVTDATAROOT)
+ if err != nil {
+ return err
+ }
+ tablet.MysqlctlProcess = *mysqlctlProcess
proc, err := tablet.MysqlctlProcess.StartProcess()
if err != nil {
log.Errorf("error starting mysqlctl process: %v, %v", tablet.MysqlctldProcess, err)
@@ -619,7 +627,11 @@ func (cluster *LocalProcessCluster) SetupCluster(keyspace *Keyspace, shards []Sh
for _, shard := range shards {
for _, tablet := range shard.Vttablets {
// Setup MysqlctlProcess
- tablet.MysqlctlProcess = *MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, cluster.TmpDirectory)
+ mysqlctlProcess, err := MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, cluster.TmpDirectory)
+ if err != nil {
+ return err
+ }
+ tablet.MysqlctlProcess = *mysqlctlProcess
// Setup VttabletProcess
tablet.VttabletProcess = VttabletProcessInstance(
tablet.HTTPPort,
@@ -703,7 +715,10 @@ func NewBareCluster(cell string, hostname string) *LocalProcessCluster {
// path/to/whatever exists
cluster.ReusingVTDATAROOT = true
} else {
- _ = createDirectory(cluster.CurrentVTDATAROOT, 0700)
+ err = createDirectory(cluster.CurrentVTDATAROOT, 0700)
+ if err != nil {
+ log.Fatal(err)
+ }
}
_ = os.Setenv("VTDATAROOT", cluster.CurrentVTDATAROOT)
log.Infof("Created cluster on %s. ReusingVTDATAROOT=%v", cluster.CurrentVTDATAROOT, cluster.ReusingVTDATAROOT)
diff --git a/go/test/endtoend/cluster/cluster_util.go b/go/test/endtoend/cluster/cluster_util.go
index ea2dd0d7e20..6ebd8806928 100644
--- a/go/test/endtoend/cluster/cluster_util.go
+++ b/go/test/endtoend/cluster/cluster_util.go
@@ -306,6 +306,7 @@ func GetPasswordUpdateSQL(localCluster *LocalProcessCluster) string {
SET PASSWORD FOR 'vt_allprivs'@'localhost' = 'VtAllprivsPass';
SET PASSWORD FOR 'vt_repl'@'%' = 'VtReplPass';
SET PASSWORD FOR 'vt_filtered'@'localhost' = 'VtFilteredPass';
+ SET PASSWORD FOR 'vt_appdebug'@'localhost' = 'VtDebugPass';
FLUSH PRIVILEGES;
`
return pwdChangeCmd
diff --git a/go/test/endtoend/cluster/mysqlctl_process.go b/go/test/endtoend/cluster/mysqlctl_process.go
index eafc8f6b98f..7c8f2607841 100644
--- a/go/test/endtoend/cluster/mysqlctl_process.go
+++ b/go/test/endtoend/cluster/mysqlctl_process.go
@@ -30,6 +30,7 @@ import (
"vitess.io/vitess/go/mysql"
"vitess.io/vitess/go/vt/log"
+ "vitess.io/vitess/go/vt/mysqlctl"
"vitess.io/vitess/go/vt/tlstest"
)
@@ -235,36 +236,65 @@ func (mysqlctl *MysqlctlProcess) Connect(ctx context.Context, username string) (
// MysqlCtlProcessInstanceOptionalInit returns a Mysqlctl handle for mysqlctl process
// configured with the given Config.
-func MysqlCtlProcessInstanceOptionalInit(tabletUID int, mySQLPort int, tmpDirectory string, initMySQL bool) *MysqlctlProcess {
+func MysqlCtlProcessInstanceOptionalInit(tabletUID int, mySQLPort int, tmpDirectory string, initMySQL bool) (*MysqlctlProcess, error) {
+ initFile, err := getInitDBFileUsed()
+ if err != nil {
+ return nil, err
+ }
mysqlctl := &MysqlctlProcess{
Name: "mysqlctl",
Binary: "mysqlctl",
LogDirectory: tmpDirectory,
- InitDBFile: path.Join(os.Getenv("VTROOT"), "/config/init_db.sql"),
+ InitDBFile: initFile,
}
mysqlctl.MySQLPort = mySQLPort
mysqlctl.TabletUID = tabletUID
mysqlctl.InitMysql = initMySQL
mysqlctl.SecureTransport = false
- return mysqlctl
+ return mysqlctl, nil
+}
+
+func getInitDBFileUsed() (string, error) {
+ versionStr, err := mysqlctl.GetVersionString()
+ if err != nil {
+ return "", err
+ }
+ flavor, _, err := mysqlctl.ParseVersionString(versionStr)
+ if err != nil {
+ return "", err
+ }
+ if flavor == mysqlctl.FlavorMySQL || flavor == mysqlctl.FlavorPercona {
+ return path.Join(os.Getenv("VTROOT"), "/config/init_db.sql"), nil
+ }
+ // Non-MySQL instances for example MariaDB, will use init_testserver_db.sql which does not contain super_read_only global variable.
+ // Even though MariaDB support is deprecated (https://github.com/vitessio/vitess/issues/9518) but we still support migration scenario.
+ return path.Join(os.Getenv("VTROOT"), "go/test/endtoend/vreplication/testdata/config/init_testserver_db.sql"), nil
}
// MysqlCtlProcessInstance returns a Mysqlctl handle for mysqlctl process
// configured with the given Config.
-func MysqlCtlProcessInstance(tabletUID int, mySQLPort int, tmpDirectory string) *MysqlctlProcess {
+func MysqlCtlProcessInstance(tabletUID int, mySQLPort int, tmpDirectory string) (*MysqlctlProcess, error) {
return MysqlCtlProcessInstanceOptionalInit(tabletUID, mySQLPort, tmpDirectory, true)
}
// StartMySQL starts mysqlctl process
func StartMySQL(ctx context.Context, tablet *Vttablet, username string, tmpDirectory string) error {
- tablet.MysqlctlProcess = *MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, tmpDirectory)
+ mysqlctlProcess, err := MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, tmpDirectory)
+ if err != nil {
+ return err
+ }
+ tablet.MysqlctlProcess = *mysqlctlProcess
return tablet.MysqlctlProcess.Start()
}
// StartMySQLAndGetConnection create a connection to tablet mysql
func StartMySQLAndGetConnection(ctx context.Context, tablet *Vttablet, username string, tmpDirectory string) (*mysql.Conn, error) {
- tablet.MysqlctlProcess = *MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, tmpDirectory)
- err := tablet.MysqlctlProcess.Start()
+ mysqlctlProcess, err := MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, tmpDirectory)
+ if err != nil {
+ return nil, err
+ }
+ tablet.MysqlctlProcess = *mysqlctlProcess
+ err = tablet.MysqlctlProcess.Start()
if err != nil {
return nil, err
}
diff --git a/go/test/endtoend/cluster/mysqlctld_process.go b/go/test/endtoend/cluster/mysqlctld_process.go
index d71f2e3b1c8..673870e62bb 100644
--- a/go/test/endtoend/cluster/mysqlctld_process.go
+++ b/go/test/endtoend/cluster/mysqlctld_process.go
@@ -144,17 +144,21 @@ func (mysqlctld *MysqlctldProcess) CleanupFiles(tabletUID int) {
// MysqlCtldProcessInstance returns a Mysqlctld handle for mysqlctld process
// configured with the given Config.
-func MysqlCtldProcessInstance(tabletUID int, mySQLPort int, tmpDirectory string) *MysqlctldProcess {
+func MysqlCtldProcessInstance(tabletUID int, mySQLPort int, tmpDirectory string) (*MysqlctldProcess, error) {
+ initFile, err := getInitDBFileUsed()
+ if err != nil {
+ return nil, err
+ }
mysqlctld := &MysqlctldProcess{
Name: "mysqlctld",
Binary: "mysqlctld",
LogDirectory: tmpDirectory,
- InitDBFile: path.Join(os.Getenv("VTROOT"), "/config/init_db.sql"),
+ InitDBFile: initFile,
}
mysqlctld.MySQLPort = mySQLPort
mysqlctld.TabletUID = tabletUID
mysqlctld.InitMysql = true
- return mysqlctld
+ return mysqlctld, nil
}
// IsHealthy gives the health status of mysql.
diff --git a/go/test/endtoend/cluster/vtbackup_process.go b/go/test/endtoend/cluster/vtbackup_process.go
index b7beed67936..be75026bf0d 100644
--- a/go/test/endtoend/cluster/vtbackup_process.go
+++ b/go/test/endtoend/cluster/vtbackup_process.go
@@ -54,7 +54,6 @@ type VtbackupProcess struct {
// Setup starts vtbackup process with required arguements
func (vtbackup *VtbackupProcess) Setup() (err error) {
-
vtbackup.proc = exec.Command(
vtbackup.Binary,
"--topo_implementation", vtbackup.CommonArg.TopoImplementation,
diff --git a/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go b/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go
index 8900db58ce1..d7de8052599 100644
--- a/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go
+++ b/go/test/endtoend/encryption/encryptedreplication/encrypted_replication_test.go
@@ -142,7 +142,11 @@ func initializeCluster(t *testing.T) (int, error) {
tablet := clusterInstance.NewVttabletInstance("replica", tabletUID, cell)
// Start Mysqlctl process
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ if err != nil {
+ return 1, err
+ }
+ tablet.MysqlctlProcess = *mysqlctlProcess
proc, err := tablet.MysqlctlProcess.StartProcess()
if err != nil {
return 1, err
diff --git a/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go b/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go
index b0d0a507a43..e81b019d273 100644
--- a/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go
+++ b/go/test/endtoend/encryption/encryptedtransport/encrypted_transport_test.go
@@ -360,7 +360,11 @@ func clusterSetUp(t *testing.T) (int, error) {
tablet := clusterInstance.NewVttabletInstance("replica", 0, cell)
// Start Mysqlctl process
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ if err != nil {
+ return 1, err
+ }
+ tablet.MysqlctlProcess = *mysqlctlProcess
proc, err := tablet.MysqlctlProcess.StartProcess()
if err != nil {
return 1, err
diff --git a/go/test/endtoend/mysqlctl/mysqlctl_test.go b/go/test/endtoend/mysqlctl/mysqlctl_test.go
index ec54ffb91d5..5b348cd4eb5 100644
--- a/go/test/endtoend/mysqlctl/mysqlctl_test.go
+++ b/go/test/endtoend/mysqlctl/mysqlctl_test.go
@@ -96,7 +96,11 @@ func initCluster(shardNames []string, totalTabletsRequired int) {
tablet.Type = "primary"
}
// Start Mysqlctl process
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ if err != nil {
+ return
+ }
+ tablet.MysqlctlProcess = *mysqlctlProcess
proc, err := tablet.MysqlctlProcess.StartProcess()
if err != nil {
return
diff --git a/go/test/endtoend/mysqlctld/mysqlctld_test.go b/go/test/endtoend/mysqlctld/mysqlctld_test.go
index 28d2bb71ced..89e35aea31d 100644
--- a/go/test/endtoend/mysqlctld/mysqlctld_test.go
+++ b/go/test/endtoend/mysqlctld/mysqlctld_test.go
@@ -96,8 +96,12 @@ func initCluster(shardNames []string, totalTabletsRequired int) error {
tablet.Type = "primary"
}
// Start Mysqlctld process
- tablet.MysqlctldProcess = *cluster.MysqlCtldProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
- err := tablet.MysqlctldProcess.Start()
+ mysqlctldProcess, err := cluster.MysqlCtldProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ if err != nil {
+ return err
+ }
+ tablet.MysqlctldProcess = *mysqlctldProcess
+ err = tablet.MysqlctldProcess.Start()
if err != nil {
return err
}
diff --git a/go/test/endtoend/recovery/pitr/shardedpitr_test.go b/go/test/endtoend/recovery/pitr/shardedpitr_test.go
index 5a7ae3e1399..ab54693993a 100644
--- a/go/test/endtoend/recovery/pitr/shardedpitr_test.go
+++ b/go/test/endtoend/recovery/pitr/shardedpitr_test.go
@@ -19,7 +19,9 @@ package pitr
import (
"context"
"fmt"
+ "os"
"os/exec"
+ "path"
"testing"
"time"
@@ -29,6 +31,7 @@ import (
"vitess.io/vitess/go/mysql"
"vitess.io/vitess/go/test/endtoend/cluster"
+ "vitess.io/vitess/go/test/endtoend/utils"
"vitess.io/vitess/go/vt/log"
)
@@ -51,20 +54,22 @@ var (
shard1Replica1 *cluster.Vttablet
shard1Replica2 *cluster.Vttablet
- cell = "zone1"
- hostname = "localhost"
- binlogHost = "127.0.0.1"
- keyspaceName = "ks"
- restoreKS1Name = "restoreks1"
- restoreKS2Name = "restoreks2"
- restoreKS3Name = "restoreks3"
- shardName = "0"
- shard0Name = "-80"
- shard1Name = "80-"
- dbName = "vt_ks"
- mysqlUserName = "vt_dba"
- mysqlPassword = "password"
- vSchema = `{
+ cell = "zone1"
+ hostname = "localhost"
+ binlogHost = "127.0.0.1"
+ keyspaceName = "ks"
+ restoreKS1Name = "restoreks1"
+ restoreKS2Name = "restoreks2"
+ restoreKS3Name = "restoreks3"
+ shardName = "0"
+ shard0Name = "-80"
+ shard1Name = "80-"
+ dbName = "vt_ks"
+ mysqlUserName = "vt_dba"
+ mysqlPassword = "VtDbaPass"
+ dbCredentialFile = ""
+ initDBFileWithPassword = ""
+ vSchema = `{
"sharded": true,
"vindexes": {
"hash_index": {
@@ -408,6 +413,10 @@ func initializeCluster(t *testing.T) {
shard0.Vttablets = []*cluster.Vttablet{shard0Primary, shard0Replica1, shard0Replica2}
shard1.Vttablets = []*cluster.Vttablet{shard1Primary, shard1Replica1, shard1Replica2}
+ dbCredentialFile = cluster.WriteDbCredentialToTmp(clusterInstance.TmpDirectory)
+ extraArgs := []string{"--db-credentials-file", dbCredentialFile}
+ commonTabletArg = append(commonTabletArg, "--db-credentials-file", dbCredentialFile)
+
clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs, commonTabletArg...)
clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs, "--restore_from_backup")
@@ -416,10 +425,23 @@ func initializeCluster(t *testing.T) {
vtctldClientProcess := cluster.VtctldClientProcessInstance("localhost", clusterInstance.VtctldProcess.GrpcPort, clusterInstance.TmpDirectory)
out, err := vtctldClientProcess.ExecuteCommandWithOutput("SetKeyspaceDurabilityPolicy", keyspaceName, "--durability-policy=semi_sync")
require.NoError(t, err, out)
+
+ initDb, _ := os.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql"))
+ sql := string(initDb)
+ // The original init_db.sql does not have any passwords. Here we update the init file with passwords
+ sql, err = utils.GetInitDBSQL(sql, cluster.GetPasswordUpdateSQL(clusterInstance), "")
+ require.NoError(t, err, "expected to load init_db file")
+ initDBFileWithPassword = path.Join(clusterInstance.TmpDirectory, "init_db_with_passwords.sql")
+ err = os.WriteFile(initDBFileWithPassword, []byte(sql), 0660)
+ require.NoError(t, err, "expected to load init_db file")
+
// Start MySql
var mysqlCtlProcessList []*exec.Cmd
for _, shard := range clusterInstance.Keyspaces[0].Shards {
for _, tablet := range shard.Vttablets {
+ tablet.MysqlctlProcess.InitDBFile = initDBFileWithPassword
+ tablet.VttabletProcess.DbPassword = mysqlPassword
+ tablet.MysqlctlProcess.ExtraArgs = extraArgs
proc, err := tablet.MysqlctlProcess.StartProcess()
require.NoError(t, err)
mysqlCtlProcessList = append(mysqlCtlProcessList, proc)
@@ -432,21 +454,8 @@ func initializeCluster(t *testing.T) {
require.NoError(t, err)
}
- queryCmds := []string{
- fmt.Sprintf("CREATE USER '%s'@'%%' IDENTIFIED BY '%s';", mysqlUserName, mysqlPassword),
- fmt.Sprintf("GRANT ALL ON *.* TO '%s'@'%%';", mysqlUserName),
- fmt.Sprintf("GRANT GRANT OPTION ON *.* TO '%s'@'%%';", mysqlUserName),
- fmt.Sprintf("create database %s;", "vt_ks"),
- "FLUSH PRIVILEGES;",
- }
-
for _, shard := range clusterInstance.Keyspaces[0].Shards {
for _, tablet := range shard.Vttablets {
- for _, query := range queryCmds {
- _, err = tablet.VttabletProcess.QueryTablet(query, keyspace.Name, false)
- require.NoError(t, err)
- }
-
err = tablet.VttabletProcess.Setup()
require.NoError(t, err)
}
@@ -509,8 +518,14 @@ func testTabletRecovery(t *testing.T, binlogServer *binLogServer, lookupTimeout,
}
func launchRecoveryTablet(t *testing.T, tablet *cluster.Vttablet, binlogServer *binLogServer, lookupTimeout, restoreKeyspaceName, shardName string) {
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
- err := tablet.MysqlctlProcess.Start()
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ require.NoError(t, err)
+ tablet.MysqlctlProcess = *mysqlctlProcess
+ extraArgs := []string{"--db-credentials-file", dbCredentialFile}
+ tablet.MysqlctlProcess.InitDBFile = initDBFileWithPassword
+ tablet.VttabletProcess.DbPassword = mysqlPassword
+ tablet.MysqlctlProcess.ExtraArgs = extraArgs
+ err = tablet.MysqlctlProcess.Start()
require.NoError(t, err)
tablet.VttabletProcess = cluster.VttabletProcessInstance(
@@ -550,6 +565,7 @@ func launchRecoveryTablet(t *testing.T, tablet *cluster.Vttablet, binlogServer *
"--lock_tables_timeout", "5s",
"--watch_replication_stream",
"--serving_state_grace_period", "1s",
+ "--db-credentials-file", dbCredentialFile,
}
tablet.VttabletProcess.ServingStatus = ""
diff --git a/go/test/endtoend/recovery/unshardedrecovery/recovery.go b/go/test/endtoend/recovery/unshardedrecovery/recovery.go
index 1ab9f1647ca..eff072f651d 100644
--- a/go/test/endtoend/recovery/unshardedrecovery/recovery.go
+++ b/go/test/endtoend/recovery/unshardedrecovery/recovery.go
@@ -30,6 +30,7 @@ import (
"vitess.io/vitess/go/test/endtoend/cluster"
"vitess.io/vitess/go/test/endtoend/recovery"
+ "vitess.io/vitess/go/test/endtoend/utils"
"vitess.io/vitess/go/vt/log"
"vitess.io/vitess/go/vt/vtgate/vtgateconn"
)
@@ -91,16 +92,19 @@ func TestMainImpl(m *testing.M) {
}
localCluster.Keyspaces = append(localCluster.Keyspaces, *keyspace)
+ // Create a new init_db.sql file that sets up passwords for all users.
+ // Then we use a db-credentials-file with the passwords.
+ // TODO: We could have operated with empty password here. Create a separate test for --db-credentials-file functionality (@rsajwani)
dbCredentialFile = cluster.WriteDbCredentialToTmp(localCluster.TmpDirectory)
initDb, _ := os.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql"))
sql := string(initDb)
+ // The original init_db.sql does not have any passwords. Here we update the init file with passwords
+ oldAlterTableMode := `SET GLOBAL old_alter_table = ON;`
+ sql, err = utils.GetInitDBSQL(sql, cluster.GetPasswordUpdateSQL(localCluster), oldAlterTableMode)
+ if err != nil {
+ return 1, err
+ }
newInitDBFile = path.Join(localCluster.TmpDirectory, "init_db_with_passwords.sql")
- sql = sql + cluster.GetPasswordUpdateSQL(localCluster)
- // https://github.com/vitessio/vitess/issues/8315
- oldAlterTableMode := `
-SET GLOBAL old_alter_table = ON;
-`
- sql = sql + oldAlterTableMode
os.WriteFile(newInitDBFile, []byte(sql), 0666)
extraArgs := []string{"--db-credentials-file", dbCredentialFile}
@@ -125,7 +129,11 @@ SET GLOBAL old_alter_table = ON;
}
tablet.VttabletProcess.SupportsBackup = true
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory)
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, localCluster.TmpDirectory)
+ if err != nil {
+ return 1, err
+ }
+ tablet.MysqlctlProcess = *mysqlctlProcess
tablet.MysqlctlProcess.InitDBFile = newInitDBFile
tablet.MysqlctlProcess.ExtraArgs = extraArgs
proc, err := tablet.MysqlctlProcess.StartProcess()
diff --git a/go/test/endtoend/reparent/plannedreparent/reparent_test.go b/go/test/endtoend/reparent/plannedreparent/reparent_test.go
index de7e6a0368b..a46f3990cdf 100644
--- a/go/test/endtoend/reparent/plannedreparent/reparent_test.go
+++ b/go/test/endtoend/reparent/plannedreparent/reparent_test.go
@@ -427,6 +427,14 @@ func TestFullStatus(t *testing.T) {
assert.Contains(t, primaryStatus.PrimaryStatus.String(), "vt-0000000101-bin")
assert.Equal(t, primaryStatus.GtidPurged, "MySQL56/")
assert.False(t, primaryStatus.ReadOnly)
+ vtTabletVersion, err := cluster.GetMajorVersion("vttablet")
+ require.NoError(t, err)
+ vtcltlVersion, err := cluster.GetMajorVersion("vtctl")
+ require.NoError(t, err)
+ // For all version at or above v17.0.0, each replica will start in super_read_only mode.
+ if vtTabletVersion >= 17 && vtcltlVersion >= 17 {
+ assert.False(t, primaryStatus.SuperReadOnly)
+ }
assert.True(t, primaryStatus.SemiSyncPrimaryEnabled)
assert.True(t, primaryStatus.SemiSyncReplicaEnabled)
assert.True(t, primaryStatus.SemiSyncPrimaryStatus)
@@ -479,6 +487,10 @@ func TestFullStatus(t *testing.T) {
assert.Contains(t, replicaStatus.PrimaryStatus.String(), "vt-0000000102-bin")
assert.Equal(t, replicaStatus.GtidPurged, "MySQL56/")
assert.True(t, replicaStatus.ReadOnly)
+ // For all version at or above v17.0.0, each replica will start in super_read_only mode.
+ if vtTabletVersion >= 17 && vtcltlVersion >= 17 {
+ assert.True(t, replicaStatus.SuperReadOnly)
+ }
assert.False(t, replicaStatus.SemiSyncPrimaryEnabled)
assert.True(t, replicaStatus.SemiSyncReplicaEnabled)
assert.False(t, replicaStatus.SemiSyncPrimaryStatus)
diff --git a/go/test/endtoend/reparent/utils/utils.go b/go/test/endtoend/reparent/utils/utils.go
index c2ab9d48306..1ec834f1cc7 100644
--- a/go/test/endtoend/reparent/utils/utils.go
+++ b/go/test/endtoend/reparent/utils/utils.go
@@ -193,7 +193,9 @@ func StartNewVTTablet(t *testing.T, clusterInstance *cluster.LocalProcessCluster
shard := keyspace.Shards[0]
// Setup MysqlctlProcess
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ require.NoError(t, err)
+ tablet.MysqlctlProcess = *mysqlctlProcess
// Setup VttabletProcess
tablet.VttabletProcess = cluster.VttabletProcessInstance(
tablet.HTTPPort,
diff --git a/go/test/endtoend/sharded/sharded_keyspace_test.go b/go/test/endtoend/sharded/sharded_keyspace_test.go
index d5f5e5b2255..0298da0bdb2 100644
--- a/go/test/endtoend/sharded/sharded_keyspace_test.go
+++ b/go/test/endtoend/sharded/sharded_keyspace_test.go
@@ -215,7 +215,11 @@ func initCluster(shardNames []string, totalTabletsRequired int) {
tablet.Type = "primary"
}
// Start Mysqlctl process
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ if err != nil {
+ return
+ }
+ tablet.MysqlctlProcess = *mysqlctlProcess
proc, err := tablet.MysqlctlProcess.StartProcess()
if err != nil {
return
diff --git a/go/test/endtoend/tabletmanager/tablet_health_test.go b/go/test/endtoend/tabletmanager/tablet_health_test.go
index 19359406607..aaa5719cdcd 100644
--- a/go/test/endtoend/tabletmanager/tablet_health_test.go
+++ b/go/test/endtoend/tabletmanager/tablet_health_test.go
@@ -103,9 +103,6 @@ func TestHealthCheck(t *testing.T) {
defer replicaConn.Close()
- // Create database in mysql
- utils.Exec(t, replicaConn, fmt.Sprintf("create database vt_%s", keyspaceName))
-
// start vttablet process, should be in SERVING state as we already have a primary
err = clusterInstance.StartVttablet(rTablet, "SERVING", false, cell, keyspaceName, hostname, shardName)
require.NoError(t, err)
diff --git a/go/test/endtoend/tabletmanager/tablet_test.go b/go/test/endtoend/tabletmanager/tablet_test.go
index 3c597e97981..643785dcd89 100644
--- a/go/test/endtoend/tabletmanager/tablet_test.go
+++ b/go/test/endtoend/tabletmanager/tablet_test.go
@@ -34,8 +34,11 @@ func TestEnsureDB(t *testing.T) {
// Create new tablet
tablet := clusterInstance.NewVttabletInstance("replica", 0, "")
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
- err := tablet.MysqlctlProcess.Start()
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ require.NoError(t, err)
+
+ tablet.MysqlctlProcess = *mysqlctlProcess
+ err = tablet.MysqlctlProcess.Start()
require.NoError(t, err)
log.Info(fmt.Sprintf("Started vttablet %v", tablet))
@@ -67,8 +70,10 @@ func TestResetReplicationParameters(t *testing.T) {
// Create new tablet
tablet := clusterInstance.NewVttabletInstance("replica", 0, "")
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
- err := tablet.MysqlctlProcess.Start()
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ require.NoError(t, err)
+ tablet.MysqlctlProcess = *mysqlctlProcess
+ err = tablet.MysqlctlProcess.Start()
require.NoError(t, err)
log.Info(fmt.Sprintf("Started vttablet %v", tablet))
diff --git a/go/test/endtoend/topoconncache/main_test.go b/go/test/endtoend/topoconncache/main_test.go
index 2a074e8428a..7cfea8839b0 100644
--- a/go/test/endtoend/topoconncache/main_test.go
+++ b/go/test/endtoend/topoconncache/main_test.go
@@ -140,7 +140,11 @@ func TestMain(m *testing.M) {
var mysqlProcs []*exec.Cmd
for _, tablet := range []*cluster.Vttablet{shard1Primary, shard1Replica, shard1Rdonly, shard2Primary, shard2Replica, shard2Rdonly} {
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ if err != nil {
+ return 1, err
+ }
+ tablet.MysqlctlProcess = *mysqlctlProcess
tablet.VttabletProcess = cluster.VttabletProcessInstance(tablet.HTTPPort,
tablet.GrpcPort,
tablet.TabletUID,
diff --git a/go/test/endtoend/topoconncache/topo_conn_cache_test.go b/go/test/endtoend/topoconncache/topo_conn_cache_test.go
index 02f14a7304d..504ca218047 100644
--- a/go/test/endtoend/topoconncache/topo_conn_cache_test.go
+++ b/go/test/endtoend/topoconncache/topo_conn_cache_test.go
@@ -136,7 +136,9 @@ func addCellback(t *testing.T) {
// create sql process for vttablets
var mysqlProcs []*exec.Cmd
for _, tablet := range []*cluster.Vttablet{shard1Replica, shard1Rdonly, shard2Replica, shard2Rdonly} {
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
+ require.NoError(t, err)
+ tablet.MysqlctlProcess = *mysqlctlProcess
tablet.VttabletProcess = cluster.VttabletProcessInstance(tablet.HTTPPort,
tablet.GrpcPort,
tablet.TabletUID,
diff --git a/go/test/endtoend/utils/mysql.go b/go/test/endtoend/utils/mysql.go
index a73160503cd..fc1a62d6736 100644
--- a/go/test/endtoend/utils/mysql.go
+++ b/go/test/endtoend/utils/mysql.go
@@ -40,7 +40,8 @@ import (
// The mysql.ConnParams to connect to the new database is returned, along with a function to
// teardown the database.
func NewMySQL(cluster *cluster.LocalProcessCluster, dbName string, schemaSQL ...string) (mysql.ConnParams, func(), error) {
- return NewMySQLWithDetails(cluster.GetAndReservePort(), cluster.Hostname, dbName, schemaSQL...)
+ mysqlParam, _, closer, error := NewMySQLWithMysqld(cluster.GetAndReservePort(), cluster.Hostname, dbName, schemaSQL...)
+ return mysqlParam, closer, error
}
// CreateMysqldAndMycnf returns a Mysqld and a Mycnf object to use for working with a MySQL
@@ -60,24 +61,24 @@ func CreateMysqldAndMycnf(tabletUID uint32, mysqlSocket string, mysqlPort int) (
return mysqlctl.NewMysqld(&cfg), mycnf, nil
}
-func NewMySQLWithDetails(port int, hostname, dbName string, schemaSQL ...string) (mysql.ConnParams, func(), error) {
+func NewMySQLWithMysqld(port int, hostname, dbName string, schemaSQL ...string) (mysql.ConnParams, *mysqlctl.Mysqld, func(), error) {
mysqlDir, err := createMySQLDir()
if err != nil {
- return mysql.ConnParams{}, nil, err
+ return mysql.ConnParams{}, nil, nil, err
}
initMySQLFile, err := createInitSQLFile(mysqlDir, dbName)
if err != nil {
- return mysql.ConnParams{}, nil, err
+ return mysql.ConnParams{}, nil, nil, err
}
mysqlPort := port
mysqld, mycnf, err := CreateMysqldAndMycnf(0, "", mysqlPort)
if err != nil {
- return mysql.ConnParams{}, nil, err
+ return mysql.ConnParams{}, nil, nil, err
}
err = initMysqld(mysqld, mycnf, initMySQLFile)
if err != nil {
- return mysql.ConnParams{}, nil, err
+ return mysql.ConnParams{}, nil, nil, err
}
params := mysql.ConnParams{
@@ -89,10 +90,10 @@ func NewMySQLWithDetails(port int, hostname, dbName string, schemaSQL ...string)
for _, sql := range schemaSQL {
err = prepareMySQLWithSchema(params, sql)
if err != nil {
- return mysql.ConnParams{}, nil, err
+ return mysql.ConnParams{}, nil, nil, err
}
}
- return params, func() {
+ return params, mysqld, func() {
ctx := context.Background()
_ = mysqld.Teardown(ctx, mycnf, true)
}, nil
@@ -114,7 +115,10 @@ func createInitSQLFile(mysqlDir, ksName string) (string, error) {
return "", err
}
defer f.Close()
-
+ _, err = f.WriteString("SET GLOBAL super_read_only='OFF';")
+ if err != nil {
+ return "", err
+ }
_, err = f.WriteString(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s;", ksName))
if err != nil {
return "", err
diff --git a/go/test/endtoend/utils/mysql_test.go b/go/test/endtoend/utils/mysql_test.go
index d2816cb1227..de9db23dab1 100644
--- a/go/test/endtoend/utils/mysql_test.go
+++ b/go/test/endtoend/utils/mysql_test.go
@@ -22,15 +22,18 @@ import (
"os"
"testing"
+ "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"vitess.io/vitess/go/mysql"
"vitess.io/vitess/go/test/endtoend/cluster"
+ "vitess.io/vitess/go/vt/mysqlctl"
)
var (
clusterInstance *cluster.LocalProcessCluster
mysqlParams mysql.ConnParams
+ mysqld *mysqlctl.Mysqld
keyspaceName = "ks"
cell = "test"
schemaSQL = `create table t1(
@@ -48,13 +51,14 @@ func TestMain(m *testing.M) {
clusterInstance = cluster.NewCluster(cell, "localhost")
defer clusterInstance.Teardown()
- conn, closer, err := NewMySQL(clusterInstance, keyspaceName, schemaSQL)
+ var closer func()
+ var err error
+ mysqlParams, mysqld, closer, err = NewMySQLWithMysqld(clusterInstance.GetAndReservePort(), clusterInstance.Hostname, keyspaceName, schemaSQL)
if err != nil {
fmt.Println(err)
return 1
}
defer closer()
- mysqlParams = conn
return m.Run()
}()
os.Exit(exitCode)
@@ -64,9 +68,58 @@ func TestCreateMySQL(t *testing.T) {
ctx := context.Background()
conn, err := mysql.Connect(ctx, &mysqlParams)
require.NoError(t, err)
-
AssertMatches(t, conn, "show databases;", `[[VARCHAR("information_schema")] [VARCHAR("ks")] [VARCHAR("mysql")] [VARCHAR("performance_schema")] [VARCHAR("sys")]]`)
AssertMatches(t, conn, "show tables;", `[[VARCHAR("t1")]]`)
Exec(t, conn, "insert into t1(id1, id2, id3) values (1, 1, 1), (2, 2, 2), (3, 3, 3)")
AssertMatches(t, conn, "select * from t1;", `[[INT64(1) INT64(1) INT64(1)] [INT64(2) INT64(2) INT64(2)] [INT64(3) INT64(3) INT64(3)]]`)
}
+
+func TestSetSuperReadOnlyMySQL(t *testing.T) {
+ require.NotNil(t, mysqld)
+ isSuperReadOnly, _ := mysqld.IsSuperReadOnly()
+ assert.False(t, isSuperReadOnly, "super_read_only should be set to False")
+ retFunc1, err := mysqld.SetSuperReadOnly(true)
+ assert.NotNil(t, retFunc1, "SetSuperReadOnly is supposed to return a defer function")
+ assert.NoError(t, err, "SetSuperReadOnly should not have failed")
+
+ isSuperReadOnly, _ = mysqld.IsSuperReadOnly()
+ assert.True(t, isSuperReadOnly, "super_read_only should be set to True")
+ // if value is already true then retFunc2 will be nil
+ retFunc2, err := mysqld.SetSuperReadOnly(true)
+ assert.Nil(t, retFunc2, "SetSuperReadOnly is supposed to return a nil function")
+ assert.NoError(t, err, "SetSuperReadOnly should not have failed")
+
+ retFunc1()
+ isSuperReadOnly, _ = mysqld.IsSuperReadOnly()
+ assert.False(t, isSuperReadOnly, "super_read_only should be set to False")
+ isReadOnly, _ := mysqld.IsReadOnly()
+ assert.True(t, isReadOnly, "read_only should be set to True")
+
+ isSuperReadOnly, _ = mysqld.IsSuperReadOnly()
+ assert.False(t, isSuperReadOnly, "super_read_only should be set to False")
+ retFunc1, err = mysqld.SetSuperReadOnly(false)
+ assert.Nil(t, retFunc1, "SetSuperReadOnly is supposed to return a nil function")
+ assert.NoError(t, err, "SetSuperReadOnly should not have failed")
+
+ _, err = mysqld.SetSuperReadOnly(true)
+ assert.NoError(t, err)
+
+ isSuperReadOnly, _ = mysqld.IsSuperReadOnly()
+ assert.True(t, isSuperReadOnly, "super_read_only should be set to True")
+ retFunc1, err = mysqld.SetSuperReadOnly(false)
+ assert.NotNil(t, retFunc1, "SetSuperReadOnly is supposed to return a defer function")
+ assert.NoError(t, err, "SetSuperReadOnly should not have failed")
+
+ isSuperReadOnly, _ = mysqld.IsSuperReadOnly()
+ assert.False(t, isSuperReadOnly, "super_read_only should be set to False")
+ // if value is already false then retFunc2 will be nil
+ retFunc2, err = mysqld.SetSuperReadOnly(false)
+ assert.Nil(t, retFunc2, "SetSuperReadOnly is supposed to return a nil function")
+ assert.NoError(t, err, "SetSuperReadOnly should not have failed")
+
+ retFunc1()
+ isSuperReadOnly, _ = mysqld.IsSuperReadOnly()
+ assert.True(t, isSuperReadOnly, "super_read_only should be set to True")
+ isReadOnly, _ = mysqld.IsReadOnly()
+ assert.True(t, isReadOnly, "read_only should be set to True")
+}
diff --git a/go/test/endtoend/utils/utils.go b/go/test/endtoend/utils/utils.go
index 1aca889025b..82d696fa856 100644
--- a/go/test/endtoend/utils/utils.go
+++ b/go/test/endtoend/utils/utils.go
@@ -22,14 +22,13 @@ import (
"testing"
"time"
- "vitess.io/vitess/go/test/endtoend/cluster"
-
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"vitess.io/vitess/go/mysql"
"vitess.io/vitess/go/sqltypes"
+ "vitess.io/vitess/go/test/endtoend/cluster"
)
// AssertContains ensures the given query result contains the expected results.
@@ -288,3 +287,23 @@ func convertToMap(input interface{}) map[string]interface{} {
output := input.(map[string]interface{})
return output
}
+
+func GetInitDBSQL(initDBSQL string, updatedPasswords string, oldAlterTableMode string) (string, error) {
+ // Since password update is DML we need to insert it before we disable
+ // super_read_only therefore doing the split below.
+ splitString := strings.Split(initDBSQL, "# {{custom_sql}}")
+ if len(splitString) != 2 {
+ return "", fmt.Errorf("missing `# {{custom_sql}}` in init_db.sql file")
+ }
+ var builder strings.Builder
+ builder.WriteString(splitString[0])
+ builder.WriteString(updatedPasswords)
+
+ // https://github.com/vitessio/vitess/issues/8315
+ if oldAlterTableMode != "" {
+ builder.WriteString(oldAlterTableMode)
+ }
+ builder.WriteString(splitString[1])
+
+ return builder.String(), nil
+}
diff --git a/go/test/endtoend/vault/dbcreds_secret.json b/go/test/endtoend/vault/dbcreds_secret.json
index 96fff38bdcd..ee0a4af534b 100644
--- a/go/test/endtoend/vault/dbcreds_secret.json
+++ b/go/test/endtoend/vault/dbcreds_secret.json
@@ -1,17 +1,23 @@
{
+ "root": [
+ "RootPass"
+ ],
"vt_app": [
- "password"
+ "VtAppPass"
],
"vt_dba": [
- "password"
+ "VtDbaPass"
],
"vt_repl": [
- "password"
+ "VtReplPass"
],
"vt_appdebug": [
- "password"
+ "VtDebugPass"
],
"vt_filtered": [
- "password"
+ "VtFilteredPass"
+ ],
+ "vt_allprivs": [
+ "VtAllprivsPass"
]
}
diff --git a/go/test/endtoend/vault/vault_test.go b/go/test/endtoend/vault/vault_test.go
index 25ed88f4335..2c4bf3059d6 100644
--- a/go/test/endtoend/vault/vault_test.go
+++ b/go/test/endtoend/vault/vault_test.go
@@ -33,6 +33,7 @@ import (
"vitess.io/vitess/go/mysql"
"vitess.io/vitess/go/test/endtoend/cluster"
+ "vitess.io/vitess/go/test/endtoend/utils"
"vitess.io/vitess/go/vt/log"
)
@@ -53,7 +54,7 @@ var (
shardName = "0"
dbName = "vt_ks"
mysqlUsers = []string{"vt_dba", "vt_app", "vt_appdebug", "vt_repl", "vt_filtered"}
- mysqlPassword = "password"
+ mysqlPassword = "VtDbaPass"
vtgateUser = "vtgate_user"
vtgatePassword = "password123"
commonTabletArg = []string{
@@ -251,10 +252,21 @@ func initializeClusterLate(t *testing.T) {
out, err := vtctldClientProcess.ExecuteCommandWithOutput("SetKeyspaceDurabilityPolicy", keyspaceName, "--durability-policy=semi_sync")
require.NoError(t, err, out)
+ initDb, _ := os.ReadFile(path.Join(os.Getenv("VTROOT"), "/config/init_db.sql"))
+ sql := string(initDb)
+ // The original init_db.sql does not have any passwords. Here we update the init file with passwords
+ sql, err = utils.GetInitDBSQL(sql, cluster.GetPasswordUpdateSQL(clusterInstance), "")
+ require.NoError(t, err, "expected to load init_db file")
+ newInitDBFile := path.Join(clusterInstance.TmpDirectory, "init_db_with_passwords.sql")
+ err = os.WriteFile(newInitDBFile, []byte(sql), 0660)
+ require.NoError(t, err, "expected to load init_db file")
+
// Start MySQL
var mysqlCtlProcessList []*exec.Cmd
for _, shard := range clusterInstance.Keyspaces[0].Shards {
for _, tablet := range shard.Vttablets {
+ tablet.MysqlctlProcess.InitDBFile = newInitDBFile
+ tablet.VttabletProcess.DbPassword = mysqlPassword
proc, err := tablet.MysqlctlProcess.StartProcess()
require.NoError(t, err)
mysqlCtlProcessList = append(mysqlCtlProcessList, proc)
@@ -268,21 +280,6 @@ func initializeClusterLate(t *testing.T) {
}
for _, tablet := range []*cluster.Vttablet{primary, replica} {
- for _, user := range mysqlUsers {
- query := fmt.Sprintf("ALTER USER '%s'@'%s' IDENTIFIED BY '%s';", user, hostname, mysqlPassword)
- _, err = tablet.VttabletProcess.QueryTablet(query, keyspace.Name, false)
- // Reset after the first ALTER, or we lock ourselves out.
- tablet.VttabletProcess.DbPassword = mysqlPassword
- if err != nil {
- query = fmt.Sprintf("ALTER USER '%s'@'%%' IDENTIFIED BY '%s';", user, mysqlPassword)
- _, err = tablet.VttabletProcess.QueryTablet(query, keyspace.Name, false)
- require.NoError(t, err)
- }
- }
- query := fmt.Sprintf("create database %s;", dbName)
- _, err = tablet.VttabletProcess.QueryTablet(query, keyspace.Name, false)
- require.NoError(t, err)
-
err = tablet.VttabletProcess.Setup()
require.NoError(t, err)
diff --git a/go/test/endtoend/vreplication/cluster_test.go b/go/test/endtoend/vreplication/cluster_test.go
index 43991454b6e..cd2c60a6336 100644
--- a/go/test/endtoend/vreplication/cluster_test.go
+++ b/go/test/endtoend/vreplication/cluster_test.go
@@ -450,7 +450,9 @@ func (vc *VitessCluster) AddTablet(t testing.TB, cell *Cell, keyspace *Keyspace,
require.NotNil(t, vttablet)
vttablet.SupportsBackup = false
- tablet.DbServer = cluster.MysqlCtlProcessInstance(tabletID, vc.ClusterConfig.tabletMysqlPortBase+tabletID, vc.ClusterConfig.tmpDir)
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstance(tabletID, vc.ClusterConfig.tabletMysqlPortBase+tabletID, vc.ClusterConfig.tmpDir)
+ require.NoError(t, err)
+ tablet.DbServer = mysqlctlProcess
require.NotNil(t, tablet.DbServer)
tablet.DbServer.InitMysql = true
proc, err := tablet.DbServer.StartProcess()
diff --git a/go/test/endtoend/vreplication/testdata/config/init_testserver_db.sql b/go/test/endtoend/vreplication/testdata/config/init_testserver_db.sql
new file mode 100644
index 00000000000..03df754ea21
--- /dev/null
+++ b/go/test/endtoend/vreplication/testdata/config/init_testserver_db.sql
@@ -0,0 +1,91 @@
+# This file is for testing purpose only.
+# This file is executed immediately after initializing a fresh data directory.
+# It is equivalent of init_db.sql. Given init_db.sql is for mysql which has super_read_only
+# related stuff therefore for testing purpose we avoid setting `super_read_only` during initialization.
+
+###############################################################################
+# WARNING: Any change to init_db.sql should gets reflected in this file as well.
+###############################################################################
+
+###############################################################################
+# WARNING: This sql is *NOT* safe for production use,
+# as it contains default well-known users and passwords.
+# Care should be taken to change these users and passwords
+# for production.
+###############################################################################
+
+###############################################################################
+# Equivalent of mysql_secure_installation
+###############################################################################
+# We need to ensure that read_only is disabled so that we can execute
+# these commands.
+SET GLOBAL read_only='OFF';
+
+# Changes during the init db should not make it to the binlog.
+# They could potentially create errant transactions on replicas.
+SET sql_log_bin = 0;
+# Remove anonymous users.
+DELETE FROM mysql.user WHERE User = '';
+
+# Disable remote root access (only allow UNIX socket).
+DELETE FROM mysql.user WHERE User = 'root' AND Host != 'localhost';
+
+# Remove test database.
+DROP DATABASE IF EXISTS test;
+
+###############################################################################
+# Vitess defaults
+###############################################################################
+
+# Admin user with all privileges.
+CREATE USER 'vt_dba'@'localhost';
+GRANT ALL ON *.* TO 'vt_dba'@'localhost';
+GRANT GRANT OPTION ON *.* TO 'vt_dba'@'localhost';
+
+# User for app traffic, with global read-write access.
+CREATE USER 'vt_app'@'localhost';
+GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE,
+ REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES,
+ LOCK TABLES, EXECUTE, REPLICATION CLIENT, CREATE VIEW,
+ SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER
+ ON *.* TO 'vt_app'@'localhost';
+
+# User for app debug traffic, with global read access.
+CREATE USER 'vt_appdebug'@'localhost';
+GRANT SELECT, SHOW DATABASES, PROCESS ON *.* TO 'vt_appdebug'@'localhost';
+
+# User for administrative operations that need to be executed as non-SUPER.
+# Same permissions as vt_app here.
+CREATE USER 'vt_allprivs'@'localhost';
+GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE,
+ REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES,
+ LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW,
+ SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER
+ ON *.* TO 'vt_allprivs'@'localhost';
+
+# User for slave replication connections.
+CREATE USER 'vt_repl'@'%';
+GRANT REPLICATION SLAVE ON *.* TO 'vt_repl'@'%';
+
+# User for Vitess VReplication (base vstreamers and vplayer).
+CREATE USER 'vt_filtered'@'localhost';
+GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE,
+ REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES,
+ LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW,
+ SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER
+ ON *.* TO 'vt_filtered'@'localhost';
+
+# User for general MySQL monitoring.
+CREATE USER 'vt_monitoring'@'localhost';
+GRANT SELECT, PROCESS, SUPER, REPLICATION CLIENT, RELOAD
+ ON *.* TO 'vt_monitoring'@'localhost';
+GRANT SELECT, UPDATE, DELETE, DROP
+ ON performance_schema.* TO 'vt_monitoring'@'localhost';
+
+FLUSH PRIVILEGES;
+
+RESET SLAVE ALL;
+RESET MASTER;
+
+# custom sql is used to add custom scripts like creating users/passwords. We use it in our tests
+# {{custom_sql}}
diff --git a/go/test/endtoend/vreplication/vreplication_test.go b/go/test/endtoend/vreplication/vreplication_test.go
index 3f8f7f91997..6e65a72544e 100644
--- a/go/test/endtoend/vreplication/vreplication_test.go
+++ b/go/test/endtoend/vreplication/vreplication_test.go
@@ -386,6 +386,7 @@ func TestMultiCellVreplicationWorkflow(t *testing.T) {
insertInitialData(t)
shardCustomer(t, true, []*Cell{cell1, cell2}, cell2.Name, true)
+ checkIfDenyListExists(t, vc, "product:0", "customer")
// we tag along this test so as not to create the overhead of creating another cluster
testVStreamCellFlag(t)
}
diff --git a/go/test/endtoend/vtgate/tablet_healthcheck_cache/correctness_test.go b/go/test/endtoend/vtgate/tablet_healthcheck_cache/correctness_test.go
index 386ef325996..928cb2d1c6a 100644
--- a/go/test/endtoend/vtgate/tablet_healthcheck_cache/correctness_test.go
+++ b/go/test/endtoend/vtgate/tablet_healthcheck_cache/correctness_test.go
@@ -183,7 +183,9 @@ func addTablet(t *testing.T, tabletUID int, tabletType string) *cluster.Vttablet
Alias: fmt.Sprintf("%s-%010d", cell, tabletUID),
}
// Start Mysqlctl process
- tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstanceOptionalInit(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory, !clusterInstance.ReusingVTDATAROOT)
+ mysqlctlProcess, err := cluster.MysqlCtlProcessInstanceOptionalInit(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory, !clusterInstance.ReusingVTDATAROOT)
+ require.Nil(t, err)
+ tablet.MysqlctlProcess = *mysqlctlProcess
proc, err := tablet.MysqlctlProcess.StartProcess()
require.Nil(t, err)
diff --git a/go/test/endtoend/vtorc/readtopologyinstance/main_test.go b/go/test/endtoend/vtorc/readtopologyinstance/main_test.go
index fbc29600e98..fe8b53f5103 100644
--- a/go/test/endtoend/vtorc/readtopologyinstance/main_test.go
+++ b/go/test/endtoend/vtorc/readtopologyinstance/main_test.go
@@ -27,6 +27,7 @@ import (
"vitess.io/vitess/go/vt/servenv"
"vitess.io/vitess/go/vt/vtorc/config"
"vitess.io/vitess/go/vt/vtorc/inst"
+ "vitess.io/vitess/go/vt/vtorc/logic"
"vitess.io/vitess/go/vt/vtorc/server"
_ "github.com/go-sql-driver/mysql"
@@ -104,8 +105,19 @@ func TestReadTopologyInstanceBufferable(t *testing.T) {
assert.Equal(t, primaryInstance.ReplicationIOThreadState, inst.ReplicationThreadStateNoThread)
assert.Equal(t, primaryInstance.ReplicationSQLThreadState, inst.ReplicationThreadStateNoThread)
- // insert an errant GTID in the replica
- _, err = utils.RunSQL(t, "insert into vt_insert_test(id, msg) values (10173, 'test 178342')", replica, "vt_ks")
+ // Insert an errant GTID in the replica.
+ // The way to do this is to disable global recoveries, stop replication and inject an errant GTID.
+ // After this we restart the replication and enable the recoveries again.
+ err = logic.DisableRecovery()
+ require.NoError(t, err)
+ err = utils.RunSQLs(t, []string{`STOP SLAVE;`,
+ `SET GTID_NEXT="12345678-1234-1234-1234-123456789012:1";`,
+ `BEGIN;`, `COMMIT;`,
+ `SET GTID_NEXT="AUTOMATIC";`,
+ `START SLAVE;`,
+ }, replica, "")
+ require.NoError(t, err)
+ err = logic.EnableRecovery()
require.NoError(t, err)
replicaInstance, err := inst.ReadTopologyInstanceBufferable(&inst.InstanceKey{
diff --git a/go/test/endtoend/vtorc/utils/utils.go b/go/test/endtoend/vtorc/utils/utils.go
index d4f23c0de70..fc118ef3ac5 100644
--- a/go/test/endtoend/vtorc/utils/utils.go
+++ b/go/test/endtoend/vtorc/utils/utils.go
@@ -316,7 +316,7 @@ func SetupVttabletsAndVTOrcs(t *testing.T, clusterInfo *VTOrcClusterInfo, numRep
// cleanAndStartVttablet cleans the MySQL instance underneath for running a new test. It also starts the vttablet.
func cleanAndStartVttablet(t *testing.T, clusterInfo *VTOrcClusterInfo, vttablet *cluster.Vttablet) {
t.Helper()
- // set super-read-only to false
+ // set super_read_only to false
_, err := RunSQL(t, "SET GLOBAL super_read_only = OFF", vttablet, "")
require.NoError(t, err)
// remove the databases if they exist
@@ -585,6 +585,26 @@ func RunSQL(t *testing.T, sql string, tablet *cluster.Vttablet, db string) (*sql
return execute(t, conn, sql)
}
+// RunSQLs is used to run a list of SQL statements on the given tablet
+func RunSQLs(t *testing.T, sqls []string, tablet *cluster.Vttablet, db string) error {
+ // Get Connection
+ tabletParams := getMysqlConnParam(tablet, db)
+ var timeoutDuration = time.Duration(5 * len(sqls))
+ ctx, cancel := context.WithTimeout(context.Background(), timeoutDuration*time.Second)
+ defer cancel()
+ conn, err := mysql.Connect(ctx, &tabletParams)
+ require.Nil(t, err)
+ defer conn.Close()
+
+ // Run SQLs
+ for _, sql := range sqls {
+ if _, err := execute(t, conn, sql); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
func execute(t *testing.T, conn *mysql.Conn, query string) (*sqltypes.Result, error) {
t.Helper()
return conn.ExecuteFetch(query, 1000, true)
diff --git a/go/vt/dbconfigs/dbconfigs.go b/go/vt/dbconfigs/dbconfigs.go
index 371892144d3..940652094c9 100644
--- a/go/vt/dbconfigs/dbconfigs.go
+++ b/go/vt/dbconfigs/dbconfigs.go
@@ -216,12 +216,12 @@ func (dbcfgs *DBConfigs) AppDebugWithDB() Connector {
return dbcfgs.makeParams(&dbcfgs.appdebugParams, true)
}
-// AllPrivsConnector returns connection parameters for appdebug with no dbname set.
+// AllPrivsConnector returns connection parameters for allprivs with no dbname set.
func (dbcfgs *DBConfigs) AllPrivsConnector() Connector {
return dbcfgs.makeParams(&dbcfgs.allprivsParams, false)
}
-// AllPrivsWithDB returns connection parameters for appdebug with dbname set.
+// AllPrivsWithDB returns connection parameters for allprivs with dbname set.
func (dbcfgs *DBConfigs) AllPrivsWithDB() Connector {
return dbcfgs.makeParams(&dbcfgs.allprivsParams, true)
}
diff --git a/go/vt/mysqlctl/backup.go b/go/vt/mysqlctl/backup.go
index 0865759f2a3..b1751ba3bd9 100644
--- a/go/vt/mysqlctl/backup.go
+++ b/go/vt/mysqlctl/backup.go
@@ -25,22 +25,19 @@ import (
"strings"
"time"
+ "github.com/spf13/pflag"
"golang.org/x/text/cases"
"golang.org/x/text/language"
- "github.com/spf13/pflag"
-
- "vitess.io/vitess/go/vt/servenv"
-
- "vitess.io/vitess/go/mysql"
"vitess.io/vitess/go/vt/log"
"vitess.io/vitess/go/vt/mysqlctl/backupstats"
- stats "vitess.io/vitess/go/vt/mysqlctl/backupstats"
"vitess.io/vitess/go/vt/mysqlctl/backupstorage"
"vitess.io/vitess/go/vt/proto/vtrpc"
+ "vitess.io/vitess/go/vt/servenv"
"vitess.io/vitess/go/vt/topo/topoproto"
"vitess.io/vitess/go/vt/vterrors"
+ stats "vitess.io/vitess/go/vt/mysqlctl/backupstats"
topodatapb "vitess.io/vitess/go/vt/proto/topodata"
)
@@ -390,21 +387,6 @@ func Restore(ctx context.Context, params RestoreParams) (*BackupManifest, error)
return nil, err
}
- // We disable super_read_only, in case it is in the default MySQL startup
- // parameters and will be blocking the writes we need to do in
- // PopulateMetadataTables(). We do it blindly, since
- // this will fail on MariaDB, which doesn't have super_read_only
- // This is safe, since we're restarting MySQL after the restore anyway
- params.Logger.Infof("Restore: disabling super_read_only")
- if err := params.Mysqld.SetSuperReadOnly(false); err != nil {
- if strings.Contains(err.Error(), mysql.ERUnknownSystemVariable.ToString()) {
- params.Logger.Warningf("Restore: server does not know about super_read_only, continuing anyway...")
- } else {
- params.Logger.Errorf("Restore: unexpected error while trying to set super_read_only: %v", err)
- return nil, err
- }
- }
-
params.Logger.Infof("Restore: running mysql_upgrade")
if err := params.Mysqld.RunMysqlUpgrade(); err != nil {
return nil, vterrors.Wrap(err, "mysql_upgrade failed")
diff --git a/go/vt/mysqlctl/builtinbackupengine.go b/go/vt/mysqlctl/builtinbackupengine.go
index 68625ede3ef..7c16aec37b7 100644
--- a/go/vt/mysqlctl/builtinbackupengine.go
+++ b/go/vt/mysqlctl/builtinbackupengine.go
@@ -318,7 +318,8 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac
// Save initial state so we can restore.
replicaStartRequired := false
sourceIsPrimary := false
- readOnly := true //nolint
+ superReadOnly := true //nolint
+ readOnly := true //nolint
var replicationPosition mysql.Position
semiSyncSource, semiSyncReplica := params.Mysqld.SemiSyncEnabled()
@@ -338,16 +339,30 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac
// get the read-only flag
readOnly, err = params.Mysqld.IsReadOnly()
if err != nil {
- return false, vterrors.Wrap(err, "can't get read-only status")
+ return false, vterrors.Wrap(err, "failed to get read_only status")
}
+ superReadOnly, err = params.Mysqld.IsSuperReadOnly()
+ if err != nil {
+ return false, vterrors.Wrap(err, "can't get super_read_only status")
+ }
+ log.Infof("Flag values during full backup, read_only: %v, super_read_only:%t", readOnly, superReadOnly)
// get the replication position
if sourceIsPrimary {
- if !readOnly {
- params.Logger.Infof("turning primary read-only before backup")
- if err = params.Mysqld.SetReadOnly(true); err != nil {
- return false, vterrors.Wrap(err, "can't set read-only status")
+ // No need to set read_only because super_read_only will implicitly set read_only to true as well.
+ if !superReadOnly {
+ params.Logger.Infof("Enabling super_read_only on primary prior to backup")
+ if _, err = params.Mysqld.SetSuperReadOnly(true); err != nil {
+ return false, vterrors.Wrap(err, "failed to enable super_read_only")
}
+ defer func() {
+ // Resetting super_read_only back to its original value
+ params.Logger.Infof("resetting mysqld super_read_only to %v", superReadOnly)
+ if _, err := params.Mysqld.SetSuperReadOnly(false); err != nil {
+ log.Error("Failed to set super_read_only back to its original value")
+ }
+ }()
+
}
replicationPosition, err = params.Mysqld.PrimaryPosition()
if err != nil {
@@ -398,9 +413,9 @@ func (be *BuiltinBackupEngine) executeFullBackup(ctx context.Context, params Bac
return usable, vterrors.Wrap(err, "can't restart mysqld")
}
- // And set read-only mode
- params.Logger.Infof("resetting mysqld read-only to %v", readOnly)
- if err := params.Mysqld.SetReadOnly(readOnly); err != nil {
+ // Resetting super_read_only back to its original value
+ params.Logger.Infof("resetting mysqld super_read_only to %v", superReadOnly)
+ if _, err := params.Mysqld.SetSuperReadOnly(superReadOnly); err != nil {
return usable, err
}
@@ -785,7 +800,6 @@ func (be *BuiltinBackupEngine) executeRestoreFullBackup(ctx context.Context, par
// The underlying mysql database is expected to be up and running.
func (be *BuiltinBackupEngine) executeRestoreIncrementalBackup(ctx context.Context, params RestoreParams, bh backupstorage.BackupHandle, bm builtinBackupManifest) error {
params.Logger.Infof("Restoring incremental backup to position: %v", bm.Position)
-
createdDir, err := be.restoreFiles(context.Background(), params, bh, bm)
defer os.RemoveAll(createdDir)
mysqld, ok := params.Mysqld.(*Mysqld)
@@ -819,7 +833,6 @@ func (be *BuiltinBackupEngine) executeRestoreIncrementalBackup(ctx context.Conte
func (be *BuiltinBackupEngine) ExecuteRestore(ctx context.Context, params RestoreParams, bh backupstorage.BackupHandle) (*BackupManifest, error) {
var bm builtinBackupManifest
-
if err := getBackupManifestInto(ctx, bh, &bm); err != nil {
return nil, err
}
diff --git a/go/vt/mysqlctl/fakemysqldaemon.go b/go/vt/mysqlctl/fakemysqldaemon.go
index 25b4f328de4..475d4251fbc 100644
--- a/go/vt/mysqlctl/fakemysqldaemon.go
+++ b/go/vt/mysqlctl/fakemysqldaemon.go
@@ -101,7 +101,7 @@ type FakeMysqlDaemon struct {
ReadOnly bool
// SuperReadOnly is the current value of the flag
- SuperReadOnly bool
+ SuperReadOnly atomic.Bool
// SetReplicationPositionPos is matched against the input of SetReplicationPosition.
// If it doesn't match, SetReplicationPosition will return an error.
@@ -364,6 +364,11 @@ func (fmd *FakeMysqlDaemon) IsReadOnly() (bool, error) {
return fmd.ReadOnly, nil
}
+// IsSuperReadOnly is part of the MysqlDaemon interface
+func (fmd *FakeMysqlDaemon) IsSuperReadOnly() (bool, error) {
+ return fmd.SuperReadOnly.Load(), nil
+}
+
// SetReadOnly is part of the MysqlDaemon interface
func (fmd *FakeMysqlDaemon) SetReadOnly(on bool) error {
fmd.ReadOnly = on
@@ -371,10 +376,10 @@ func (fmd *FakeMysqlDaemon) SetReadOnly(on bool) error {
}
// SetSuperReadOnly is part of the MysqlDaemon interface
-func (fmd *FakeMysqlDaemon) SetSuperReadOnly(on bool) error {
- fmd.SuperReadOnly = on
+func (fmd *FakeMysqlDaemon) SetSuperReadOnly(on bool) (ResetSuperReadOnlyFunc, error) {
+ fmd.SuperReadOnly.Store(on)
fmd.ReadOnly = on
- return nil
+ return nil, nil
}
// StartReplication is part of the MysqlDaemon interface.
diff --git a/go/vt/mysqlctl/mysql_daemon.go b/go/vt/mysqlctl/mysql_daemon.go
index 020595b0277..190b81fb001 100644
--- a/go/vt/mysqlctl/mysql_daemon.go
+++ b/go/vt/mysqlctl/mysql_daemon.go
@@ -72,8 +72,9 @@ type MysqlDaemon interface {
ResetReplication(ctx context.Context) error
PrimaryPosition() (mysql.Position, error)
IsReadOnly() (bool, error)
+ IsSuperReadOnly() (bool, error)
SetReadOnly(on bool) error
- SetSuperReadOnly(on bool) error
+ SetSuperReadOnly(on bool) (ResetSuperReadOnlyFunc, error)
SetReplicationPosition(ctx context.Context, pos mysql.Position) error
SetReplicationSource(ctx context.Context, host string, port int32, stopReplicationBefore bool, startReplicationAfter bool) error
WaitForReparentJournal(ctx context.Context, timeCreatedNS int64) error
diff --git a/go/vt/mysqlctl/mysqld.go b/go/vt/mysqlctl/mysqld.go
index f91d880bc5f..19ab541a322 100644
--- a/go/vt/mysqlctl/mysqld.go
+++ b/go/vt/mysqlctl/mysqld.go
@@ -667,7 +667,7 @@ func (mysqld *Mysqld) InitConfig(cnf *Mycnf) error {
// generate / configure a my.cnf file install a skeleton database,
// and apply the provided initial SQL file.
func (mysqld *Mysqld) Init(ctx context.Context, cnf *Mycnf, initDBSQLFile string) error {
- log.Infof("mysqlctl.Init")
+ log.Infof("mysqlctl.Init running with contents previously embedded from %s", initDBSQLFile)
err := mysqld.InitConfig(cnf)
if err != nil {
log.Errorf("%s", err.Error())
@@ -695,7 +695,6 @@ func (mysqld *Mysqld) Init(ctx context.Context, cnf *Mycnf, initDBSQLFile string
log.Errorf("failed starting mysqld in time: %v\n%v", err, readTailOfMysqldErrorLog(cnf.ErrorLogPath))
return err
}
-
if initDBSQLFile == "" { // default to built-in
if err := mysqld.executeMysqlScript(params, strings.NewReader(config.DefaultInitDB)); err != nil {
return fmt.Errorf("failed to initialize mysqld: %v", err)
@@ -1223,6 +1222,7 @@ func (mysqld *Mysqld) applyBinlogFile(binlogFile string, includeGTIDs mysql.GTID
gtids,
)
}
+
args = append(args, binlogFile)
mysqlbinlogCmd = exec.Command(name, args...)
@@ -1251,6 +1251,29 @@ func (mysqld *Mysqld) applyBinlogFile(binlogFile string, includeGTIDs mysql.GTID
args := []string{
"--defaults-extra-file=" + cnf,
}
+
+ // We disable super_read_only, in case it is in the default MySQL startup
+ // parameters. We do it blindly, since this will fail on MariaDB, which doesn't
+ // have super_read_only This is safe, since we're restarting MySQL after the restore anyway
+ log.Infof("applyBinlogFile: disabling super_read_only")
+ resetFunc, err := mysqld.SetSuperReadOnly(false)
+ if err != nil {
+ if sqlErr, ok := err.(*mysql.SQLError); ok && sqlErr.Number() == mysql.ERUnknownSystemVariable {
+ log.Warningf("applyBinlogFile: server does not know about super_read_only, continuing anyway...")
+ } else {
+ log.Errorf("applyBinlogFile: unexpected error while trying to set super_read_only: %v", err)
+ return err
+ }
+ }
+ if resetFunc != nil {
+ defer func() {
+ err := resetFunc()
+ if err != nil {
+ log.Error("Not able to set super_read_only to its original value during applyBinlogFile.")
+ }
+ }()
+ }
+
mysqlCmd = exec.Command(name, args...)
mysqlCmd.Dir = dir
mysqlCmd.Env = env
diff --git a/go/vt/mysqlctl/replication.go b/go/vt/mysqlctl/replication.go
index 3a4aee6e063..10258b16d2c 100644
--- a/go/vt/mysqlctl/replication.go
+++ b/go/vt/mysqlctl/replication.go
@@ -37,6 +37,8 @@ import (
"vitess.io/vitess/go/vt/log"
)
+type ResetSuperReadOnlyFunc func() error
+
// WaitForReplicationStart waits until the deadline for replication to start.
// This validates the current primary is correct and can be connected to.
func WaitForReplicationStart(mysqld MysqlDaemon, replicaStartDeadline int) error {
@@ -230,6 +232,22 @@ func (mysqld *Mysqld) IsReadOnly() (bool, error) {
return false, nil
}
+// IsSuperReadOnly return true if the instance is super read only
+func (mysqld *Mysqld) IsSuperReadOnly() (bool, error) {
+ qr, err := mysqld.FetchSuperQuery(context.TODO(), "SELECT @@global.super_read_only")
+ if err != nil {
+ return false, err
+ }
+ if err == nil && len(qr.Rows) == 1 {
+ sro := qr.Rows[0][0].ToString()
+ if sro == "1" || sro == "ON" {
+ return true, nil
+ }
+ }
+
+ return false, nil
+}
+
// SetReadOnly set/unset the read_only flag
func (mysqld *Mysqld) SetReadOnly(on bool) error {
// temp logging, to be removed in v17
@@ -252,15 +270,52 @@ func (mysqld *Mysqld) SetReadOnly(on bool) error {
return mysqld.ExecuteSuperQuery(context.TODO(), query)
}
-// SetSuperReadOnly set/unset the super_read_only flag
-func (mysqld *Mysqld) SetSuperReadOnly(on bool) error {
+// SetSuperReadOnly set/unset the super_read_only flag.
+// Returns a function which is called to set super_read_only back to its original value.
+func (mysqld *Mysqld) SetSuperReadOnly(on bool) (ResetSuperReadOnlyFunc, error) {
+ // return function for switching `OFF` super_read_only
+ var resetFunc ResetSuperReadOnlyFunc
+ var disableFunc = func() error {
+ query := "SET GLOBAL super_read_only = 'OFF'"
+ err := mysqld.ExecuteSuperQuery(context.Background(), query)
+ return err
+ }
+
+ // return function for switching `ON` super_read_only.
+ var enableFunc = func() error {
+ query := "SET GLOBAL super_read_only = 'ON'"
+ err := mysqld.ExecuteSuperQuery(context.Background(), query)
+ return err
+ }
+
+ superReadOnlyEnabled, err := mysqld.IsSuperReadOnly()
+ if err != nil {
+ return nil, err
+ }
+
+ // If non-idempotent then set the right call-back.
+ // We are asked to turn on super_read_only but original value is false,
+ // therefore return disableFunc, that can be used as defer by caller.
+ if on && !superReadOnlyEnabled {
+ resetFunc = disableFunc
+ }
+ // We are asked to turn off super_read_only but original value is true,
+ // therefore return enableFunc, that can be used as defer by caller.
+ if !on && superReadOnlyEnabled {
+ resetFunc = enableFunc
+ }
+
query := "SET GLOBAL super_read_only = "
if on {
- query += "ON"
+ query += "'ON'"
} else {
- query += "OFF"
+ query += "'OFF'"
}
- return mysqld.ExecuteSuperQuery(context.TODO(), query)
+ if err := mysqld.ExecuteSuperQuery(context.Background(), query); err != nil {
+ return nil, err
+ }
+
+ return resetFunc, nil
}
// WaitSourcePos lets replicas wait to given replication position
diff --git a/go/vt/proto/replicationdata/replicationdata.pb.go b/go/vt/proto/replicationdata/replicationdata.pb.go
index 55bcdf99b55..58aedab8bbc 100644
--- a/go/vt/proto/replicationdata/replicationdata.pb.go
+++ b/go/vt/proto/replicationdata/replicationdata.pb.go
@@ -441,6 +441,7 @@ type FullStatus struct {
SemiSyncPrimaryClients uint32 `protobuf:"varint,18,opt,name=semi_sync_primary_clients,json=semiSyncPrimaryClients,proto3" json:"semi_sync_primary_clients,omitempty"`
SemiSyncPrimaryTimeout uint64 `protobuf:"varint,19,opt,name=semi_sync_primary_timeout,json=semiSyncPrimaryTimeout,proto3" json:"semi_sync_primary_timeout,omitempty"`
SemiSyncWaitForReplicaCount uint32 `protobuf:"varint,20,opt,name=semi_sync_wait_for_replica_count,json=semiSyncWaitForReplicaCount,proto3" json:"semi_sync_wait_for_replica_count,omitempty"`
+ SuperReadOnly bool `protobuf:"varint,21,opt,name=super_read_only,json=superReadOnly,proto3" json:"super_read_only,omitempty"`
}
func (x *FullStatus) Reset() {
@@ -615,6 +616,13 @@ func (x *FullStatus) GetSemiSyncWaitForReplicaCount() uint32 {
return 0
}
+func (x *FullStatus) GetSuperReadOnly() bool {
+ if x != nil {
+ return x.SuperReadOnly
+ }
+ return false
+}
+
var File_replicationdata_proto protoreflect.FileDescriptor
var file_replicationdata_proto_rawDesc = []byte{
@@ -690,7 +698,7 @@ var file_replicationdata_proto_rawDesc = []byte{
0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x66, 0x69, 0x6c, 0x65, 0x5f,
0x70, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
- 0x66, 0x69, 0x6c, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xc3, 0x07, 0x0a,
+ 0x66, 0x69, 0x6c, 0x65, 0x50, 0x6f, 0x73, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xeb, 0x07, 0x0a,
0x0a, 0x46, 0x75, 0x6c, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08,
0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x65, 0x72, 0x76,
@@ -751,14 +759,16 @@ var file_replicationdata_proto_rawDesc = []byte{
0x72, 0x5f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18,
0x14, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x1b, 0x73, 0x65, 0x6d, 0x69, 0x53, 0x79, 0x6e, 0x63, 0x57,
0x61, 0x69, 0x74, 0x46, 0x6f, 0x72, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x75,
- 0x6e, 0x74, 0x2a, 0x3b, 0x0a, 0x13, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63,
- 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4f, 0x41,
- 0x4e, 0x44, 0x53, 0x51, 0x4c, 0x54, 0x48, 0x52, 0x45, 0x41, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a,
- 0x0c, 0x49, 0x4f, 0x54, 0x48, 0x52, 0x45, 0x41, 0x44, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x01, 0x42,
- 0x2e, 0x5a, 0x2c, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74,
- 0x65, 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
- 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x62,
- 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+ 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0f, 0x73, 0x75, 0x70, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x61, 0x64,
+ 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x18, 0x15, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x73, 0x75, 0x70,
+ 0x65, 0x72, 0x52, 0x65, 0x61, 0x64, 0x4f, 0x6e, 0x6c, 0x79, 0x2a, 0x3b, 0x0a, 0x13, 0x53, 0x74,
+ 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x6f, 0x64,
+ 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x49, 0x4f, 0x41, 0x4e, 0x44, 0x53, 0x51, 0x4c, 0x54, 0x48, 0x52,
+ 0x45, 0x41, 0x44, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x4f, 0x54, 0x48, 0x52, 0x45, 0x41,
+ 0x44, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x01, 0x42, 0x2e, 0x5a, 0x2c, 0x76, 0x69, 0x74, 0x65, 0x73,
+ 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x76, 0x69, 0x74, 0x65, 0x73, 0x73, 0x2f, 0x67, 0x6f, 0x2f, 0x76,
+ 0x74, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x72, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x64, 0x61, 0x74, 0x61, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
diff --git a/go/vt/proto/replicationdata/replicationdata_vtproto.pb.go b/go/vt/proto/replicationdata/replicationdata_vtproto.pb.go
index 350a733e865..64e5a8d8b94 100644
--- a/go/vt/proto/replicationdata/replicationdata_vtproto.pb.go
+++ b/go/vt/proto/replicationdata/replicationdata_vtproto.pb.go
@@ -354,6 +354,18 @@ func (m *FullStatus) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
i -= len(m.unknownFields)
copy(dAtA[i:], m.unknownFields)
}
+ if m.SuperReadOnly {
+ i--
+ if m.SuperReadOnly {
+ dAtA[i] = 1
+ } else {
+ dAtA[i] = 0
+ }
+ i--
+ dAtA[i] = 0x1
+ i--
+ dAtA[i] = 0xa8
+ }
if m.SemiSyncWaitForReplicaCount != 0 {
i = encodeVarint(dAtA, i, uint64(m.SemiSyncWaitForReplicaCount))
i--
@@ -734,6 +746,9 @@ func (m *FullStatus) SizeVT() (n int) {
if m.SemiSyncWaitForReplicaCount != 0 {
n += 2 + sov(uint64(m.SemiSyncWaitForReplicaCount))
}
+ if m.SuperReadOnly {
+ n += 3
+ }
n += len(m.unknownFields)
return n
}
@@ -2127,6 +2142,26 @@ func (m *FullStatus) UnmarshalVT(dAtA []byte) error {
break
}
}
+ case 21:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field SuperReadOnly", wireType)
+ }
+ var v int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflow
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ v |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ m.SuperReadOnly = bool(v != 0)
default:
iNdEx = preIndex
skippy, err := skip(dAtA[iNdEx:])
diff --git a/go/vt/vtexplain/vtexplain_test.go b/go/vt/vtexplain/vtexplain_test.go
index 8145c59b44d..3ba417778da 100644
--- a/go/vt/vtexplain/vtexplain_test.go
+++ b/go/vt/vtexplain/vtexplain_test.go
@@ -24,15 +24,15 @@ import (
"strings"
"testing"
- "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv/tabletenvtest"
-
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/require"
"vitess.io/vitess/go/vt/key"
- querypb "vitess.io/vitess/go/vt/proto/query"
"vitess.io/vitess/go/vt/proto/topodata"
"vitess.io/vitess/go/vt/topo"
+ "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv/tabletenvtest"
+
+ querypb "vitess.io/vitess/go/vt/proto/query"
)
func defaultTestOpts() *Options {
diff --git a/go/vt/vtexplain/vtexplain_vttablet.go b/go/vt/vtexplain/vtexplain_vttablet.go
index a1bef6547fc..94c03671659 100644
--- a/go/vt/vtexplain/vtexplain_vttablet.go
+++ b/go/vt/vtexplain/vtexplain_vttablet.go
@@ -496,6 +496,30 @@ func (t *explainTablet) HandleQuery(c *mysql.Conn, query string, callback func(*
t.mu.Lock()
defer t.mu.Unlock()
+ // If query is part of rejected list then return error right away.
+ if err := t.db.GetRejectedQueryResult(query); err != nil {
+ return err
+ }
+
+ // If query is expected to have a specific result then return the result.
+ if result := t.db.GetQueryResult(query); result != nil {
+ if f := result.BeforeFunc; f != nil {
+ f()
+ }
+ return callback(result.Result)
+ }
+
+ // return result if query is part of defined pattern.
+ if userCallback, expResult, ok, err := t.db.GetQueryPatternResult(query); ok {
+ if userCallback != nil {
+ userCallback(query)
+ }
+ if err != nil {
+ return err
+ }
+ return callback(expResult.Result)
+ }
+
if !strings.Contains(query, "1 != 1") {
t.mysqlQueries = append(t.mysqlQueries, &MysqlQuery{
Time: t.currentTime,
@@ -506,13 +530,11 @@ func (t *explainTablet) HandleQuery(c *mysql.Conn, query string, callback func(*
// return the pre-computed results for any schema introspection queries
tEnv := t.vte.getGlobalTabletEnv()
result := tEnv.getResult(query)
- emptyResult := &sqltypes.Result{}
- if sidecardb.MatchesInitQuery(query) {
- return callback(emptyResult)
- }
+
if result != nil {
return callback(result)
}
+
switch sqlparser.Preview(query) {
case sqlparser.StmtSelect:
var err error
diff --git a/go/vt/vttablet/tabletmanager/rpc_replication.go b/go/vt/vttablet/tabletmanager/rpc_replication.go
index 196014b8271..445e8d94c8b 100644
--- a/go/vt/vttablet/tabletmanager/rpc_replication.go
+++ b/go/vt/vttablet/tabletmanager/rpc_replication.go
@@ -37,11 +37,11 @@ import (
topodatapb "vitess.io/vitess/go/vt/proto/topodata"
)
-var setSuperReadOnly bool
var disableReplicationManager bool
func registerReplicationFlags(fs *pflag.FlagSet) {
- fs.BoolVar(&setSuperReadOnly, "use_super_read_only", setSuperReadOnly, "Set super_read_only flag when performing planned failover.")
+ fs.Bool("use_super_read_only", true, "Set super_read_only flag when performing planned failover.")
+ fs.MarkDeprecated("use_super_read_only", "From v17 onwards MySQL server will always try to start with super_read_only=ON")
fs.BoolVar(&disableReplicationManager, "disable-replication-manager", disableReplicationManager, "Disable replication manager to prevent replication repairs.")
fs.MarkDeprecated("disable-replication-manager", "Replication manager is deleted")
}
@@ -112,6 +112,12 @@ func (tm *TabletManager) FullStatus(ctx context.Context) (*replicationdatapb.Ful
return nil, err
}
+ // superReadOnly - "SELECT @@global.super_read_only"
+ superReadOnly, err := tm.MysqlDaemon.IsSuperReadOnly()
+ if err != nil {
+ return nil, err
+ }
+
// Binlog Information - "select @@global.binlog_format, @@global.log_bin, @@global.log_slave_updates, @@global.binlog_row_image"
binlogFormat, logBin, logReplicaUpdates, binlogRowImage, err := tm.MysqlDaemon.GetBinlogInformation(ctx)
if err != nil {
@@ -157,6 +163,7 @@ func (tm *TabletManager) FullStatus(ctx context.Context) (*replicationdatapb.Ful
SemiSyncPrimaryClients: semiSyncClients,
SemiSyncPrimaryTimeout: semiSyncTimeout,
SemiSyncWaitForReplicaCount: semiSyncNumReplicas,
+ SuperReadOnly: superReadOnly,
}, nil
}
@@ -297,14 +304,12 @@ func (tm *TabletManager) InitPrimary(ctx context.Context, semiSync bool) (string
}
defer tm.unlock()
- if setSuperReadOnly {
- // Setting super_read_only off so that we can run the DDL commands
- if err := tm.MysqlDaemon.SetSuperReadOnly(false); err != nil {
- if strings.Contains(err.Error(), mysql.ERUnknownSystemVariable.ToString()) {
- log.Warningf("server does not know about super_read_only, continuing anyway...")
- } else {
- return "", err
- }
+ // Setting super_read_only `OFF` so that we can run the DDL commands
+ if _, err := tm.MysqlDaemon.SetSuperReadOnly(false); err != nil {
+ if sqlErr, ok := err.(*mysql.SQLError); ok && sqlErr.Number() == mysql.ERUnknownSystemVariable {
+ log.Warningf("server does not know about super_read_only, continuing anyway...")
+ } else {
+ return "", err
}
}
@@ -463,23 +468,17 @@ func (tm *TabletManager) demotePrimary(ctx context.Context, revertPartialFailure
}
// Now that we know no writes are in-flight and no new writes can occur,
- // set MySQL to read-only mode. If we are already read-only because of a
+ // set MySQL to super_read_only mode. If we are already super_read_only because of a
// previous demotion, or because we are not primary anyway, this should be
// idempotent.
- if setSuperReadOnly {
- // Setting super_read_only also sets read_only
- if err := tm.MysqlDaemon.SetSuperReadOnly(true); err != nil {
- if strings.Contains(err.Error(), mysql.ERUnknownSystemVariable.ToString()) {
- log.Warningf("server does not know about super_read_only, continuing anyway...")
- } else {
- return nil, err
- }
- }
- } else {
- if err := tm.MysqlDaemon.SetReadOnly(true); err != nil {
+ if _, err := tm.MysqlDaemon.SetSuperReadOnly(true); err != nil {
+ if sqlErr, ok := err.(*mysql.SQLError); ok && sqlErr.Number() == mysql.ERUnknownSystemVariable {
+ log.Warningf("server does not know about super_read_only, continuing anyway...")
+ } else {
return nil, err
}
}
+
defer func() {
if finalErr != nil && revertPartialFailure && !wasReadOnly {
// setting read_only OFF will also set super_read_only OFF if it was set
diff --git a/go/vt/vttablet/tabletmanager/tm_init.go b/go/vt/vttablet/tabletmanager/tm_init.go
index c4e1c425667..06f42e8bee6 100644
--- a/go/vt/vttablet/tabletmanager/tm_init.go
+++ b/go/vt/vttablet/tabletmanager/tm_init.go
@@ -96,7 +96,6 @@ func registerInitFlags(fs *pflag.FlagSet) {
fs.StringVar(&initDbNameOverride, "init_db_name_override", initDbNameOverride, "(init parameter) override the name of the db used by vttablet. Without this flag, the db name defaults to vt_")
fs.StringVar(&skipBuildInfoTags, "vttablet_skip_buildinfo_tags", skipBuildInfoTags, "comma-separated list of buildinfo tags to skip from merging with --init_tags. each tag is either an exact match or a regular expression of the form '/regexp/'.")
fs.Var(&initTags, "init_tags", "(init parameter) comma separated list of key:value pairs used to tag the tablet")
-
fs.BoolVar(&initPopulateMetadata, "init_populate_metadata", initPopulateMetadata, "(init parameter) populate metadata tables even if restore_from_backup is disabled. If restore_from_backup is enabled, metadata tables are always populated regardless of this flag.")
fs.MarkDeprecated("init_populate_metadata", "this flag is no longer being used and will be removed in future versions")
fs.DurationVar(&initTimeout, "init_timeout", initTimeout, "(init parameter) timeout to use for the init phase.")
diff --git a/proto/replicationdata.proto b/proto/replicationdata.proto
index 536ea2c4d13..2f98e30576f 100644
--- a/proto/replicationdata.proto
+++ b/proto/replicationdata.proto
@@ -93,4 +93,5 @@ message FullStatus {
uint32 semi_sync_primary_clients = 18;
uint64 semi_sync_primary_timeout = 19;
uint32 semi_sync_wait_for_replica_count = 20;
+ bool super_read_only = 21;
}
diff --git a/web/vtadmin/src/proto/vtadmin.d.ts b/web/vtadmin/src/proto/vtadmin.d.ts
index b05b8420064..79f8c2ead63 100644
--- a/web/vtadmin/src/proto/vtadmin.d.ts
+++ b/web/vtadmin/src/proto/vtadmin.d.ts
@@ -32005,6 +32005,9 @@ export namespace replicationdata {
/** FullStatus semi_sync_wait_for_replica_count */
semi_sync_wait_for_replica_count?: (number|null);
+
+ /** FullStatus super_read_only */
+ super_read_only?: (boolean|null);
}
/** Represents a FullStatus. */
@@ -32076,6 +32079,9 @@ export namespace replicationdata {
/** FullStatus semi_sync_wait_for_replica_count. */
public semi_sync_wait_for_replica_count: number;
+ /** FullStatus super_read_only. */
+ public super_read_only: boolean;
+
/**
* Creates a new FullStatus instance using the specified properties.
* @param [properties] Properties to set
diff --git a/web/vtadmin/src/proto/vtadmin.js b/web/vtadmin/src/proto/vtadmin.js
index 9f05ca795a5..0e32a463451 100644
--- a/web/vtadmin/src/proto/vtadmin.js
+++ b/web/vtadmin/src/proto/vtadmin.js
@@ -75777,6 +75777,7 @@ $root.replicationdata = (function() {
* @property {number|null} [semi_sync_primary_clients] FullStatus semi_sync_primary_clients
* @property {number|Long|null} [semi_sync_primary_timeout] FullStatus semi_sync_primary_timeout
* @property {number|null} [semi_sync_wait_for_replica_count] FullStatus semi_sync_wait_for_replica_count
+ * @property {boolean|null} [super_read_only] FullStatus super_read_only
*/
/**
@@ -75954,6 +75955,14 @@ $root.replicationdata = (function() {
*/
FullStatus.prototype.semi_sync_wait_for_replica_count = 0;
+ /**
+ * FullStatus super_read_only.
+ * @member {boolean} super_read_only
+ * @memberof replicationdata.FullStatus
+ * @instance
+ */
+ FullStatus.prototype.super_read_only = false;
+
/**
* Creates a new FullStatus instance using the specified properties.
* @function create
@@ -76018,6 +76027,8 @@ $root.replicationdata = (function() {
writer.uint32(/* id 19, wireType 0 =*/152).uint64(message.semi_sync_primary_timeout);
if (message.semi_sync_wait_for_replica_count != null && Object.hasOwnProperty.call(message, "semi_sync_wait_for_replica_count"))
writer.uint32(/* id 20, wireType 0 =*/160).uint32(message.semi_sync_wait_for_replica_count);
+ if (message.super_read_only != null && Object.hasOwnProperty.call(message, "super_read_only"))
+ writer.uint32(/* id 21, wireType 0 =*/168).bool(message.super_read_only);
return writer;
};
@@ -76112,6 +76123,9 @@ $root.replicationdata = (function() {
case 20:
message.semi_sync_wait_for_replica_count = reader.uint32();
break;
+ case 21:
+ message.super_read_only = reader.bool();
+ break;
default:
reader.skipType(tag & 7);
break;
@@ -76211,6 +76225,9 @@ $root.replicationdata = (function() {
if (message.semi_sync_wait_for_replica_count != null && message.hasOwnProperty("semi_sync_wait_for_replica_count"))
if (!$util.isInteger(message.semi_sync_wait_for_replica_count))
return "semi_sync_wait_for_replica_count: integer expected";
+ if (message.super_read_only != null && message.hasOwnProperty("super_read_only"))
+ if (typeof message.super_read_only !== "boolean")
+ return "super_read_only: boolean expected";
return null;
};
@@ -76279,6 +76296,8 @@ $root.replicationdata = (function() {
message.semi_sync_primary_timeout = new $util.LongBits(object.semi_sync_primary_timeout.low >>> 0, object.semi_sync_primary_timeout.high >>> 0).toNumber(true);
if (object.semi_sync_wait_for_replica_count != null)
message.semi_sync_wait_for_replica_count = object.semi_sync_wait_for_replica_count >>> 0;
+ if (object.super_read_only != null)
+ message.super_read_only = Boolean(object.super_read_only);
return message;
};
@@ -76320,6 +76339,7 @@ $root.replicationdata = (function() {
} else
object.semi_sync_primary_timeout = options.longs === String ? "0" : 0;
object.semi_sync_wait_for_replica_count = 0;
+ object.super_read_only = false;
}
if (message.server_id != null && message.hasOwnProperty("server_id"))
object.server_id = message.server_id;
@@ -76364,6 +76384,8 @@ $root.replicationdata = (function() {
object.semi_sync_primary_timeout = options.longs === String ? $util.Long.prototype.toString.call(message.semi_sync_primary_timeout) : options.longs === Number ? new $util.LongBits(message.semi_sync_primary_timeout.low >>> 0, message.semi_sync_primary_timeout.high >>> 0).toNumber(true) : message.semi_sync_primary_timeout;
if (message.semi_sync_wait_for_replica_count != null && message.hasOwnProperty("semi_sync_wait_for_replica_count"))
object.semi_sync_wait_for_replica_count = message.semi_sync_wait_for_replica_count;
+ if (message.super_read_only != null && message.hasOwnProperty("super_read_only"))
+ object.super_read_only = message.super_read_only;
return object;
};