From 4f5ab229213de475cb4d46f524f566ebf89167fa Mon Sep 17 00:00:00 2001 From: Rohit Nayak <57520317+rohit-nayak-ps@users.noreply.github.com> Date: Wed, 1 Feb 2023 10:50:14 +0100 Subject: [PATCH] vttablet sidecar schema:use schemadiff to reach desired schema on tablet init replacing the withDDL-based approach (#11520) * Add sidecardb module based schema init. Remove metatables. Conditionally disable withddl/health streamer schema init. Fix tests as needed. Signed-off-by: Rohit Nayak * Implement review comments Signed-off-by: Rohit Nayak * update to column type Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> * Fix bug where we were using _vt before creating it. Added e2e test to CI Signed-off-by: Rohit Nayak * Reinstate creating vtdatabase optionally Signed-off-by: Rohit Nayak * Address review comments Signed-off-by: Rohit Nayak * Fix missed function rename Signed-off-by: Rohit Nayak * Use schemadiff's new ignore charset/collate hints Signed-off-by: Rohit Nayak * Add comments to schema files. Normalize schema while reading from file to get rid of the comments and make related changes to tests Signed-off-by: Rohit Nayak * Refactor e2e test Signed-off-by: Rohit Nayak * Addressed more review comments Signed-off-by: Rohit Nayak * Moved sidecar test helpers back to package since it is used in vtexplain Signed-off-by: Rohit Nayak * Fix issues with setting up fakesqldb test queries Signed-off-by: Rohit Nayak * Address new review comments Signed-off-by: Rohit Nayak * Refactor the way we first create tables Signed-off-by: Rohit Nayak --------- Signed-off-by: Rohit Nayak Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> Co-authored-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> --- config/init_db.sql | 17 - doc/releasenotes/16_0_0_summary.md | 18 + go.mod | 10 +- go.sum | 19 +- go/cmd/vtbackup/vtbackup.go | 14 +- go/cmd/vttablet/vttablet.go | 1 - go/flags/endtoend/vttablet.txt | 1 - go/mysql/endtoend/schema_change_test.go | 6 - go/mysql/fakesqldb/server.go | 19 +- go/mysql/schema.go | 30 -- .../backup/vtbackup/backup_only_test.go | 24 +- .../backup/vtctlbackup/backup_utils.go | 5 - go/test/endtoend/cluster/cluster_util.go | 28 +- go/test/endtoend/cluster/mysqlctld_process.go | 2 +- .../recovery/unshardedrecovery/recovery.go | 2 - .../reparent/emergencyreparent/ers_test.go | 1 - .../reparent/plannedreparent/reparent_test.go | 54 ++- .../endtoend/reparent/prscomplex/main_test.go | 2 +- go/test/endtoend/reparent/utils/utils.go | 2 - .../replication_manager/tablet_test.go | 19 +- go/test/endtoend/tabletmanager/tablet_test.go | 60 +-- go/test/endtoend/vreplication/cluster_test.go | 4 +- .../endtoend/vreplication/sidecardb_test.go | 138 ++++++ .../loadkeyspace/schema_load_keyspace_test.go | 39 -- go/vt/binlog/binlogplayer/binlog_player.go | 72 ---- go/vt/binlog/binlogplayer/mock_dbclient.go | 52 +-- go/vt/dbconfigs/dbconfigs.go | 2 +- go/vt/mysqlctl/backup.go | 18 - go/vt/mysqlctl/backupengine.go | 4 +- go/vt/mysqlctl/metadata_tables.go | 234 ---------- go/vt/mysqlctl/reparent.go | 34 +- go/vt/mysqlctl/replication.go | 11 + go/vt/servenv/exporter.go | 3 + go/vt/servenv/servenv.go | 8 + go/vt/sidecardb/doc.go | 40 ++ go/vt/sidecardb/schema/misc/heartbeat.sql | 23 + .../schema/misc/reparent_journal.sql | 25 ++ go/vt/sidecardb/schema/misc/views.sql | 24 ++ .../schema/onlineddl/schema_migrations.sql | 82 ++++ .../schema/schematracker/schemacopy.sql | 28 ++ .../sidecardb/schema/twopc/dt_participant.sql | 24 ++ go/vt/sidecardb/schema/twopc/dt_state.sql | 23 + go/vt/sidecardb/schema/twopc/redo_state.sql | 22 + .../sidecardb/schema/twopc/redo_statement.sql | 22 + go/vt/sidecardb/schema/vdiff/vdiff.sql | 36 ++ go/vt/sidecardb/schema/vdiff/vdiff_log.sql | 24 ++ go/vt/sidecardb/schema/vdiff/vdiff_table.sql | 30 ++ .../schema/vreplication/copy_state.sql | 25 ++ .../schema/vreplication/post_copy_action.sql | 24 ++ .../vreplication/resharding_journal.sql | 23 + .../schema/vreplication/schema_version.sql | 25 ++ .../schema/vreplication/vreplication.sql | 43 ++ .../schema/vreplication/vreplication_log.sql | 28 ++ go/vt/sidecardb/sidecardb.go | 399 ++++++++++++++++++ go/vt/sidecardb/sidecardb_test.go | 108 +++++ .../endtoend/init_shard_primary_test.go | 16 +- go/vt/vtctl/grpcvtctldserver/server.go | 15 +- go/vt/vtctl/grpcvtctldserver/server_test.go | 15 +- go/vt/vtctl/schematools/copy.go | 101 ----- .../uneven-keyspace-output.txt | 16 + go/vt/vtexplain/vtexplain_vtgate.go | 2 +- go/vt/vtexplain/vtexplain_vttablet.go | 18 +- go/vt/vtgate/querylog.go | 12 +- go/vt/vtgate/vschemaacl/vschemaacl.go | 5 + go/vt/vttablet/onlineddl/executor.go | 58 +-- go/vt/vttablet/onlineddl/schema.go | 117 ----- go/vt/vttablet/tabletmanager/restore.go | 11 +- .../vttablet/tabletmanager/rpc_replication.go | 36 +- go/vt/vttablet/tabletmanager/tm_init.go | 33 +- go/vt/vttablet/tabletmanager/tm_init_test.go | 13 - go/vt/vttablet/tabletmanager/tm_state.go | 53 +-- go/vt/vttablet/tabletmanager/vdiff/action.go | 5 - go/vt/vttablet/tabletmanager/vdiff/engine.go | 4 +- .../tabletmanager/vdiff/framework_test.go | 9 +- go/vt/vttablet/tabletmanager/vdiff/schema.go | 64 --- .../tabletmanager/vreplication/controller.go | 5 - .../tabletmanager/vreplication/engine.go | 67 +-- .../tabletmanager/vreplication/engine_test.go | 101 ----- .../vreplication/framework_test.go | 52 +-- .../vreplication/journal_test.go | 19 +- .../vreplication/queryhistory/verifier.go | 4 +- .../tabletmanager/vreplication/utils.go | 17 +- .../vreplication/vplayer_flaky_test.go | 7 - .../tabletmanager/vreplication/vreplicator.go | 6 +- .../vttablet/tabletserver/health_streamer.go | 19 - .../tabletserver/health_streamer_test.go | 9 - go/vt/vttablet/tabletserver/output.txt | 0 go/vt/vttablet/tabletserver/query_executor.go | 12 +- .../tabletserver/query_executor_test.go | 14 +- .../tabletserver/repltracker/writer.go | 15 +- .../tabletserver/repltracker/writer_test.go | 25 -- go/vt/vttablet/tabletserver/report.xml | 0 go/vt/vttablet/tabletserver/schema/engine.go | 52 ++- go/vt/vttablet/tabletserver/schema/tracker.go | 24 +- go/vt/vttablet/tabletserver/state_manager.go | 5 + go/vt/vttablet/tabletserver/tabletserver.go | 3 +- .../tabletserver/tabletserver_test.go | 14 +- go/vt/vttablet/tabletserver/twopc.go | 56 +-- go/vt/vttablet/tabletserver/tx_engine.go | 2 +- .../vstreamer/vstreamer_flaky_test.go | 5 +- go/vt/vttest/local_cluster.go | 26 +- go/vt/vttest/mysqlctl.go | 4 +- go/vt/withddl/withddl.go | 145 ------- go/vt/withddl/withddl_test.go | 316 -------------- go/vt/wrangler/resharder_test.go | 4 - go/vt/wrangler/schema.go | 5 - go/vt/wrangler/testlib/backup_test.go | 36 +- .../testlib/copy_schema_shard_test.go | 27 -- .../testlib/emergency_reparent_shard_test.go | 6 - .../testlib/planned_reparent_shard_test.go | 36 +- test/config.json | 9 + 111 files changed, 1642 insertions(+), 2139 deletions(-) create mode 100644 go/test/endtoend/vreplication/sidecardb_test.go delete mode 100644 go/vt/mysqlctl/metadata_tables.go create mode 100644 go/vt/sidecardb/doc.go create mode 100644 go/vt/sidecardb/schema/misc/heartbeat.sql create mode 100644 go/vt/sidecardb/schema/misc/reparent_journal.sql create mode 100644 go/vt/sidecardb/schema/misc/views.sql create mode 100644 go/vt/sidecardb/schema/onlineddl/schema_migrations.sql create mode 100644 go/vt/sidecardb/schema/schematracker/schemacopy.sql create mode 100644 go/vt/sidecardb/schema/twopc/dt_participant.sql create mode 100644 go/vt/sidecardb/schema/twopc/dt_state.sql create mode 100644 go/vt/sidecardb/schema/twopc/redo_state.sql create mode 100644 go/vt/sidecardb/schema/twopc/redo_statement.sql create mode 100644 go/vt/sidecardb/schema/vdiff/vdiff.sql create mode 100644 go/vt/sidecardb/schema/vdiff/vdiff_log.sql create mode 100644 go/vt/sidecardb/schema/vdiff/vdiff_table.sql create mode 100644 go/vt/sidecardb/schema/vreplication/copy_state.sql create mode 100644 go/vt/sidecardb/schema/vreplication/post_copy_action.sql create mode 100644 go/vt/sidecardb/schema/vreplication/resharding_journal.sql create mode 100644 go/vt/sidecardb/schema/vreplication/schema_version.sql create mode 100644 go/vt/sidecardb/schema/vreplication/vreplication.sql create mode 100644 go/vt/sidecardb/schema/vreplication/vreplication_log.sql create mode 100644 go/vt/sidecardb/sidecardb.go create mode 100644 go/vt/sidecardb/sidecardb_test.go delete mode 100644 go/vt/vtctl/schematools/copy.go create mode 100644 go/vt/vtexplain/testdata/plan_test1941596715/uneven-keyspace-output.txt create mode 100644 go/vt/vttablet/tabletserver/output.txt create mode 100644 go/vt/vttablet/tabletserver/report.xml delete mode 100644 go/vt/withddl/withddl.go delete mode 100644 go/vt/withddl/withddl_test.go diff --git a/config/init_db.sql b/config/init_db.sql index 67a41d98c4b..7be4de6f7ea 100644 --- a/config/init_db.sql +++ b/config/init_db.sql @@ -28,23 +28,6 @@ DROP DATABASE IF EXISTS test; # Vitess defaults ############################################################################### -# Vitess-internal database. -CREATE DATABASE IF NOT EXISTS _vt; -# Note that definitions of local_metadata and shard_metadata should be the same -# as in production which is defined in go/vt/mysqlctl/metadata_tables.go. -CREATE TABLE IF NOT EXISTS _vt.local_metadata ( - name VARCHAR(255) NOT NULL, - value VARCHAR(255) NOT NULL, - db_name VARBINARY(255) NOT NULL, - PRIMARY KEY (db_name, name) - ) ENGINE=InnoDB; -CREATE TABLE IF NOT EXISTS _vt.shard_metadata ( - name VARCHAR(255) NOT NULL, - value MEDIUMBLOB NOT NULL, - db_name VARBINARY(255) NOT NULL, - PRIMARY KEY (db_name, name) - ) ENGINE=InnoDB; - # Admin user with all privileges. CREATE USER 'vt_dba'@'localhost'; GRANT ALL ON *.* TO 'vt_dba'@'localhost'; diff --git a/doc/releasenotes/16_0_0_summary.md b/doc/releasenotes/16_0_0_summary.md index 865b62b4da7..c1d66e71698 100644 --- a/doc/releasenotes/16_0_0_summary.md +++ b/doc/releasenotes/16_0_0_summary.md @@ -280,6 +280,8 @@ is now fixed. The full issue can be found [here](https://github.com/vitessio/vit - vtbackup flag `--backup_storage_hook` has been removed, use one of the builtin compression algorithms or `--external-compressor` and `--external-decompressor` instead. +- The VTTablet flag `--init_populate_metadata` has been deprecated, since we have deleted the `local_metadata` and `shard_metadata` sidecar database tables. + - The dead legacy Workflow Manager related code was removed in [#12085](https://github.com/vitessio/vitess/pull/12085). This included the following `vtctl` client commands: `WorkflowAction`, `WorkflowCreate`, `WorkflowWait`, `WorkflowStart`, `WorkflowStop`, `WorkflowTree`, `WorkflowDelete`. - VTAdmin's `VTExplain` endpoint has been deprecated. Users can use the new `vexplain` query format instead. The endpoint will be deleted in a future release. @@ -394,3 +396,19 @@ BenchmarkCompressLz4Builtin PASS cleaning up "/var/folders/96/k7gzd7q10zdb749vr02q7sjh0000gn/T/ee7d47b45ef09786c54fa2d7354d2a68.dat" ``` + +## Refactor + +### VTTablet sidecar schema maintenance refactor + +This is an internal refactor and should not change the behavior of Vitess as seen by users. + +Developers will see a difference though: v16 changes the way we maintain vttablet's sidecar database schema (also referred to as the `_vt` +database). Instead of using the `WithDDL` package, introduced in #6348, we use a declarative approach. Users will now have to update +the desired schema in the `go/vt/sidecardb/schema` directory. + +The desired schema is specified, one per table. A new module `sidecardb`, compares this to the existing schema and +performs the required `create` or `alter` to reach it. This is done whenever a primary vttablet starts up. + +The sidecar tables `local_metadata` and `shard_metadata` are no longer in use and all references to them are removed as +part of this refactor. There were used previously for Orchestrator support, which has been superseded by `vtorc`. diff --git a/go.mod b/go.mod index e898ce54865..85b9213e887 100644 --- a/go.mod +++ b/go.mod @@ -80,13 +80,13 @@ require ( golang.org/x/crypto v0.3.0 // indirect golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 golang.org/x/mod v0.7.0 // indirect - golang.org/x/net v0.2.0 + golang.org/x/net v0.3.0 golang.org/x/oauth2 v0.2.0 - golang.org/x/sys v0.2.0 // indirect - golang.org/x/term v0.2.0 - golang.org/x/text v0.4.0 + golang.org/x/sys v0.3.0 // indirect + golang.org/x/term v0.3.0 + golang.org/x/text v0.5.0 golang.org/x/time v0.2.0 - golang.org/x/tools v0.3.0 + golang.org/x/tools v0.4.0 google.golang.org/api v0.103.0 google.golang.org/genproto v0.0.0-20221116193143-41c2ba794472 // indirect google.golang.org/grpc v1.50.1 diff --git a/go.sum b/go.sum index c5e221f771b..b2328bac129 100644 --- a/go.sum +++ b/go.sum @@ -953,8 +953,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= -golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk= +golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1061,13 +1061,13 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= +golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= +golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1078,8 +1078,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= +golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1146,8 +1147,8 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM= -golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= +golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4= +golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/go/cmd/vtbackup/vtbackup.go b/go/cmd/vtbackup/vtbackup.go index 4de11422deb..eba97493170 100644 --- a/go/cmd/vtbackup/vtbackup.go +++ b/go/cmd/vtbackup/vtbackup.go @@ -74,7 +74,6 @@ import ( "vitess.io/vitess/go/cmd" "vitess.io/vitess/go/exit" "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/sqlescape" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logutil" @@ -159,7 +158,6 @@ func main() { servenv.ParseFlags("vtbackup") servenv.Init() - ctx, cancel := context.WithCancel(context.Background()) servenv.OnClose(func() { cancel() @@ -304,16 +302,11 @@ 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) } - cmds := mysqlctl.CreateReparentJournal() - cmds = append(cmds, fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s", sqlescape.EscapeID(dbName))) - if err := mysqld.ExecuteSuperQueryList(ctx, cmds); err != nil { - return fmt.Errorf("can't initialize database: %v", err) + cmd := mysqlctl.GenerateInitialBinlogEntry() + if err := mysqld.ExecuteSuperQueryList(ctx, []string{cmd}); err != nil { + return err } - // Execute Alter commands on reparent_journal and ignore errors - cmds = mysqlctl.AlterReparentJournal() - _ = mysqld.ExecuteSuperQueryList(ctx, cmds) - backupParams.BackupTime = time.Now() // Now we're ready to take the backup. if err := mysqlctl.Backup(ctx, backupParams); err != nil { @@ -331,7 +324,6 @@ func takeBackup(ctx context.Context, topoServer *topo.Server, backupStorage back Logger: logutil.NewConsoleLogger(), Concurrency: concurrency, HookExtraEnv: extraEnv, - LocalMetadata: map[string]string{}, DeleteBeforeRestore: true, DbName: dbName, Keyspace: initKeyspace, diff --git a/go/cmd/vttablet/vttablet.go b/go/cmd/vttablet/vttablet.go index f9c43bd2a2a..c7ee81511d5 100644 --- a/go/cmd/vttablet/vttablet.go +++ b/go/cmd/vttablet/vttablet.go @@ -123,7 +123,6 @@ func main() { UpdateStream: binlog.NewUpdateStream(ts, tablet.Keyspace, tabletAlias.Cell, qsc.SchemaEngine()), VREngine: vreplication.NewEngine(config, ts, tabletAlias.Cell, mysqld, qsc.LagThrottler()), VDiffEngine: vdiff.NewEngine(config, ts, tablet), - MetadataManager: &mysqlctl.MetadataManager{}, } if err := tm.Start(tablet, config.Healthcheck.IntervalSeconds.Get()); err != nil { log.Exitf("failed to parse --tablet-path or initialize DB credentials: %v", err) diff --git a/go/flags/endtoend/vttablet.txt b/go/flags/endtoend/vttablet.txt index 98b672b3877..ed8c0f434a6 100644 --- a/go/flags/endtoend/vttablet.txt +++ b/go/flags/endtoend/vttablet.txt @@ -151,7 +151,6 @@ Usage of vttablet: --hot_row_protection_max_queue_size int Maximum number of BeginExecute RPCs which will be queued for the same row (range). (default 20) --init_db_name_override string (init parameter) override the name of the db used by vttablet. Without this flag, the db name defaults to vt_ --init_keyspace string (init parameter) keyspace to use for this tablet - --init_populate_metadata (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. --init_shard string (init parameter) shard to use for this tablet --init_tablet_type string (init parameter) the tablet type to use for this tablet. --init_tags StringMap (init parameter) comma separated list of key:value pairs used to tag the tablet diff --git a/go/mysql/endtoend/schema_change_test.go b/go/mysql/endtoend/schema_change_test.go index 39e1275dc08..7e58852d176 100644 --- a/go/mysql/endtoend/schema_change_test.go +++ b/go/mysql/endtoend/schema_change_test.go @@ -32,7 +32,6 @@ import ( var ctx = context.Background() const ( - createDb = `create database if not exists _vt` createUserTable = `create table vttest.product (id bigint(20) primary key, name char(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci, created bigint(20))` dropTestTable = `drop table if exists product` ) @@ -42,11 +41,6 @@ func TestChangeSchemaIsNoticed(t *testing.T) { require.NoError(t, err) defer conn.Close() - _, err = conn.ExecuteFetch(createDb, 1000, true) - require.NoError(t, err) - _, err = conn.ExecuteFetch(mysql.CreateSchemaCopyTable, 1000, true) - require.NoError(t, err) - tests := []struct { name string changeQ string diff --git a/go/mysql/fakesqldb/server.go b/go/mysql/fakesqldb/server.go index aae66bc22af..5b00b2c2e01 100644 --- a/go/mysql/fakesqldb/server.go +++ b/go/mysql/fakesqldb/server.go @@ -23,12 +23,13 @@ import ( "os" "path" "regexp" - "runtime/debug" "strings" "sync" "testing" "time" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/mysql" @@ -137,9 +138,10 @@ type ExpectedResult struct { } type exprResult struct { - expr *regexp.Regexp - result *sqltypes.Result - err string + queryPattern string + expr *regexp.Regexp + result *sqltypes.Result + err string } // ExpectedExecuteFetch defines for an expected query the to be faked output. @@ -414,8 +416,9 @@ func (db *DB) HandleQuery(c *mysql.Conn, query string, callback func(*sqltypes.R return callback(&sqltypes.Result{}) } // Nothing matched. - err := fmt.Errorf("fakesqldb:: query: '%s' is not supported on %v", query, db.name) - log.Errorf("Query not found: %s:%s", query, debug.Stack()) + err := fmt.Errorf("fakesqldb:: query: '%s' is not supported on %v", + sqlparser.TruncateForUI(query), db.name) + log.Errorf("Query not found: %s", sqlparser.TruncateForUI(query)) return err } @@ -557,7 +560,7 @@ func (db *DB) AddQueryPattern(queryPattern string, expectedResult *sqltypes.Resu result := *expectedResult db.mu.Lock() defer db.mu.Unlock() - db.patternData[queryPattern] = exprResult{expr: expr, result: &result} + db.patternData[queryPattern] = exprResult{queryPattern: queryPattern, expr: expr, result: &result} } // RejectQueryPattern allows a query pattern to be rejected with an error @@ -565,7 +568,7 @@ func (db *DB) RejectQueryPattern(queryPattern, error string) { expr := regexp.MustCompile("(?is)^" + queryPattern + "$") db.mu.Lock() defer db.mu.Unlock() - db.patternData[queryPattern] = exprResult{expr: expr, err: error} + db.patternData[queryPattern] = exprResult{queryPattern: queryPattern, expr: expr, err: error} } // ClearQueryPattern removes all query patterns set up diff --git a/go/mysql/schema.go b/go/mysql/schema.go index 62f1e446d38..5029dd86ef7 100644 --- a/go/mysql/schema.go +++ b/go/mysql/schema.go @@ -40,22 +40,6 @@ const ( // ShowRowsRead is the query used to find the number of rows read. ShowRowsRead = "show status like 'Innodb_rows_read'" - // CreateVTDatabase creates the _vt database - CreateVTDatabase = `CREATE DATABASE IF NOT EXISTS _vt` - - // CreateSchemaCopyTable query creates schemacopy table in _vt schema. - CreateSchemaCopyTable = ` -CREATE TABLE if not exists _vt.schemacopy ( - table_schema varchar(64) NOT NULL, - table_name varchar(64) NOT NULL, - column_name varchar(64) NOT NULL, - ordinal_position bigint(21) unsigned NOT NULL, - character_set_name varchar(32) DEFAULT NULL, - collation_name varchar(32) DEFAULT NULL, - data_type varchar(64) NOT NULL, - column_key varchar(3) NOT NULL, - PRIMARY KEY (table_schema, table_name, ordinal_position))` - // DetectSchemaChange query detects if there is any schema change from previous copy. DetectSchemaChange = ` SELECT DISTINCT table_name @@ -103,13 +87,6 @@ order by table_name, ordinal_position` GetColumnNamesQueryPatternForTable = `SELECT COLUMN_NAME.*TABLE_NAME.*%s.*` // Views - CreateViewsTable = `CREATE TABLE IF NOT EXISTS _vt.views ( - TABLE_NAME varchar(64) NOT NULL, - VIEW_DEFINITION longtext NOT NULL, - CREATE_STATEMENT longtext NOT NULL, - UPDATED_AT TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - PRIMARY KEY (TABLE_NAME))` - InsertIntoViewsTable = `insert into _vt.views ( table_name, view_definition, @@ -137,13 +114,6 @@ order by table_name, ordinal_position` FetchViews = `select table_name, view_definition, create_statement from _vt.views` ) -// VTDatabaseInit contains all the schema creation queries needed to -var VTDatabaseInit = []string{ - CreateVTDatabase, - CreateSchemaCopyTable, - CreateViewsTable, -} - // BaseShowTablesFields contains the fields returned by a BaseShowTables or a BaseShowTablesForTable command. // They are validated by the // testBaseShowTables test. diff --git a/go/test/endtoend/backup/vtbackup/backup_only_test.go b/go/test/endtoend/backup/vtbackup/backup_only_test.go index 39430733e1c..3730a1fa586 100644 --- a/go/test/endtoend/backup/vtbackup/backup_only_test.go +++ b/go/test/endtoend/backup/vtbackup/backup_only_test.go @@ -36,7 +36,7 @@ import ( var ( vtInsertTest = ` - create table vt_insert_test ( + create table if not exists vt_insert_test ( id bigint auto_increment, msg varchar(64), primary key (id) @@ -63,8 +63,14 @@ func TestTabletInitialBackup(t *testing.T) { initTablets(t, false, false) // Restore the Tablets + 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( + "SetReadWrite", primary.Alias) + require.Nil(t, err) + err = localCluster.VtctlclientProcess.ExecuteCommand( "TabletExternallyReparented", primary.Alias) require.Nil(t, err) restore(t, replica1, "replica", "SERVING") @@ -137,7 +143,7 @@ func firstBackupTest(t *testing.T, tabletType string) { require.Nil(t, err) cluster.VerifyRowsInTablet(t, replica1, keyspaceName, 2) - // eventhough we change the value of compression it won't effect + // even though we change the value of compression it won't affect // decompression since it gets its value from MANIFEST file, created // as part of backup. mysqlctl.CompressionEngineName = "lz4" @@ -151,20 +157,6 @@ func firstBackupTest(t *testing.T, tabletType string) { //check the new replica has the data cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 2) - // check that the restored replica has the right local_metadata - result, err := replica2.VttabletProcess.QueryTabletWithDB("select * from local_metadata", "_vt") - require.Nil(t, err) - require.NotNil(t, result) - require.NotEmpty(t, result.Rows) - assert.Equal(t, replica2.Alias, result.Rows[0][1].ToString(), "Alias") - assert.Equal(t, "ks.0", result.Rows[1][1].ToString(), "ClusterAlias") - assert.Equal(t, cell, result.Rows[2][1].ToString(), "DataCenter") - if tabletType == "replica" { - assert.Equal(t, "neutral", result.Rows[3][1].ToString(), "PromotionRule") - } else { - assert.Equal(t, "must_not", result.Rows[3][1].ToString(), "PromotionRule") - } - removeBackups(t) verifyBackupCount(t, shardKsName, 0) } diff --git a/go/test/endtoend/backup/vtctlbackup/backup_utils.go b/go/test/endtoend/backup/vtctlbackup/backup_utils.go index 85b0c88450d..69334b51637 100644 --- a/go/test/endtoend/backup/vtctlbackup/backup_utils.go +++ b/go/test/endtoend/backup/vtctlbackup/backup_utils.go @@ -409,7 +409,6 @@ func primaryBackup(t *testing.T) { // Verify that we have all the new data -- we should have 2 records now... // And only 1 record after we restore using the first backup timestamp cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 2) - cluster.VerifyLocalMetadata(t, replica2, keyspaceName, shardName, cell) err = localCluster.VtctlclientProcess.ExecuteCommand("Backup", "--", "--allow_primary=true", primary.Alias) require.Nil(t, err) @@ -441,7 +440,6 @@ func primaryBackup(t *testing.T) { // Verify that we don't have the record created after the older/first backup cluster.VerifyRowsInTablet(t, primary, keyspaceName, 1) - cluster.VerifyLocalMetadata(t, primary, keyspaceName, shardName, cell) verifyAfterRemovingBackupNoBackupShouldBePresent(t, backups) require.Nil(t, err) @@ -793,7 +791,6 @@ func vtctlBackup(t *testing.T, tabletType string) { require.Nil(t, err) cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 2) - cluster.VerifyLocalMetadata(t, replica2, keyspaceName, shardName, cell) verifyAfterRemovingBackupNoBackupShouldBePresent(t, backups) // Stop VTOrc @@ -944,8 +941,6 @@ func vtctlBackupReplicaNoDestroyNoWrites(t *testing.T, tabletType string) (backu err = replica2.VttabletProcess.WaitForTabletStatusesForTimeout([]string{"SERVING"}, 25*time.Second) require.Nil(t, err) - cluster.VerifyLocalMetadata(t, replica2, keyspaceName, shardName, cell) - err = replica2.VttabletProcess.TearDown() require.Nil(t, err) diff --git a/go/test/endtoend/cluster/cluster_util.go b/go/test/endtoend/cluster/cluster_util.go index 8d416380292..b7f3e33e716 100644 --- a/go/test/endtoend/cluster/cluster_util.go +++ b/go/test/endtoend/cluster/cluster_util.go @@ -95,16 +95,20 @@ func GetPrimaryPosition(t *testing.T, vttablet Vttablet, hostname string) (strin // This is used to check that replication has caught up with the changes on primary. func VerifyRowsInTabletForTable(t *testing.T, vttablet *Vttablet, ksName string, expectedRows int, tableName string) { timeout := time.Now().Add(1 * time.Minute) + lastNumRowsFound := 0 for time.Now().Before(timeout) { // ignoring the error check, if the newly created table is not replicated, then there might be error and we should ignore it // but eventually it will catch up and if not caught up in required time, testcase will fail qr, _ := vttablet.VttabletProcess.QueryTablet("select * from "+tableName, ksName, true) - if qr != nil && len(qr.Rows) == expectedRows { - return + if qr != nil { + if len(qr.Rows) == expectedRows { + return + } + lastNumRowsFound = len(qr.Rows) } time.Sleep(300 * time.Millisecond) } - assert.Fail(t, "expected rows not found.") + require.Equalf(t, expectedRows, lastNumRowsFound, "unexpected number of rows in %s (%s.%s)", vttablet.Alias, ksName, tableName) } // VerifyRowsInTablet Verify total number of rows in a tablet @@ -121,20 +125,6 @@ func PanicHandler(t *testing.T) { require.Nilf(t, err, "panic occured in testcase %v", t.Name()) } -// VerifyLocalMetadata Verify Local Metadata of a tablet -func VerifyLocalMetadata(t *testing.T, tablet *Vttablet, ksName string, shardName string, cell string) { - qr, err := tablet.VttabletProcess.QueryTablet("select * from _vt.local_metadata", ksName, false) - require.Nil(t, err) - assert.Equal(t, fmt.Sprintf("%v", qr.Rows[0][1]), fmt.Sprintf(`BLOB("%s")`, tablet.Alias)) - assert.Equal(t, fmt.Sprintf("%v", qr.Rows[1][1]), fmt.Sprintf(`BLOB("%s.%s")`, ksName, shardName)) - assert.Equal(t, fmt.Sprintf("%v", qr.Rows[2][1]), fmt.Sprintf(`BLOB("%s")`, cell)) - if tablet.Type == "replica" { - assert.Equal(t, fmt.Sprintf("%v", qr.Rows[3][1]), `BLOB("neutral")`) - } else if tablet.Type == "rdonly" { - assert.Equal(t, fmt.Sprintf("%v", qr.Rows[3][1]), `BLOB("must_not")`) - } -} - // ListBackups Lists back preset in shard func (cluster LocalProcessCluster) ListBackups(shardKsName string) ([]string, error) { output, err := cluster.VtctlclientProcess.ExecuteCommandWithOutput("ListBackups", shardKsName) @@ -262,8 +252,8 @@ func NewConnParams(port int, password, socketPath, keyspace string) mysql.ConnPa UnixSocket: socketPath, Pass: password, } - - if keyspace != "" { + cp.DbName = keyspace + if keyspace != "" && keyspace != "_vt" { cp.DbName = "vt_" + keyspace } diff --git a/go/test/endtoend/cluster/mysqlctld_process.go b/go/test/endtoend/cluster/mysqlctld_process.go index 9876dd38797..d71f2e3b1c8 100644 --- a/go/test/endtoend/cluster/mysqlctld_process.go +++ b/go/test/endtoend/cluster/mysqlctld_process.go @@ -85,7 +85,7 @@ func (mysqlctld *MysqlctldProcess) Start() error { tempProcess.Stdout = os.Stdout tempProcess.Stderr = os.Stderr - log.Infof("%v %v", strings.Join(tempProcess.Args, " ")) + log.Infof("%v", strings.Join(tempProcess.Args, " ")) err := tempProcess.Start() if err != nil { diff --git a/go/test/endtoend/recovery/unshardedrecovery/recovery.go b/go/test/endtoend/recovery/unshardedrecovery/recovery.go index 68c66a7bbc0..f9824c23b2f 100644 --- a/go/test/endtoend/recovery/unshardedrecovery/recovery.go +++ b/go/test/endtoend/recovery/unshardedrecovery/recovery.go @@ -225,8 +225,6 @@ func TestRecoveryImpl(t *testing.T) { cluster.VerifyRowsInTablet(t, replica2, keyspaceName, 1) - cluster.VerifyLocalMetadata(t, replica2, recoveryKS1, shardName, cell) - // update the original row in primary _, err = primary.VttabletProcess.QueryTablet("update vt_insert_test set msg = 'msgx1' where id = 1", keyspaceName, true) assert.NoError(t, err) diff --git a/go/test/endtoend/reparent/emergencyreparent/ers_test.go b/go/test/endtoend/reparent/emergencyreparent/ers_test.go index b5ad22b38a4..8f6638ecb7e 100644 --- a/go/test/endtoend/reparent/emergencyreparent/ers_test.go +++ b/go/test/endtoend/reparent/emergencyreparent/ers_test.go @@ -387,7 +387,6 @@ func TestERSForInitialization(t *testing.T) { shard.Vttablets = tablets clusterInstance.VtTabletExtraArgs = []string{ "--lock_tables_timeout", "5s", - "--init_populate_metadata", "--track_schema_versions=true", } diff --git a/go/test/endtoend/reparent/plannedreparent/reparent_test.go b/go/test/endtoend/reparent/plannedreparent/reparent_test.go index 72f560a9bc0..de7e6a0368b 100644 --- a/go/test/endtoend/reparent/plannedreparent/reparent_test.go +++ b/go/test/endtoend/reparent/plannedreparent/reparent_test.go @@ -363,25 +363,6 @@ func TestChangeTypeSemiSync(t *testing.T) { utils.CheckDBstatus(ctx, t, rdonly2, "Rpl_semi_sync_slave_status", "ON") } -func TestReparentDoesntHangIfPrimaryFails(t *testing.T) { - defer cluster.PanicHandler(t) - clusterInstance := utils.SetupReparentCluster(t, "semi_sync") - defer utils.TeardownCluster(clusterInstance) - tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets - - // Change the schema of the _vt.reparent_journal table, so that - // inserts into it will fail. That will make the primary fail. - _, err := tablets[0].VttabletProcess.QueryTabletWithDB( - "ALTER TABLE reparent_journal DROP COLUMN replication_position", "_vt") - require.NoError(t, err) - - // Perform a planned reparent operation, the primary will fail the - // insert. The replicas should then abort right away. - out, err := utils.Prs(t, clusterInstance, tablets[1]) - require.Error(t, err) - assert.Contains(t, out, "primary failed to PopulateReparentJournal") -} - // TestCrossCellDurability tests 2 things - // 1. When PRS is run with the cross_cell durability policy setup, then the semi-sync settings on all the tablets are as expected // 2. Bringing up a new vttablet should have its replication and semi-sync setup correctly without any manual intervention @@ -433,7 +414,8 @@ func TestFullStatus(t *testing.T) { utils.ConfirmReplication(t, tablets[0], []*cluster.Vttablet{tablets[1], tablets[2], tablets[3]}) // Check that full status gives the correct result for a primary tablet - primaryStatusString, err := clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("GetFullStatus", tablets[0].Alias) + primaryTablet := tablets[0] + primaryStatusString, err := clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("GetFullStatus", primaryTablet.Alias) require.NoError(t, err) primaryStatus := &replicationdatapb.FullStatus{} err = protojson.Unmarshal([]byte(primaryStatusString), primaryStatus) @@ -460,8 +442,12 @@ func TestFullStatus(t *testing.T) { assert.Regexp(t, `[58]\.[07].*`, primaryStatus.Version) assert.NotEmpty(t, primaryStatus.VersionComment) + replicaTablet := tablets[1] + + waitForFilePosition(t, clusterInstance, primaryTablet, replicaTablet, 5*time.Second) + // Check that full status gives the correct result for a replica tablet - replicaStatusString, err := clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("GetFullStatus", tablets[1].Alias) + replicaStatusString, err := clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("GetFullStatus", replicaTablet.Alias) require.NoError(t, err) replicaStatus := &replicationdatapb.FullStatus{} err = protojson.Unmarshal([]byte(replicaStatusString), replicaStatus) @@ -509,6 +495,32 @@ func TestFullStatus(t *testing.T) { assert.NotEmpty(t, replicaStatus.VersionComment) } +func getFullStatus(t *testing.T, clusterInstance *cluster.LocalProcessCluster, tablet *cluster.Vttablet) *replicationdatapb.FullStatus { + statusString, err := clusterInstance.VtctldClientProcess.ExecuteCommandWithOutput("GetFullStatus", tablet.Alias) + require.NoError(t, err) + status := &replicationdatapb.FullStatus{} + err = protojson.Unmarshal([]byte(statusString), status) + require.NoError(t, err) + return status +} + +// waitForFilePosition waits for timeout to see if FilePositions align b/w primary and replica, to fix flakiness in tests due to race conditions where replica is still catching up +func waitForFilePosition(t *testing.T, clusterInstance *cluster.LocalProcessCluster, primary *cluster.Vttablet, replica *cluster.Vttablet, timeout time.Duration) { + start := time.Now() + for { + primaryStatus := getFullStatus(t, clusterInstance, primary) + replicaStatus := getFullStatus(t, clusterInstance, replica) + if primaryStatus.PrimaryStatus.FilePosition == replicaStatus.ReplicationStatus.FilePosition { + return + } + if d := time.Since(start); d > timeout { + require.FailNowf(t, "waitForFilePosition timed out, primary %s, replica %s", + primaryStatus.PrimaryStatus.FilePosition, replicaStatus.ReplicationStatus.FilePosition) + } + time.Sleep(100 * time.Millisecond) + } +} + // fileNameFromPosition gets the file name from the position func fileNameFromPosition(pos string) string { return pos[0 : len(pos)-4] diff --git a/go/test/endtoend/reparent/prscomplex/main_test.go b/go/test/endtoend/reparent/prscomplex/main_test.go index 82010a6a19c..88276012781 100644 --- a/go/test/endtoend/reparent/prscomplex/main_test.go +++ b/go/test/endtoend/reparent/prscomplex/main_test.go @@ -148,7 +148,7 @@ func TestAcquireSameConnID(t *testing.T) { } } - // We run the above loop 100 times so we execute 200 queries, off which only some should fail due to MySQL restart. + // We run the above loop 100 times so we execute 200 queries, of which only some should fail due to MySQL restart. assert.Less(t, totalErrCount, 10, "MySQL restart can cause some errors, but not too many.") // prs should happen without any error. diff --git a/go/test/endtoend/reparent/utils/utils.go b/go/test/endtoend/reparent/utils/utils.go index cc0523b4339..c2ab9d48306 100644 --- a/go/test/endtoend/reparent/utils/utils.go +++ b/go/test/endtoend/reparent/utils/utils.go @@ -111,7 +111,6 @@ func setupCluster(ctx context.Context, t *testing.T, shardName string, cells []s clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs, "--lock_tables_timeout", "5s", - "--init_populate_metadata", "--track_schema_versions=true", // disabling online-ddl for reparent tests. This is done to reduce flakiness. // All the tests in this package reparent frequently between different tablets @@ -210,7 +209,6 @@ func StartNewVTTablet(t *testing.T, clusterInstance *cluster.LocalProcessCluster clusterInstance.TmpDirectory, []string{ "--lock_tables_timeout", "5s", - "--init_populate_metadata", "--track_schema_versions=true", "--queryserver_enable_online_ddl=false", }, diff --git a/go/test/endtoend/tabletmanager/replication_manager/tablet_test.go b/go/test/endtoend/tabletmanager/replication_manager/tablet_test.go index 40ac9b060dc..34f3b272e61 100644 --- a/go/test/endtoend/tabletmanager/replication_manager/tablet_test.go +++ b/go/test/endtoend/tabletmanager/replication_manager/tablet_test.go @@ -164,15 +164,27 @@ func waitForSourcePort(ctx context.Context, t *testing.T, tablet cluster.Vttable return fmt.Errorf("time out before source port became %v for %v", expectedPort, tablet.Alias) } +func getSidecarDbDDLQueryCount(tablet *cluster.VttabletProcess) (int64, error) { + vars := tablet.GetVars() + val, ok := vars["SidecarDbDDLQueryCount"] + if !ok { + return 0, fmt.Errorf("SidecarDbDDLQueryCount not found in debug/vars") + } + return int64(val.(float64)), nil +} func TestReplicationRepairAfterPrimaryTabletChange(t *testing.T) { ctx := context.Background() // Check that initially replication is setup correctly on the replica tablet err := waitForSourcePort(ctx, t, replicaTablet, int32(primaryTablet.MySQLPort)) require.NoError(t, err) + sidecarDDLCount, err := getSidecarDbDDLQueryCount(primaryTablet.VttabletProcess) + require.NoError(t, err) + // sidecar db should create all _vt tables when vttablet started + require.Greater(t, sidecarDDLCount, int64(0)) + // Stop the primary tablet stopTablet(t, primaryTablet) - // Change the MySQL port of the primary tablet newMysqlPort := clusterInstance.GetAndReservePort() primaryTablet.MySQLPort = newMysqlPort @@ -184,4 +196,9 @@ func TestReplicationRepairAfterPrimaryTabletChange(t *testing.T) { // Let replication manager repair replication err = waitForSourcePort(ctx, t, replicaTablet, int32(newMysqlPort)) require.NoError(t, err) + + sidecarDDLCount, err = getSidecarDbDDLQueryCount(primaryTablet.VttabletProcess) + require.NoError(t, err) + // sidecardb should find the desired _vt schema and not apply any new creates or upgrades when the tablet comes up again + require.Equal(t, sidecarDDLCount, int64(0)) } diff --git a/go/test/endtoend/tabletmanager/tablet_test.go b/go/test/endtoend/tabletmanager/tablet_test.go index 0296f397f5a..3c597e97981 100644 --- a/go/test/endtoend/tabletmanager/tablet_test.go +++ b/go/test/endtoend/tabletmanager/tablet_test.go @@ -52,67 +52,15 @@ func TestEnsureDB(t *testing.T) { status := tablet.VttabletProcess.GetStatusDetails() assert.Contains(t, status, "read-only") - // Switch to read-write and verify that that we go serving. - _ = clusterInstance.VtctlclientProcess.ExecuteCommand("SetReadWrite", tablet.Alias) + // Switch to read-write and verify that we go serving. + // Note: for TabletExternallyReparented, we expect SetReadWrite to be called by the user + err = clusterInstance.VtctlclientProcess.ExecuteCommand("SetReadWrite", tablet.Alias) + require.NoError(t, err) err = tablet.VttabletProcess.WaitForTabletStatus("SERVING") require.NoError(t, err) killTablets(t, tablet) } -// TestLocalMetadata tests the contents of local_metadata table after vttablet startup -func TestLocalMetadata(t *testing.T) { - defer cluster.PanicHandler(t) - // by default tablets are started with --restore_from_backup - // so metadata should exist - cluster.VerifyLocalMetadata(t, &replicaTablet, keyspaceName, shardName, cell) - - // Create new tablet - rTablet := clusterInstance.NewVttabletInstance("replica", 0, "") - - clusterInstance.VtTabletExtraArgs = []string{ - "--lock_tables_timeout", "5s", - "--init_populate_metadata", - } - rTablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(rTablet.TabletUID, rTablet.MySQLPort, clusterInstance.TmpDirectory) - err := rTablet.MysqlctlProcess.Start() - require.NoError(t, err) - - log.Info(fmt.Sprintf("Started vttablet %v", rTablet)) - // SupportsBackup=False prevents vttablet from trying to restore - // Start vttablet process - clusterInstance.VtGatePlannerVersion = 0 - err = clusterInstance.StartVttablet(rTablet, "SERVING", false, cell, keyspaceName, hostname, shardName) - require.NoError(t, err) - - cluster.VerifyLocalMetadata(t, rTablet, keyspaceName, shardName, cell) - - // Create another new tablet - rTablet2 := clusterInstance.NewVttabletInstance("replica", 0, "") - - // start with --init_populate_metadata false (default) - clusterInstance.VtTabletExtraArgs = []string{ - "--lock_tables_timeout", "5s", - } - rTablet2.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(rTablet2.TabletUID, rTablet2.MySQLPort, clusterInstance.TmpDirectory) - err = rTablet2.MysqlctlProcess.Start() - require.NoError(t, err) - - log.Info(fmt.Sprintf("Started vttablet %v", rTablet2)) - // SupportsBackup=False prevents vttablet from trying to restore - // Start vttablet process - err = clusterInstance.StartVttablet(rTablet2, "SERVING", false, cell, keyspaceName, hostname, shardName) - require.NoError(t, err) - - // check that tablet did _not_ get populated - qr, err := rTablet2.VttabletProcess.QueryTablet("select * from _vt.local_metadata", keyspaceName, false) - require.NoError(t, err) - require.Nil(t, qr.Rows) - - // Reset the VtTabletExtraArgs and kill tablets - clusterInstance.VtTabletExtraArgs = []string{} - killTablets(t, rTablet, rTablet2) -} - // TestResetReplicationParameters tests that the RPC ResetReplicationParameters works as intended. func TestResetReplicationParameters(t *testing.T) { defer cluster.PanicHandler(t) diff --git a/go/test/endtoend/vreplication/cluster_test.go b/go/test/endtoend/vreplication/cluster_test.go index b9995249086..1949205b8b1 100644 --- a/go/test/endtoend/vreplication/cluster_test.go +++ b/go/test/endtoend/vreplication/cluster_test.go @@ -84,6 +84,7 @@ type VitessCluster struct { Vtctld *cluster.VtctldProcess Vtctl *cluster.VtctlProcess VtctlClient *cluster.VtctlClientProcess + VtctldClient *cluster.VtctldClientProcess } // Cell represents a Vitess cell within the test cluster @@ -341,7 +342,8 @@ func NewVitessCluster(t *testing.T, name string, cellNames []string, clusterConf vc.VtctlClient = cluster.VtctlClientProcessInstance(vc.ClusterConfig.hostname, vc.Vtctld.GrpcPort, vc.ClusterConfig.tmpDir) require.NotNil(t, vc.VtctlClient) - + vc.VtctldClient = cluster.VtctldClientProcessInstance(vc.ClusterConfig.hostname, vc.Vtctld.GrpcPort, vc.ClusterConfig.tmpDir) + require.NotNil(t, vc.VtctldClient) return vc } diff --git a/go/test/endtoend/vreplication/sidecardb_test.go b/go/test/endtoend/vreplication/sidecardb_test.go new file mode 100644 index 00000000000..a40a80736af --- /dev/null +++ b/go/test/endtoend/vreplication/sidecardb_test.go @@ -0,0 +1,138 @@ +package vreplication + +import ( + "fmt" + "strconv" + "testing" + + "github.com/stretchr/testify/require" + "github.com/tidwall/gjson" +) + +const GetCurrentTablesQuery = "show tables from _vt" + +func getSidecarDBTables(t *testing.T, tabletID string) (numTablets int, tables []string) { + output, err := vc.VtctlClient.ExecuteCommandWithOutput("ExecuteFetchAsDba", "--", "--json", tabletID, GetCurrentTablesQuery) + require.NoError(t, err) + result := gjson.Get(output, "rows") + require.NotNil(t, result) + require.True(t, result.IsArray()) + rows := result.Array() + numTablets = len(rows) + for _, row := range rows { + require.True(t, row.IsArray()) + rows2 := row.Array() + require.NotNil(t, rows2) + require.Equal(t, len(rows2), 1) + table := rows2[0].String() + tables = append(tables, table) + } + return numTablets, tables +} + +var sidecarDBTables []string +var numSidecarDBTables int +var ddls1, ddls2 []string + +func init() { + sidecarDBTables = []string{"copy_state", "dt_participant", "dt_state", "heartbeat", "post_copy_action", "redo_state", + "redo_statement", "reparent_journal", "resharding_journal", "schema_migrations", "schema_version", "schemacopy", + "vdiff", "vdiff_log", "vdiff_table", "views", "vreplication", "vreplication_log"} + numSidecarDBTables = len(sidecarDBTables) + ddls1 = []string{ + "drop table _vt.vreplication_log", + "alter table _vt.vreplication drop column defer_secondary_keys", + } + ddls2 = []string{ + "alter table _vt.vreplication modify column defer_secondary_keys boolean default false", + } +} + +func prs(t *testing.T, keyspace, shard string) { + _, err := vc.VtctldClient.ExecuteCommandWithOutput("PlannedReparentShard", "--", fmt.Sprintf("%s/%s", keyspace, shard)) + require.NoError(t, err) +} + +// TestSidecarDB launches a Vitess cluster and ensures that the expected sidecar tables are created. We also drop/alter +// tables and ensure the next tablet init will recreate the sidecar database to the desired schema. +func TestSidecarDB(t *testing.T) { + cells := []string{"zone1"} + + vc = NewVitessCluster(t, "TestSidecarDB", cells, mainClusterConfig) + require.NotNil(t, vc) + allCellNames = "zone1" + defaultCellName := "zone1" + defaultCell = vc.Cells[defaultCellName] + + defer vc.TearDown(t) + + keyspace := "product" + shard := "0" + + cell1 := vc.Cells[defaultCellName] + tablet100 := fmt.Sprintf("%s-100", defaultCellName) + tablet101 := fmt.Sprintf("%s-101", defaultCellName) + vc.AddKeyspace(t, []*Cell{cell1}, keyspace, shard, initialProductVSchema, initialProductSchema, 1, 0, 100, sourceKsOpts) + shard0 := vc.Cells[defaultCellName].Keyspaces[keyspace].Shards[shard] + tablet100Port := shard0.Tablets[tablet100].Vttablet.Port + tablet101Port := shard0.Tablets[tablet101].Vttablet.Port + currentPrimary := tablet100 + + var expectedChanges100, expectedChanges101 int + + t.Run("validate sidecar on startup", func(t *testing.T) { + expectedChanges100 = len(sidecarDBTables) + expectedChanges101 = 0 + validateSidecarDBTables(t, tablet100, sidecarDBTables) + validateSidecarDBTables(t, tablet101, sidecarDBTables) + require.Equal(t, expectedChanges100, getNumExecutedDDLQueries(t, tablet100Port)) + require.Equal(t, expectedChanges101, getNumExecutedDDLQueries(t, tablet101Port)) + }) + + t.Run("modify schema, prs, and self heal on primary", func(t *testing.T) { + numChanges := modifySidecarDBSchema(t, vc, currentPrimary, ddls1) + validateSidecarDBTables(t, tablet100, sidecarDBTables[0:numSidecarDBTables-1]) + validateSidecarDBTables(t, tablet101, sidecarDBTables[0:numSidecarDBTables-1]) + + prs(t, keyspace, shard) + currentPrimary = tablet101 + expectedChanges100 += numChanges + validateSidecarDBTables(t, tablet100, sidecarDBTables) + validateSidecarDBTables(t, tablet101, sidecarDBTables) + require.Equal(t, expectedChanges100, getNumExecutedDDLQueries(t, tablet100Port)) + require.Equal(t, expectedChanges101, getNumExecutedDDLQueries(t, tablet101Port)) + }) + + t.Run("modify schema, prs, and self heal on new primary", func(t *testing.T) { + numChanges := modifySidecarDBSchema(t, vc, currentPrimary, ddls1) + expectedChanges101 += numChanges + prs(t, keyspace, shard) + // nolint + currentPrimary = tablet100 + + validateSidecarDBTables(t, tablet100, sidecarDBTables) + validateSidecarDBTables(t, tablet101, sidecarDBTables) + require.Equal(t, expectedChanges100, getNumExecutedDDLQueries(t, tablet100Port)) + require.Equal(t, expectedChanges101, getNumExecutedDDLQueries(t, tablet101Port)) + }) +} +func validateSidecarDBTables(t *testing.T, tabletID string, tables []string) { + _, tables2 := getSidecarDBTables(t, tabletID) + require.EqualValues(t, tables, tables2) +} + +func modifySidecarDBSchema(t *testing.T, vc *VitessCluster, tabletID string, ddls []string) (numChanges int) { + for _, ddl := range ddls { + output, err := vc.VtctlClient.ExecuteCommandWithOutput("ExecuteFetchAsDba", "--", tabletID, ddl) + require.NoErrorf(t, err, output) + } + return len(ddls) +} + +func getNumExecutedDDLQueries(t *testing.T, port int) int { + val, err := getDebugVar(t, port, []string{"SidecarDbDDLQueryCount"}) + require.NoError(t, err) + i, err := strconv.Atoi(val) + require.NoError(t, err) + return i +} diff --git a/go/test/endtoend/vtgate/schematracker/loadkeyspace/schema_load_keyspace_test.go b/go/test/endtoend/vtgate/schematracker/loadkeyspace/schema_load_keyspace_test.go index a603bc1c89b..8580240a942 100644 --- a/go/test/endtoend/vtgate/schematracker/loadkeyspace/schema_load_keyspace_test.go +++ b/go/test/endtoend/vtgate/schematracker/loadkeyspace/schema_load_keyspace_test.go @@ -20,7 +20,6 @@ import ( "os" "path" "testing" - "time" "github.com/stretchr/testify/require" @@ -53,44 +52,6 @@ var ( ` ) -func TestBlockedLoadKeyspace(t *testing.T) { - defer cluster.PanicHandler(t) - var err error - - clusterInstance = cluster.NewCluster(cell, hostname) - defer clusterInstance.Teardown() - - // Start topo server - err = clusterInstance.StartTopo() - require.NoError(t, err) - - // Start keyspace without the --queryserver-config-schema-change-signal flag - keyspace := &cluster.Keyspace{ - Name: keyspaceName, - SchemaSQL: sqlSchema, - } - clusterInstance.VtTabletExtraArgs = []string{"--queryserver-config-schema-change-signal=false"} - err = clusterInstance.StartUnshardedKeyspace(*keyspace, 0, false) - require.NoError(t, err) - - // Start vtgate with the schema_change_signal flag - clusterInstance.VtGateExtraArgs = []string{"--schema_change_signal"} - err = clusterInstance.StartVtgate() - require.NoError(t, err) - - // wait for addKeyspaceToTracker to timeout - time.Sleep(30 * time.Second) - - // check warning logs - logDir := clusterInstance.VtgateProcess.LogDir - all, err := os.ReadFile(path.Join(logDir, "vtgate-stderr.txt")) - require.NoError(t, err) - require.Contains(t, string(all), "Unable to get initial schema reload") - - // This error should not be logged as the initial load itself failed. - require.NotContains(t, string(all), "Unable to add keyspace to tracker") -} - func TestLoadKeyspaceWithNoTablet(t *testing.T) { defer cluster.PanicHandler(t) var err error diff --git a/go/vt/binlog/binlogplayer/binlog_player.go b/go/vt/binlog/binlogplayer/binlog_player.go index 1f37eeabdb4..1f75a396505 100644 --- a/go/vt/binlog/binlogplayer/binlog_player.go +++ b/go/vt/binlog/binlogplayer/binlog_player.go @@ -46,7 +46,6 @@ import ( topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/throttler" - "vitess.io/vitess/go/vt/withddl" ) var ( @@ -524,77 +523,6 @@ func (blp *BinlogPlayer) setVReplicationState(state, message string) error { return nil } -// CreateVReplicationTable returns the statements required to create -// the _vt.vreplication table. -// id: is an auto-increment column that identifies the stream. -// workflow: documents the creator/manager of the stream. Example: 'MoveTables'. -// source: contains a string proto representation of binlogpb.BinlogSource. -// pos: initially, a start position, and is updated to the current position by the binlog player. -// stop_pos: optional column that specifies the stop position. -// max_tps: max transactions per second. -// max_replication_lag: if replication lag exceeds this amount writing is throttled accordingly. -// cell: optional column that overrides the current cell to replicate from. -// tablet_types: optional column that overrides the tablet types to look to replicate from. -// time_update: last time an event was applied. -// transaction_timestamp: timestamp of the transaction (from the primary). -// state: Running, Error or Stopped. -// message: Reason for current state. -func CreateVReplicationTable() []string { - return []string{ - "CREATE DATABASE IF NOT EXISTS _vt", - "DROP TABLE IF EXISTS _vt.blp_checkpoint", - `CREATE TABLE IF NOT EXISTS _vt.vreplication ( - id INT AUTO_INCREMENT, - workflow VARBINARY(1000), - source VARBINARY(10000) NOT NULL, - pos VARBINARY(10000) NOT NULL, - stop_pos VARBINARY(10000) DEFAULT NULL, - max_tps BIGINT(20) NOT NULL, - max_replication_lag BIGINT(20) NOT NULL, - cell VARBINARY(1000) DEFAULT NULL, - tablet_types VARBINARY(100) DEFAULT NULL, - time_updated BIGINT(20) NOT NULL, - transaction_timestamp BIGINT(20) NOT NULL, - state VARBINARY(100) NOT NULL, - message VARBINARY(1000) DEFAULT NULL, - db_name VARBINARY(255) NOT NULL, - PRIMARY KEY (id) -) ENGINE=InnoDB`, - } -} - -// AlterVReplicationTable adds new columns to vreplication table -var AlterVReplicationTable = []string{ - "ALTER TABLE _vt.vreplication ADD COLUMN db_name VARBINARY(255) NOT NULL", - "ALTER TABLE _vt.vreplication MODIFY source MEDIUMBLOB NOT NULL", - "ALTER TABLE _vt.vreplication ADD KEY workflow_idx (workflow(64))", - "ALTER TABLE _vt.vreplication ADD COLUMN rows_copied BIGINT(20) NOT NULL DEFAULT 0", - "ALTER TABLE _vt.vreplication ADD COLUMN tags VARBINARY(1024) NOT NULL DEFAULT ''", - - // records the time of the last heartbeat. Heartbeats are only received if the source has no recent events - "ALTER TABLE _vt.vreplication ADD COLUMN time_heartbeat BIGINT(20) NOT NULL DEFAULT 0", - "ALTER TABLE _vt.vreplication ADD COLUMN workflow_type int NOT NULL DEFAULT 0", - "ALTER TABLE _vt.vreplication ADD COLUMN time_throttled BIGINT NOT NULL DEFAULT 0", - "ALTER TABLE _vt.vreplication ADD COLUMN component_throttled VARCHAR(255) NOT NULL DEFAULT ''", - "ALTER TABLE _vt.vreplication ADD COLUMN workflow_sub_type int NOT NULL DEFAULT 0", - "ALTER TABLE _vt.vreplication ADD COLUMN defer_secondary_keys bool NOT NULL DEFAULT false", -} - -// WithDDLInitialQueries contains the queries that: -// - are to be expected by the mock db client during tests, or -// - trigger some of the above _vt.vreplication schema changes to take effect -// when the binlogplayer starts up -// -// todo: cleanup here. QueryToTriggerWithDDL will be enough to ensure vreplication schema gets created/altered correctly. -// -// So do that explicitly and move queries required into the mock code. -var WithDDLInitialQueries = []string{ - "SELECT db_name FROM _vt.vreplication LIMIT 0", - "SELECT rows_copied FROM _vt.vreplication LIMIT 0", - "SELECT time_heartbeat FROM _vt.vreplication LIMIT 0", - withddl.QueryToTriggerWithDDL, -} - // VRSettings contains the settings of a vreplication table. type VRSettings struct { StartPos mysql.Position diff --git a/go/vt/binlog/binlogplayer/mock_dbclient.go b/go/vt/binlog/binlogplayer/mock_dbclient.go index d1b9836745e..50df683976d 100644 --- a/go/vt/binlog/binlogplayer/mock_dbclient.go +++ b/go/vt/binlog/binlogplayer/mock_dbclient.go @@ -22,8 +22,6 @@ import ( "testing" "time" - "vitess.io/vitess/go/vt/withddl" - "vitess.io/vitess/go/sqltypes" ) @@ -33,13 +31,12 @@ const mockClientUNameDba = "Dba" // MockDBClient mocks a DBClient. // It must be configured to expect requests in a specific order. type MockDBClient struct { - t *testing.T - UName string - expect []*mockExpect - currentResult int - done chan struct{} - queriesToIgnore []*mockExpect // these queries will return a standard nil result, you SHOULD NOT expect them in the tests - invariants map[string]*sqltypes.Result + t *testing.T + UName string + expect []*mockExpect + currentResult int + done chan struct{} + invariants map[string]*sqltypes.Result } type mockExpect struct { @@ -49,31 +46,12 @@ type mockExpect struct { err error } -func getQueriesToIgnore() []*mockExpect { - var queriesToIgnore []*mockExpect - var queries []string - queries = append(queries, WithDDLInitialQueries...) - queries = append(queries, withddl.QueryToTriggerWithDDL) - for _, query := range queries { - exp := &mockExpect{ - query: query, - re: nil, - result: &sqltypes.Result{}, - err: nil, - } - queriesToIgnore = append(queriesToIgnore, exp) - - } - return queriesToIgnore -} - // NewMockDBClient returns a new DBClientMock with the default "Filtered" UName. func NewMockDBClient(t *testing.T) *MockDBClient { return &MockDBClient{ - t: t, - UName: mockClientUNameFiltered, - done: make(chan struct{}), - queriesToIgnore: getQueriesToIgnore(), + t: t, + UName: mockClientUNameFiltered, + done: make(chan struct{}), invariants: map[string]*sqltypes.Result{ "CREATE TABLE IF NOT EXISTS _vt.vreplication_log": {}, "select id, type, state, message from _vt.vreplication_log": {}, @@ -85,10 +63,9 @@ func NewMockDBClient(t *testing.T) *MockDBClient { // NewMockDbaClient returns a new DBClientMock with the default "Dba" UName. func NewMockDbaClient(t *testing.T) *MockDBClient { return &MockDBClient{ - t: t, - UName: mockClientUNameDba, - done: make(chan struct{}), - queriesToIgnore: getQueriesToIgnore(), + t: t, + UName: mockClientUNameDba, + done: make(chan struct{}), } } @@ -174,11 +151,6 @@ func (dc *MockDBClient) ExecuteFetch(query string, maxrows int) (qr *sqltypes.Re dc.t.Helper() dc.t.Logf("DBClient query: %v", query) - for _, q := range dc.queriesToIgnore { - if strings.EqualFold(q.query, query) || strings.Contains(strings.ToLower(query), strings.ToLower(q.query)) { - return q.result, q.err - } - } for q, result := range dc.invariants { if strings.Contains(query, q) { return result, nil diff --git a/go/vt/dbconfigs/dbconfigs.go b/go/vt/dbconfigs/dbconfigs.go index 29553fc7a09..371892144d3 100644 --- a/go/vt/dbconfigs/dbconfigs.go +++ b/go/vt/dbconfigs/dbconfigs.go @@ -274,7 +274,7 @@ func (dbcfgs *DBConfigs) IsZero() bool { } // HasGlobalSettings returns true if DBConfigs contains values -// for gloabl configs. +// for global configs. func (dbcfgs *DBConfigs) HasGlobalSettings() bool { return dbcfgs.Host != "" || dbcfgs.Socket != "" } diff --git a/go/vt/mysqlctl/backup.go b/go/vt/mysqlctl/backup.go index 106188fd80b..4a7781c1be9 100644 --- a/go/vt/mysqlctl/backup.go +++ b/go/vt/mysqlctl/backup.go @@ -291,8 +291,6 @@ func Restore(ctx context.Context, params RestoreParams) (*BackupManifest, error) return nil, vterrors.Wrap(err, "ListBackups failed") } - metadataManager := &MetadataManager{} - if len(bhs) == 0 { // There are no backups (not even broken/incomplete ones). params.Logger.Errorf("no backup to restore on BackupStorage for directory %v. Starting up empty.", backupDir) @@ -306,10 +304,6 @@ func Restore(ctx context.Context, params RestoreParams) (*BackupManifest, error) params.Logger.Errorf("error resetting replication: %v. Continuing", err) } - if err := metadataManager.PopulateMetadataTables(params.Mysqld, params.LocalMetadata, params.DbName); err != nil { - params.Logger.Errorf("error populating metadata tables: %v. Continuing", err) - - } // Always return ErrNoBackup return nil, ErrNoBackup } @@ -370,18 +364,6 @@ func Restore(ctx context.Context, params RestoreParams) (*BackupManifest, error) return nil, vterrors.Wrap(err, "mysql_upgrade failed") } - // Add backupTime and restorePosition to LocalMetadata - params.LocalMetadata["RestoredBackupTime"] = manifest.BackupTime - params.LocalMetadata["RestorePosition"] = mysql.EncodePosition(manifest.Position) - - // Populate local_metadata before starting without --skip-networking, - // so it's there before we start announcing ourselves. - params.Logger.Infof("Restore: populating local_metadata") - err = metadataManager.PopulateMetadataTables(params.Mysqld, params.LocalMetadata, params.DbName) - if err != nil { - return nil, err - } - // The MySQL manual recommends restarting mysqld after running mysql_upgrade, // so that any changes made to system tables take effect. params.Logger.Infof("Restore: restarting mysqld after mysql_upgrade") diff --git a/go/vt/mysqlctl/backupengine.go b/go/vt/mysqlctl/backupengine.go index e25b9bd9e00..7473edd5dba 100644 --- a/go/vt/mysqlctl/backupengine.go +++ b/go/vt/mysqlctl/backupengine.go @@ -82,8 +82,6 @@ type RestoreParams struct { Concurrency int // Extra env variables for pre-restore and post-restore transform hooks HookExtraEnv map[string]string - // Metadata to write into database after restore. See PopulateMetadataTables - LocalMetadata map[string]string // DeleteBeforeRestore tells us whether existing data should be deleted before // restoring. This is always set to false when starting a tablet with -restore_from_backup, // but is set to true when executing a RestoreFromBackup command on an already running vttablet @@ -417,7 +415,7 @@ func FindBackupToRestore(ctx context.Context, params RestoreParams, bhs []backup } // restore to a position (using incremental backups): // we calculate a possible restore path based on the manifests. The resulting manifests are - // a sorted subsequence, with the full backup first, and zero or more inremental backups to follow. + // a sorted subsequence, with the full backup first, and zero or more incremental backups to follow. manifests, err := FindPITRPath(params.RestoreToPos.GTIDSet, manifests) if err != nil { return nil, err diff --git a/go/vt/mysqlctl/metadata_tables.go b/go/vt/mysqlctl/metadata_tables.go deleted file mode 100644 index 5ed7bb9cfe1..00000000000 --- a/go/vt/mysqlctl/metadata_tables.go +++ /dev/null @@ -1,234 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package mysqlctl - -import ( - "bytes" - "fmt" - - "context" - - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/dbconnpool" - "vitess.io/vitess/go/vt/log" -) - -// Note that definitions of local_metadata and shard_metadata should be the same -// as in testing which is defined in config/init_db.sql. -const ( - sqlCreateLocalMetadataTable = `CREATE TABLE IF NOT EXISTS _vt.local_metadata ( - name VARCHAR(255) NOT NULL, - value MEDIUMBLOB NOT NULL, - PRIMARY KEY (name) - ) ENGINE=InnoDB` - sqlCreateShardMetadataTable = `CREATE TABLE IF NOT EXISTS _vt.shard_metadata ( - name VARCHAR(255) NOT NULL, - value MEDIUMBLOB NOT NULL, - PRIMARY KEY (name) - ) ENGINE=InnoDB` - sqlUpdateLocalMetadataTable = "UPDATE _vt.local_metadata SET db_name='%s' WHERE db_name=''" - sqlUpdateShardMetadataTable = "UPDATE _vt.shard_metadata SET db_name='%s' WHERE db_name=''" -) - -var ( - sqlAlterLocalMetadataTable = []string{ - `ALTER TABLE _vt.local_metadata ADD COLUMN db_name VARBINARY(255) NOT NULL DEFAULT ''`, - `ALTER TABLE _vt.local_metadata DROP PRIMARY KEY, ADD PRIMARY KEY(name, db_name)`, - // VARCHAR(255) is not long enough to hold replication positions, hence changing to - // MEDIUMBLOB. - `ALTER TABLE _vt.local_metadata CHANGE value value MEDIUMBLOB NOT NULL`, - } - sqlAlterShardMetadataTable = []string{ - `ALTER TABLE _vt.shard_metadata ADD COLUMN db_name VARBINARY(255) NOT NULL DEFAULT ''`, - `ALTER TABLE _vt.shard_metadata DROP PRIMARY KEY, ADD PRIMARY KEY(name, db_name)`, - } -) - -// MetadataManager manages the creation and filling of the _vt.local_metadata -// and _vt.shard_metadata tables. -type MetadataManager struct{} - -// PopulateMetadataTables creates and fills the _vt.local_metadata table and -// creates the _vt.shard_metadata table. -// -// _vt.local_metadata table is a per-tablet table that is never replicated. -// This allows queries against local_metadata to return different values on -// different tablets, which is used for communicating between Vitess and -// MySQL-level tools like Orchestrator (https://github.com/openark/orchestrator). -// -// _vt.shard_metadata is a replicated table with per-shard information, but it's -// created here to make it easier to create it on databases that were running -// old version of Vitess, or databases that are getting converted to run under -// Vitess. -// -// This function is semantically equivalent to calling createMetadataTables -// followed immediately by upsertLocalMetadata. -func (m *MetadataManager) PopulateMetadataTables(mysqld MysqlDaemon, localMetadata map[string]string, dbName string) error { - log.Infof("Populating _vt.local_metadata table...") - - // Get a non-pooled DBA connection. - conn, err := mysqld.GetDbaConnection(context.TODO()) - if err != nil { - return err - } - defer conn.Close() - - // Disable replication on this session. We close the connection after using - // it, so there's no need to re-enable replication when we're done. - if _, err := conn.ExecuteFetch("SET @@session.sql_log_bin = 0", 0, false); err != nil { - return err - } - - // Create the database and table if necessary. - if err := createMetadataTables(conn, dbName); err != nil { - return err - } - - // Populate local_metadata from the passed list of values. - return upsertLocalMetadata(conn, localMetadata, dbName) -} - -// UpsertLocalMetadata adds the given metadata map to the _vt.local_metadata -// table, updating any rows that exist for a given `_vt.local_metadata.name` -// with the map value. The session that performs these upserts sets -// sql_log_bin=0, as the _vt.local_metadata table is meant to never be -// replicated. -// -// Callers are responsible for ensuring the _vt.local_metadata table exists -// before calling this function, usually by calling CreateMetadataTables at -// least once prior. -func (m *MetadataManager) UpsertLocalMetadata(mysqld MysqlDaemon, localMetadata map[string]string, dbName string) error { - log.Infof("Upserting _vt.local_metadata ...") - - conn, err := mysqld.GetDbaConnection(context.TODO()) - if err != nil { - return err - } - defer conn.Close() - - // Disable replication on this session. We close the connection after using - // it, so there's no need to re-enable replication when we're done. - if _, err := conn.ExecuteFetch("SET @@session.sql_log_bin = 0", 0, false); err != nil { - return err - } - - return upsertLocalMetadata(conn, localMetadata, dbName) -} - -func createMetadataTables(conn *dbconnpool.DBConnection, dbName string) error { - if _, err := conn.ExecuteFetch("CREATE DATABASE IF NOT EXISTS _vt", 0, false); err != nil { - return err - } - - if err := createLocalMetadataTable(conn, dbName); err != nil { - return err - } - - if err := createShardMetadataTable(conn, dbName); err != nil { - return err - } - - return nil -} - -func createLocalMetadataTable(conn *dbconnpool.DBConnection, dbName string) error { - if _, err := conn.ExecuteFetch(sqlCreateLocalMetadataTable, 0, false); err != nil { - return err - } - - for _, sql := range sqlAlterLocalMetadataTable { - if _, err := conn.ExecuteFetch(sql, 0, false); err != nil { - // Ignore "Duplicate column name 'db_name'" errors which can happen on every restart. - if merr, ok := err.(*mysql.SQLError); !ok || merr.Num != mysql.ERDupFieldName { - log.Errorf("Error executing %v: %v", sql, err) - return err - } - } - } - - sql := fmt.Sprintf(sqlUpdateLocalMetadataTable, dbName) - if _, err := conn.ExecuteFetch(sql, 0, false); err != nil { - log.Errorf("Error executing %v: %v, continuing. Please check the data in _vt.local_metadata and take corrective action.", sql, err) - } - - return nil -} - -func createShardMetadataTable(conn *dbconnpool.DBConnection, dbName string) error { - if _, err := conn.ExecuteFetch(sqlCreateShardMetadataTable, 0, false); err != nil { - return err - } - - for _, sql := range sqlAlterShardMetadataTable { - if _, err := conn.ExecuteFetch(sql, 0, false); err != nil { - // Ignore "Duplicate column name 'db_name'" errors which can happen on every restart. - if merr, ok := err.(*mysql.SQLError); !ok || merr.Num != mysql.ERDupFieldName { - log.Errorf("Error executing %v: %v", sql, err) - return err - } - } - } - - sql := fmt.Sprintf(sqlUpdateShardMetadataTable, dbName) - if _, err := conn.ExecuteFetch(sql, 0, false); err != nil { - log.Errorf("Error executing %v: %v, continuing. Please check the data in _vt.shard_metadata and take corrective action.", sql, err) - } - - return nil -} - -// upsertLocalMetadata adds the given metadata map to the _vt.local_metadata -// table, updating any rows that exist for a given `_vt.local_metadata.name` -// with the map value. The session that performs these upserts sets -// sql_log_bin=0, as the _vt.local_metadata table is meant to never be -// replicated. -// -// Callers are responsible for ensuring the _vt.local_metadata table exists -// before calling this function, usually by calling CreateMetadataTables at -// least once prior. -func upsertLocalMetadata(conn *dbconnpool.DBConnection, localMetadata map[string]string, dbName string) error { - // Populate local_metadata from the passed list of values. - if _, err := conn.ExecuteFetch("BEGIN", 0, false); err != nil { - return err - } - for name, val := range localMetadata { - nameValue := sqltypes.NewVarChar(name) - valValue := sqltypes.NewVarChar(val) - dbNameValue := sqltypes.NewVarBinary(dbName) - - queryBuf := bytes.Buffer{} - queryBuf.WriteString("INSERT INTO _vt.local_metadata (name,value, db_name) VALUES (") - nameValue.EncodeSQL(&queryBuf) - queryBuf.WriteByte(',') - valValue.EncodeSQL(&queryBuf) - queryBuf.WriteByte(',') - dbNameValue.EncodeSQL(&queryBuf) - queryBuf.WriteString(") ON DUPLICATE KEY UPDATE value = ") - valValue.EncodeSQL(&queryBuf) - - if _, err := conn.ExecuteFetch(queryBuf.String(), 0, false); err != nil { - return err - } - } - - if _, err := conn.ExecuteFetch("COMMIT", 0, false); err != nil { - return err - } - - return nil -} diff --git a/go/vt/mysqlctl/reparent.go b/go/vt/mysqlctl/reparent.go index d8a4640bc7a..5a25cf8d7e3 100644 --- a/go/vt/mysqlctl/reparent.go +++ b/go/vt/mysqlctl/reparent.go @@ -24,37 +24,18 @@ import ( "fmt" "time" + "vitess.io/vitess/go/vt/sidecardb" + "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/log" "context" ) -// CreateReparentJournal returns the commands to execute to create -// the _vt.reparent_journal table. It is safe to run these commands -// even if the table already exists. -func CreateReparentJournal() []string { - return []string{ - "CREATE DATABASE IF NOT EXISTS _vt", - fmt.Sprintf(`CREATE TABLE IF NOT EXISTS _vt.reparent_journal ( - time_created_ns BIGINT UNSIGNED NOT NULL, - action_name VARBINARY(250) NOT NULL, - primary_alias VARBINARY(32) NOT NULL, - replication_position VARBINARY(%v) DEFAULT NULL, - PRIMARY KEY (time_created_ns)) -ENGINE=InnoDB`, mysql.MaximumPositionSize)} -} - -// AlterReparentJournal returns the commands to execute to change -// column master_alias -> primary_alias or the other way -// In 13.0.0 we introduced renaming of primary_alias -> master_alias. -// This was to support in-place downgrade from a later version. -// In 14.0.0 we replace that with renaming of master_alias -> primary_alias. -// This is to support in-place upgrades from 13.0.x to 14.0.x -func AlterReparentJournal() []string { - return []string{ - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", - } +// GenerateInitialBinlogEntry is used to create a binlog entry when a primary comes up and we need to get a +// MySQL position so that we can set it as the starting position for replicas to do MySQL Replication from. +func GenerateInitialBinlogEntry() string { + return sidecardb.CreateSidecarDatabaseQuery } // PopulateReparentJournal returns the SQL command to use to populate @@ -82,6 +63,9 @@ func queryReparentJournal(timeCreatedNS int64) string { func (mysqld *Mysqld) WaitForReparentJournal(ctx context.Context, timeCreatedNS int64) error { for { qr, err := mysqld.FetchSuperQuery(ctx, queryReparentJournal(timeCreatedNS)) + if err != nil { + log.Infof("Error querying reparent journal: %v", err) + } if err == nil && len(qr.Rows) == 1 { // we have the row, we're done return nil diff --git a/go/vt/mysqlctl/replication.go b/go/vt/mysqlctl/replication.go index e1dfee7b141..1f9ca28af7c 100644 --- a/go/vt/mysqlctl/replication.go +++ b/go/vt/mysqlctl/replication.go @@ -233,6 +233,17 @@ func (mysqld *Mysqld) IsReadOnly() (bool, error) { // SetReadOnly set/unset the read_only flag func (mysqld *Mysqld) SetReadOnly(on bool) error { + // temp logging, to be removed in v17 + var newState string + switch on { + case false: + newState = "ReadWrite" + case true: + newState = "ReadOnly" + } + log.Infof("SetReadOnly setting connection setting of %s:%d to : %s", + mysqld.dbcfgs.Host, mysqld.dbcfgs.Port, newState) + query := "SET GLOBAL read_only = " if on { query += "ON" diff --git a/go/vt/servenv/exporter.go b/go/vt/servenv/exporter.go index 397be415581..d8eb4ef428d 100644 --- a/go/vt/servenv/exporter.go +++ b/go/vt/servenv/exporter.go @@ -102,6 +102,7 @@ type Exporter struct { name, label string handleFuncs map[string]*handleFunc sp *statusPage + mu sync.Mutex } // NewExporter creates a new Exporter with name as namespace. @@ -154,6 +155,8 @@ func (e *Exporter) URLPrefix() string { // url remapped from /path to /name/path. If name is empty, the request // is passed through to http.HandleFunc. func (e *Exporter) HandleFunc(url string, f func(w http.ResponseWriter, r *http.Request)) { + e.mu.Lock() + defer e.mu.Unlock() if e.name == "" { http.HandleFunc(url, f) return diff --git a/go/vt/servenv/servenv.go b/go/vt/servenv/servenv.go index 1a2b7b1a81d..7a22c8b4f44 100644 --- a/go/vt/servenv/servenv.go +++ b/go/vt/servenv/servenv.go @@ -83,6 +83,7 @@ var ( catchSigpipe bool maxStackSize = 64 * 1024 * 1024 usePSLogger bool + initStartTime time.Time // time when tablet init started: for debug purposes to time how long a tablet init takes ) // RegisterFlags installs the flags used by Init, Run, and RunDefault. @@ -104,10 +105,17 @@ func RegisterFlags() { }) } +func GetInitStartTime() time.Time { + mu.Lock() + defer mu.Unlock() + return initStartTime +} + // Init is the first phase of the server startup. func Init() { mu.Lock() defer mu.Unlock() + initStartTime = time.Now() // Ignore SIGPIPE if specified // The Go runtime catches SIGPIPE for us on all fds except stdout/stderr diff --git a/go/vt/sidecardb/doc.go b/go/vt/sidecardb/doc.go new file mode 100644 index 00000000000..72a10244b04 --- /dev/null +++ b/go/vt/sidecardb/doc.go @@ -0,0 +1,40 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sidecardb + +/* + +The sidecardb module is used to create and upgrade the sidecar database schema on tablet init. The sidecar database +is named `_vt`. + +The schema subdirectory has subdirectories, categorized by module, with one file per table in _vt. Each has the latest +schema for each table in _vt (in the form of a create table statement). + +sidecardb uses the schemadiff module in Vitess to reach the desired schema for each table. + +Note: + +The `if not exists` in the schema files should not be needed since we only create tables in the sidecar database if they don't exist. +However, during development, we came across some Vitess flows like backup restore on replicas where the database +already had the tables but mysql replication also found these `create`s +in the primary's binlog causing the replica tablet to halt since it could not execute the duplicate create. + +We did fix these flows and hence ideally this **should never happen**, but as an abundance of caution +I have left it in now for operational reasons, so that we paper over any bugs for now. +We can remove it in v17 or v18 once the schema init is stable and we have done more testing. + +*/ diff --git a/go/vt/sidecardb/schema/misc/heartbeat.sql b/go/vt/sidecardb/schema/misc/heartbeat.sql new file mode 100644 index 00000000000..cacd80529b5 --- /dev/null +++ b/go/vt/sidecardb/schema/misc/heartbeat.sql @@ -0,0 +1,23 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.heartbeat +( + keyspaceShard VARBINARY(256) NOT NULL, + tabletUid INT UNSIGNED NOT NULL, + ts BIGINT UNSIGNED NOT NULL, + PRIMARY KEY (`keyspaceShard`) +) engine = InnoDB diff --git a/go/vt/sidecardb/schema/misc/reparent_journal.sql b/go/vt/sidecardb/schema/misc/reparent_journal.sql new file mode 100644 index 00000000000..74534f57098 --- /dev/null +++ b/go/vt/sidecardb/schema/misc/reparent_journal.sql @@ -0,0 +1,25 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.reparent_journal +( + `time_created_ns` bigint(20) unsigned NOT NULL, + `action_name` varbinary(250) NOT NULL, + `primary_alias` varbinary(32) NOT NULL, + `replication_position` varbinary(64000) DEFAULT NULL, + + PRIMARY KEY (`time_created_ns`) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/misc/views.sql b/go/vt/sidecardb/schema/misc/views.sql new file mode 100644 index 00000000000..1d31a648a1b --- /dev/null +++ b/go/vt/sidecardb/schema/misc/views.sql @@ -0,0 +1,24 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.views +( + TABLE_NAME varchar(64) NOT NULL, + VIEW_DEFINITION longtext NOT NULL, + CREATE_STATEMENT longtext NOT NULL, + UPDATED_AT TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (TABLE_NAME) +) engine = InnoDB diff --git a/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql b/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql new file mode 100644 index 00000000000..54aa1f9cbb6 --- /dev/null +++ b/go/vt/sidecardb/schema/onlineddl/schema_migrations.sql @@ -0,0 +1,82 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.schema_migrations +( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `migration_uuid` varchar(64) NOT NULL, + `keyspace` varchar(256) NOT NULL, + `shard` varchar(255) NOT NULL, + `mysql_schema` varchar(128) NOT NULL, + `mysql_table` varchar(128) NOT NULL, + `migration_statement` text NOT NULL, + `strategy` varchar(128) NOT NULL, + `options` varchar(8192) NOT NULL, + `added_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `requested_timestamp` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `ready_timestamp` timestamp NULL DEFAULT NULL, + `started_timestamp` timestamp NULL DEFAULT NULL, + `liveness_timestamp` timestamp NULL DEFAULT NULL, + `completed_timestamp` timestamp(6) NULL DEFAULT NULL, + `cleanup_timestamp` timestamp NULL DEFAULT NULL, + `migration_status` varchar(128) NOT NULL, + `log_path` varchar(1024) NOT NULL, + `artifacts` text NOT NULL, + `retries` int unsigned NOT NULL DEFAULT '0', + `tablet` varchar(128) NOT NULL DEFAULT '', + `tablet_failure` tinyint unsigned NOT NULL DEFAULT '0', + `progress` float NOT NULL DEFAULT '0', + `migration_context` varchar(1024) NOT NULL DEFAULT '', + `ddl_action` varchar(16) NOT NULL DEFAULT '', + `message` text NOT NULL, + `eta_seconds` bigint NOT NULL DEFAULT '-1', + `rows_copied` bigint unsigned NOT NULL DEFAULT '0', + `table_rows` bigint NOT NULL DEFAULT '0', + `added_unique_keys` int unsigned NOT NULL DEFAULT '0', + `removed_unique_keys` int unsigned NOT NULL DEFAULT '0', + `log_file` varchar(1024) NOT NULL DEFAULT '', + `retain_artifacts_seconds` bigint NOT NULL DEFAULT '0', + `postpone_completion` tinyint unsigned NOT NULL DEFAULT '0', + `removed_unique_key_names` text NOT NULL, + `dropped_no_default_column_names` text NOT NULL, + `expanded_column_names` text NOT NULL, + `revertible_notes` text NOT NULL, + `allow_concurrent` tinyint unsigned NOT NULL DEFAULT '0', + `reverted_uuid` varchar(64) NOT NULL DEFAULT '', + `is_view` tinyint unsigned NOT NULL DEFAULT '0', + `ready_to_complete` tinyint unsigned NOT NULL DEFAULT '0', + `stowaway_table` tinytext NOT NULL, + `vitess_liveness_indicator` bigint NOT NULL DEFAULT '0', + `user_throttle_ratio` float NOT NULL DEFAULT '0', + `special_plan` text NOT NULL, + `last_throttled_timestamp` timestamp NULL DEFAULT NULL, + `component_throttled` tinytext NOT NULL, + `cancelled_timestamp` timestamp NULL DEFAULT NULL, + `postpone_launch` tinyint unsigned NOT NULL DEFAULT '0', + `stage` text NOT NULL, + `cutover_attempts` int unsigned NOT NULL DEFAULT '0', + `is_immediate_operation` tinyint unsigned NOT NULL DEFAULT '0', + `reviewed_timestamp` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uuid_idx` (`migration_uuid`), + KEY `keyspace_shard_idx` (`keyspace`(64), `shard`(64)), + KEY `status_idx` (`migration_status`, `liveness_timestamp`), + KEY `cleanup_status_idx` (`cleanup_timestamp`, `migration_status`), + KEY `tablet_failure_idx` (`tablet_failure`, `migration_status`, `retries`), + KEY `table_complete_idx` (`migration_status`, `keyspace`(64), `mysql_table`(64), `completed_timestamp`), + KEY `migration_context_idx` (`migration_context`(64)), + KEY `reverted_uuid_idx` (`reverted_uuid`) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/schematracker/schemacopy.sql b/go/vt/sidecardb/schema/schematracker/schemacopy.sql new file mode 100644 index 00000000000..95cd7c34f3f --- /dev/null +++ b/go/vt/sidecardb/schema/schematracker/schemacopy.sql @@ -0,0 +1,28 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.schemacopy +( + `table_schema` varchar(64) NOT NULL, + `table_name` varchar(64) NOT NULL, + `column_name` varchar(64) NOT NULL, + `ordinal_position` bigint unsigned NOT NULL, + `character_set_name` varchar(32) DEFAULT NULL, + `collation_name` varchar(32) DEFAULT NULL, + `data_type` varchar(64) NOT NULL, + `column_key` varchar(3) NOT NULL, + PRIMARY KEY (`table_schema`, `table_name`, `ordinal_position`) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/twopc/dt_participant.sql b/go/vt/sidecardb/schema/twopc/dt_participant.sql new file mode 100644 index 00000000000..66ff4bda987 --- /dev/null +++ b/go/vt/sidecardb/schema/twopc/dt_participant.sql @@ -0,0 +1,24 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.dt_participant +( + dtid varbinary(512) NOT NULL, + id bigint NOT NULL, + keyspace varchar(256) NOT NULL, + shard varchar(256) NOT NULL, + primary key(dtid, id) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/twopc/dt_state.sql b/go/vt/sidecardb/schema/twopc/dt_state.sql new file mode 100644 index 00000000000..e877a31a75f --- /dev/null +++ b/go/vt/sidecardb/schema/twopc/dt_state.sql @@ -0,0 +1,23 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.dt_state +( + dtid varbinary(512) NOT NULL, + state bigint NOT NULL, + time_created bigint NOT NULL, + primary key(dtid) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/twopc/redo_state.sql b/go/vt/sidecardb/schema/twopc/redo_state.sql new file mode 100644 index 00000000000..a1122b0ac8f --- /dev/null +++ b/go/vt/sidecardb/schema/twopc/redo_state.sql @@ -0,0 +1,22 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.redo_state( + dtid varbinary(512) NOT NULL, + state bigint NOT NULL, + time_created bigint NOT NULL, + primary key(dtid) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/twopc/redo_statement.sql b/go/vt/sidecardb/schema/twopc/redo_statement.sql new file mode 100644 index 00000000000..148cc0bb3c0 --- /dev/null +++ b/go/vt/sidecardb/schema/twopc/redo_statement.sql @@ -0,0 +1,22 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.redo_statement( + dtid varbinary(512) NOT NULL, + id bigint NOT NULL, + statement mediumblob NOT NULL, + primary key(dtid, id) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/vdiff/vdiff.sql b/go/vt/sidecardb/schema/vdiff/vdiff.sql new file mode 100644 index 00000000000..24f5cf6e7ab --- /dev/null +++ b/go/vt/sidecardb/schema/vdiff/vdiff.sql @@ -0,0 +1,36 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.vdiff +( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `vdiff_uuid` varchar(64) NOT NULL, + `workflow` varbinary(1024) DEFAULT NULL, + `keyspace` varbinary(256) DEFAULT NULL, + `shard` varchar(255) NOT NULL, + `db_name` varbinary(1024) DEFAULT NULL, + `state` varbinary(64) DEFAULT NULL, + `options` json DEFAULT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `started_at` timestamp NULL DEFAULT NULL, + `liveness_timestamp` timestamp NULL DEFAULT NULL, + `completed_at` timestamp NULL DEFAULT NULL, + `last_error` varbinary(512) DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `uuid_idx` (`vdiff_uuid`), + KEY `state` (`state`), + KEY `ks_wf_idx` (`keyspace`(64), `workflow`(64)) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/vdiff/vdiff_log.sql b/go/vt/sidecardb/schema/vdiff/vdiff_log.sql new file mode 100644 index 00000000000..2935baf9b24 --- /dev/null +++ b/go/vt/sidecardb/schema/vdiff/vdiff_log.sql @@ -0,0 +1,24 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.vdiff_log +( + `id` int(11) NOT NULL AUTO_INCREMENT, + `vdiff_id` int(11) NOT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `message` text NOT NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/vdiff/vdiff_table.sql b/go/vt/sidecardb/schema/vdiff/vdiff_table.sql new file mode 100644 index 00000000000..81f0ba17599 --- /dev/null +++ b/go/vt/sidecardb/schema/vdiff/vdiff_table.sql @@ -0,0 +1,30 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.vdiff_table +( + `vdiff_id` varchar(64) NOT NULL, + `table_name` varbinary(128) NOT NULL, + `state` varbinary(64) DEFAULT NULL, + `lastpk` varbinary(2000) DEFAULT NULL, + `table_rows` bigint(20) NOT NULL DEFAULT '0', + `rows_compared` bigint(20) NOT NULL DEFAULT '0', + `mismatch` tinyint(1) NOT NULL DEFAULT '0', + `report` json DEFAULT NULL, + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`vdiff_id`, `table_name`) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/vreplication/copy_state.sql b/go/vt/sidecardb/schema/vreplication/copy_state.sql new file mode 100644 index 00000000000..f7005135aba --- /dev/null +++ b/go/vt/sidecardb/schema/vreplication/copy_state.sql @@ -0,0 +1,25 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.copy_state +( + `id` bigint unsigned NOT NULL AUTO_INCREMENT, + `vrepl_id` int NOT NULL, + `table_name` varbinary(128) NOT NULL, + `lastpk` varbinary(2000) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `vrepl_id` (`vrepl_id`,`table_name`) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/vreplication/post_copy_action.sql b/go/vt/sidecardb/schema/vreplication/post_copy_action.sql new file mode 100644 index 00000000000..8ca979fc15d --- /dev/null +++ b/go/vt/sidecardb/schema/vreplication/post_copy_action.sql @@ -0,0 +1,24 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.post_copy_action( + id BIGINT NOT NULL auto_increment, + vrepl_id INT NOT NULL, + table_name VARBINARY(128) NOT NULL, + action JSON NOT NULL, + UNIQUE KEY (vrepl_id, table_name), + PRIMARY KEY(id) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/vreplication/resharding_journal.sql b/go/vt/sidecardb/schema/vreplication/resharding_journal.sql new file mode 100644 index 00000000000..b5b960c92aa --- /dev/null +++ b/go/vt/sidecardb/schema/vreplication/resharding_journal.sql @@ -0,0 +1,23 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.resharding_journal +( + `id` bigint NOT NULL, + `db_name` varbinary(255) DEFAULT NULL, + `val` blob, + PRIMARY KEY (`id`) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/vreplication/schema_version.sql b/go/vt/sidecardb/schema/vreplication/schema_version.sql new file mode 100644 index 00000000000..86f782ddac1 --- /dev/null +++ b/go/vt/sidecardb/schema/vreplication/schema_version.sql @@ -0,0 +1,25 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.schema_version +( + id INT NOT NULL AUTO_INCREMENT, + pos VARBINARY(10000) NOT NULL, + time_updated BIGINT(20) NOT NULL, + ddl BLOB DEFAULT NULL, + schemax LONGBLOB NOT NULL, + PRIMARY KEY (id) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/vreplication/vreplication.sql b/go/vt/sidecardb/schema/vreplication/vreplication.sql new file mode 100644 index 00000000000..3b30d1250c9 --- /dev/null +++ b/go/vt/sidecardb/schema/vreplication/vreplication.sql @@ -0,0 +1,43 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.vreplication +( + `id` int NOT NULL AUTO_INCREMENT, + `workflow` varbinary(1000) DEFAULT NULL, + `source` mediumblob NOT NULL, + `pos` varbinary(10000) NOT NULL, + `stop_pos` varbinary(10000) DEFAULT NULL, + `max_tps` bigint NOT NULL, + `max_replication_lag` bigint NOT NULL, + `cell` varbinary(1000) DEFAULT NULL, + `tablet_types` varbinary(100) DEFAULT NULL, + `time_updated` bigint NOT NULL, + `transaction_timestamp` bigint NOT NULL, + `state` varbinary(100) NOT NULL, + `message` varbinary(1000) DEFAULT NULL, + `db_name` varbinary(255) NOT NULL, + `rows_copied` bigint NOT NULL DEFAULT '0', + `tags` varbinary(1024) NOT NULL DEFAULT '', + `time_heartbeat` bigint NOT NULL DEFAULT '0', + `workflow_type` int NOT NULL DEFAULT '0', + `time_throttled` bigint NOT NULL DEFAULT '0', + `component_throttled` varchar(255) NOT NULL DEFAULT '', + `workflow_sub_type` int NOT NULL DEFAULT '0', + `defer_secondary_keys` tinyint(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `workflow_idx` (`workflow`(64)) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/schema/vreplication/vreplication_log.sql b/go/vt/sidecardb/schema/vreplication/vreplication_log.sql new file mode 100644 index 00000000000..6700ede3c47 --- /dev/null +++ b/go/vt/sidecardb/schema/vreplication/vreplication_log.sql @@ -0,0 +1,28 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +CREATE TABLE IF NOT EXISTS _vt.vreplication_log +( + `id` bigint NOT NULL AUTO_INCREMENT, + `vrepl_id` int NOT NULL, + `type` varbinary(256) NOT NULL, + `state` varbinary(100) NOT NULL, + `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + `message` text NOT NULL, + `count` bigint NOT NULL DEFAULT '1', + PRIMARY KEY (`id`) +) ENGINE = InnoDB diff --git a/go/vt/sidecardb/sidecardb.go b/go/vt/sidecardb/sidecardb.go new file mode 100644 index 00000000000..11f7a402592 --- /dev/null +++ b/go/vt/sidecardb/sidecardb.go @@ -0,0 +1,399 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sidecardb + +import ( + "context" + "embed" + "fmt" + "io/fs" + "path/filepath" + "regexp" + "runtime" + "strings" + + "vitess.io/vitess/go/mysql" + + "vitess.io/vitess/go/mysql/fakesqldb" + + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" + + "vitess.io/vitess/go/stats" + + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/vt/schemadiff" +) + +const ( + SidecarDBName = "_vt" + CreateSidecarDatabaseQuery = "create database if not exists _vt" + UseSidecarDatabaseQuery = "use _vt" + ShowSidecarDatabasesQuery = "SHOW DATABASES LIKE '\\_vt'" + SelectCurrentDatabaseQuery = "select database()" + ShowCreateTableQuery = "show create table _vt.%s" + + CreateTableRegexp = "CREATE TABLE .* `\\_vt`\\..*" + AlterTableRegexp = "ALTER TABLE `\\_vt`\\..*" +) + +// All tables needed in the sidecar database have their schema in the schema subdirectory. +// +//go:embed schema/* +var schemaLocation embed.FS + +type sidecarTable struct { + module string // which module uses this table + path string // path of the schema relative to this module + name string // table name + schema string // create table dml +} + +func (t *sidecarTable) String() string { + return fmt.Sprintf("%s.%s (%s)", SidecarDBName, t.name, t.module) +} + +var sidecarTables []*sidecarTable +var ddlCount *stats.Counter + +func init() { + initSchemaFiles() + ddlCount = stats.NewCounter("SidecarDbDDLQueryCount", "Number of create/upgrade queries executed") +} + +func validateSchemaDefinition(name, schema string) (string, error) { + stmt, err := sqlparser.ParseStrictDDL(schema) + + if err != nil { + return "", err + } + createTable, ok := stmt.(*sqlparser.CreateTable) + if !ok { + return "", vterrors.Errorf(vtrpcpb.Code_INTERNAL, "expected CREATE TABLE. Got %v", sqlparser.CanonicalString(stmt)) + } + tableName := createTable.Table.Name.String() + qualifier := createTable.Table.Qualifier.String() + if qualifier != SidecarDBName { + return "", fmt.Errorf("database qualifier specified for the %s table is %s rather than the expected value of %s", + name, qualifier, SidecarDBName) + } + if !strings.EqualFold(tableName, name) { + return "", fmt.Errorf("table name of %s does not match the table name specified within the file: %s", name, tableName) + } + if !createTable.IfNotExists { + return "", fmt.Errorf("%s file did not include the required IF NOT EXISTS clause in the CREATE TABLE statement for the %s table", name, tableName) + } + normalizedSchema := sqlparser.CanonicalString(createTable) + return normalizedSchema, nil +} + +func initSchemaFiles() { + sqlFileExtension := ".sql" + err := fs.WalkDir(schemaLocation, ".", func(path string, entry fs.DirEntry, err error) error { + if err != nil { + return err + } + if !entry.IsDir() { + var module string + dir, fname := filepath.Split(path) + if !strings.HasSuffix(strings.ToLower(fname), sqlFileExtension) { + log.Infof("Ignoring non-SQL file: %s, found during sidecar database initialization", path) + return nil + } + dirparts := strings.Split(strings.Trim(dir, "/"), "/") + switch len(dirparts) { + case 1: + module = dir + case 2: + module = fmt.Sprintf("%s/%s", dirparts[0], dirparts[1]) + default: + return fmt.Errorf("unexpected path value of %s specified for sidecar schema table; expected structure is [/]/.sql", dir) + } + + name := strings.Split(fname, ".")[0] + schema, err := schemaLocation.ReadFile(path) + if err != nil { + panic(err) + } + var normalizedSchema string + if normalizedSchema, err = validateSchemaDefinition(name, string(schema)); err != nil { + return err + } + sidecarTables = append(sidecarTables, &sidecarTable{name: name, module: module, path: path, schema: normalizedSchema}) + } + return nil + }) + if err != nil { + log.Errorf("error loading schema files: %+v", err) + } +} + +// printCallerDetails is a helper for dev debugging. +func printCallerDetails() { + pc, _, line, ok := runtime.Caller(2) + details := runtime.FuncForPC(pc) + if ok && details != nil { + log.Infof("%s schema init called from %s:%d\n", SidecarDBName, details.Name(), line) + } +} + +type schemaInit struct { + ctx context.Context + exec Exec + existingTables map[string]bool + dbCreated bool // The first upgrade/create query will also create the sidecar database if required. +} + +// Exec is a callback that has to be passed to Init() to execute the specified query in the database. +type Exec func(ctx context.Context, query string, maxRows int, useDB bool) (*sqltypes.Result, error) + +// GetDDLCount metric returns the count of sidecardb ddls that have been run as part of this vttablet's init process. +func GetDDLCount() int64 { + return ddlCount.Get() +} + +// Init creates or upgrades the sidecar database based on declarative schema for all tables in the schema. +func Init(ctx context.Context, exec Exec) error { + printCallerDetails() // for debug purposes only, remove in v17 + log.Infof("Starting sidecardb.Init()") + si := &schemaInit{ + ctx: ctx, + exec: exec, + } + + // There are paths in the tablet initialization where we are in read-only mode but the schema is already updated. + // Hence, we should not always try to create the database, since it will then error out as the db is read-only. + dbExists, err := si.doesSidecarDBExist() + if err != nil { + return err + } + if !dbExists { + if err := si.createSidecarDB(); err != nil { + return err + } + si.dbCreated = true + } + + if _, err := si.setCurrentDatabase(SidecarDBName); err != nil { + return err + } + + for _, table := range sidecarTables { + if err := si.ensureSchema(table); err != nil { + return err + } + } + return nil +} + +func (si *schemaInit) doesSidecarDBExist() (bool, error) { + rs, err := si.exec(si.ctx, ShowSidecarDatabasesQuery, 2, false) + if err != nil { + log.Error(err) + return false, err + } + + switch len(rs.Rows) { + case 0: + log.Infof("doesSidecarDBExist: not found") + return false, nil + case 1: + log.Infof("doesSidecarDBExist: found") + return true, nil + default: + log.Errorf("found too many rows for sidecarDB %s: %d", SidecarDBName, len(rs.Rows)) + return false, fmt.Errorf("found too many rows for sidecarDB %s: %d", SidecarDBName, len(rs.Rows)) + } +} + +func (si *schemaInit) createSidecarDB() error { + _, err := si.exec(si.ctx, CreateSidecarDatabaseQuery, 1, false) + if err != nil { + log.Error(err) + return err + } + log.Infof("createSidecarDB: %s", CreateSidecarDatabaseQuery) + return nil +} + +// Sets db of current connection, returning the currently selected database. +func (si *schemaInit) setCurrentDatabase(dbName string) (string, error) { + rs, err := si.exec(si.ctx, SelectCurrentDatabaseQuery, 1, false) + if err != nil { + return "", err + } + if rs == nil || rs.Rows == nil { // we get this in tests + return "", nil + } + currentDB := rs.Rows[0][0].ToString() + if currentDB != "" { // while running tests we can get currentDB as empty + _, err = si.exec(si.ctx, fmt.Sprintf("use %s", dbName), 1, false) + if err != nil { + return "", err + } + } + return currentDB, nil +} + +// Gets existing schema of a table in the sidecar database. +func (si *schemaInit) getCurrentSchema(tableName string) (string, error) { + var currentTableSchema string + + rs, err := si.exec(si.ctx, fmt.Sprintf(ShowCreateTableQuery, tableName), 1, false) + if err != nil { + if sqlErr, ok := err.(*mysql.SQLError); ok && sqlErr.Number() == mysql.ERNoSuchTable { + // table does not exist in the sidecar database + return "", nil + } + log.Errorf("Error getting table schema for %s: %+v", tableName, err) + return "", err + } + if len(rs.Rows) > 0 { + currentTableSchema = rs.Rows[0][1].ToString() + } + return currentTableSchema, nil +} + +// findTableSchemaDiff gets the diff that needs to be applied to current table schema to get the desired one. Will be an empty string if they match. +// This could be a CREATE statement if the table does not exist or an ALTER if table exists but has a different schema. +func (si *schemaInit) findTableSchemaDiff(tableName, current, desired string) (string, error) { + hints := &schemadiff.DiffHints{ + TableCharsetCollateStrategy: schemadiff.TableCharsetCollateIgnoreAlways, + } + diff, err := schemadiff.DiffCreateTablesQueries(current, desired, hints) + if err != nil { + return "", err + } + + var ddl string + if diff != nil { + ddl = diff.CanonicalStatementString() + + // Temporary logging to debug any eventual issues around the new schema init, should be removed in v17. + log.Infof("Current schema for table %s:\n%s", tableName, current) + if ddl == "" { + log.Infof("No changes needed for table %s", tableName) + } else { + log.Infof("Applying ddl for table %s:\n%s", tableName, ddl) + } + } + + return ddl, nil +} + +// ensureSchema first checks if the table exist, in which case it runs the create script provided in +// the schema directory. If the table exists, schemadiff is used to compare the existing schema with the desired one. +// If it needs to be altered then we run the alter script. +func (si *schemaInit) ensureSchema(table *sidecarTable) error { + ctx := si.ctx + desiredTableSchema := table.schema + + var ddl string + currentTableSchema, err := si.getCurrentSchema(table.name) + if err != nil { + return err + } + ddl, err = si.findTableSchemaDiff(table.name, currentTableSchema, desiredTableSchema) + if err != nil { + return err + } + + if ddl != "" { + if !si.dbCreated { + // We use CreateSidecarDatabaseQuery to also create the first binlog entry when a primary comes up. + // That statement doesn't make it to the replicas, so we run the query again so that it is replicated + // to the replicas so that the replicas can create the sidecar database. + if err := si.createSidecarDB(); err != nil { + return err + } + si.dbCreated = true + } + _, err := si.exec(ctx, ddl, 1, true) + if err != nil { + log.Errorf("Error running ddl %s for table %s during sidecar database initialization %s: %+v", ddl, table, err) + return err + } + log.Infof("Applied ddl %s for table %s during sidecar database initialization %s", ddl, table) + ddlCount.Add(1) + return nil + } + log.Infof("Table schema was already up to date for the %s table in the %s sidecar database", table.name, SidecarDBName) + return nil +} + +// region unit-test-only +// This section uses helpers used in tests, but also in the go/vt/vtexplain/vtexplain_vttablet.go. +// Hence, it is here and not in the _test.go file. + +// Query patterns to handle in mocks. +var sidecarDBInitQueries = []string{ + ShowSidecarDatabasesQuery, + SelectCurrentDatabaseQuery, + CreateSidecarDatabaseQuery, + UseSidecarDatabaseQuery, +} + +var sidecarDBInitQueryPatterns = []string{ + CreateTableRegexp, + AlterTableRegexp, +} + +// AddSchemaInitQueries adds sidecar database schema related queries to a mock db. +func AddSchemaInitQueries(db *fakesqldb.DB, populateTables bool) { + result := &sqltypes.Result{} + for _, q := range sidecarDBInitQueryPatterns { + db.AddQueryPattern(q, result) + } + for _, q := range sidecarDBInitQueries { + db.AddQuery(q, result) + } + for _, table := range sidecarTables { + result = &sqltypes.Result{} + if populateTables { + result = sqltypes.MakeTestResult(sqltypes.MakeTestFields( + "Table|Create Table", + "varchar|varchar"), + fmt.Sprintf("%s|%s", table.name, table.schema), + ) + } + db.AddQuery(fmt.Sprintf(ShowCreateTableQuery, table.name), result) + } +} + +// MatchesInitQuery returns true if query has one of the test patterns as a substring, or it matches a provided regexp. +func MatchesInitQuery(query string) bool { + query = strings.ToLower(query) + for _, q := range sidecarDBInitQueries { + if strings.EqualFold(q, query) { + return true + } + } + for _, q := range sidecarDBInitQueryPatterns { + q = strings.ToLower(q) + if strings.Contains(query, q) { + return true + } + if match, _ := regexp.MatchString(q, query); match { + return true + } + } + return false +} + +// endregion diff --git a/go/vt/sidecardb/sidecardb_test.go b/go/vt/sidecardb/sidecardb_test.go new file mode 100644 index 00000000000..0c2cfcd249e --- /dev/null +++ b/go/vt/sidecardb/sidecardb_test.go @@ -0,0 +1,108 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package sidecardb + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "vitess.io/vitess/go/mysql/fakesqldb" + "vitess.io/vitess/go/sqltypes" +) + +// Tests all non-error code paths in sidecardb +func TestAll(t *testing.T) { + db := fakesqldb.New(t) + defer db.Close() + AddSchemaInitQueries(db, false) + db.AddQuery("use dbname", &sqltypes.Result{}) + + ctx := context.Background() + cp := db.ConnParams() + conn, err := cp.Connect(ctx) + require.NoError(t, err) + exec := func(ctx context.Context, query string, maxRows int, useDB bool) (*sqltypes.Result, error) { + if useDB { + if _, err := conn.ExecuteFetch(UseSidecarDatabaseQuery, maxRows, true); err != nil { + return nil, err + } + } + return conn.ExecuteFetch(query, maxRows, true) + } + + // tests init on empty db + require.Equal(t, int64(0), GetDDLCount()) + err = Init(ctx, exec) + require.NoError(t, err) + require.Equal(t, int64(len(sidecarTables)), GetDDLCount()) + + // tests init on already inited db + AddSchemaInitQueries(db, true) + err = Init(ctx, exec) + require.NoError(t, err) + require.Equal(t, int64(len(sidecarTables)), GetDDLCount()) + + // tests misc paths not covered above + si := &schemaInit{ + ctx: ctx, + exec: exec, + } + result := sqltypes.MakeTestResult(sqltypes.MakeTestFields( + "Database", + "varchar"), + "currentDB", + ) + db.AddQuery(SelectCurrentDatabaseQuery, result) + + currentDB, err := si.setCurrentDatabase("dbname") + require.NoError(t, err) + require.Equal(t, "currentDB", currentDB) + + require.False(t, MatchesInitQuery("abc")) + require.True(t, MatchesInitQuery(SelectCurrentDatabaseQuery)) + require.True(t, MatchesInitQuery("CREATE TABLE IF NOT EXISTS `_vt`.vreplication")) +} + +// test the logic that confirms that the user defined schema's table name and qualifier are valid +func TestValidateSchema(t *testing.T) { + type testCase struct { + testName string + name string + schema string + mustError bool + } + testCases := []testCase{ + {"valid", "t1", "create table if not exists _vt.t1(i int)", false}, + {"no if not exists", "t1", "create table _vt.t1(i int)", true}, + {"invalid table name", "t2", "create table if not exists _vt.t1(i int)", true}, + {"invalid table name", "t1", "create table if not exists _vt.t2(i int)", true}, + {"invalid qualifier", "t1", "create table if not exists vt_product.t1(i int)", true}, + {"invalid qualifier", "t1", "create table if not exists t1(i int)", true}, + } + for _, tc := range testCases { + t.Run(tc.testName, func(t *testing.T) { + _, err := validateSchemaDefinition(tc.name, tc.schema) + if tc.mustError { + require.Error(t, err) + } else { + require.NoError(t, err) + } + }) + } +} diff --git a/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go b/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go index 19a580c451c..e100bad310b 100644 --- a/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go +++ b/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go @@ -21,6 +21,8 @@ import ( "fmt" "testing" + "vitess.io/vitess/go/vt/mysqlctl" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -52,12 +54,7 @@ func TestInitShardPrimary(t *testing.T) { tablet1.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ "FAKE RESET ALL REPLICATION", - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", + mysqlctl.GenerateInitialBinlogEntry(), "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } @@ -119,12 +116,7 @@ func TestInitShardPrimaryNoFormerPrimary(t *testing.T) { tablet1.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ "FAKE RESET ALL REPLICATION", - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", + mysqlctl.GenerateInitialBinlogEntry(), "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } diff --git a/go/vt/vtctl/grpcvtctldserver/server.go b/go/vt/vtctl/grpcvtctldserver/server.go index fd5640d2529..c2186f66e2f 100644 --- a/go/vt/vtctl/grpcvtctldserver/server.go +++ b/go/vt/vtctl/grpcvtctldserver/server.go @@ -1930,7 +1930,7 @@ func (s *VtctldServer) GetVersion(ctx context.Context, req *vtctldatapb.GetVersi return nil, err } - version, err := getVersionFromTablet(tablet.Addr()) + version, err := GetVersionFunc()(tablet.Addr()) if err != nil { return nil, err } @@ -4426,8 +4426,21 @@ var getVersionFromTabletDebugVars = func(tabletAddr string) (string, error) { return version, nil } +var versionFuncMu sync.Mutex var getVersionFromTablet = getVersionFromTabletDebugVars +func SetVersionFunc(versionFunc func(string) (string, error)) { + versionFuncMu.Lock() + defer versionFuncMu.Unlock() + getVersionFromTablet = versionFunc +} + +func GetVersionFunc() func(string) (string, error) { + versionFuncMu.Lock() + defer versionFuncMu.Unlock() + return getVersionFromTablet +} + // helper method to asynchronously get and diff a version func (s *VtctldServer) diffVersion(ctx context.Context, primaryVersion string, primaryAlias *topodatapb.TabletAlias, alias *topodatapb.TabletAlias, wg *sync.WaitGroup, er concurrency.ErrorRecorder) { defer wg.Done() diff --git a/go/vt/vtctl/grpcvtctldserver/server_test.go b/go/vt/vtctl/grpcvtctldserver/server_test.go index 2e3bf21c822..05ac69768e8 100644 --- a/go/vt/vtctl/grpcvtctldserver/server_test.go +++ b/go/vt/vtctl/grpcvtctldserver/server_test.go @@ -21,10 +21,13 @@ import ( "errors" "fmt" "io" + "os" "sort" "testing" "time" + _flag "vitess.io/vitess/go/internal/flag" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" @@ -11227,7 +11230,7 @@ func TestValidateVersionKeyspace(t *testing.T) { "primary:0": "version1", "replica:0": "version1", } - getVersionFromTablet = testutil.MockGetVersionFromTablet(addrVersionMap) + SetVersionFunc(testutil.MockGetVersionFromTablet(addrVersionMap)) }, shouldErr: false, }, @@ -11247,7 +11250,7 @@ func TestValidateVersionKeyspace(t *testing.T) { "primary:0": "version1", "replica:0": "version2", } - getVersionFromTablet = testutil.MockGetVersionFromTablet(addrVersionMap) + SetVersionFunc(testutil.MockGetVersionFromTablet(addrVersionMap)) }, shouldErr: false, }, @@ -11339,7 +11342,7 @@ func TestValidateVersionShard(t *testing.T) { "primary:0": "version1", "replica:0": "version1", } - getVersionFromTablet = testutil.MockGetVersionFromTablet(addrVersionMap) + SetVersionFunc(testutil.MockGetVersionFromTablet(addrVersionMap)) }, shouldErr: false, }, @@ -11357,7 +11360,7 @@ func TestValidateVersionShard(t *testing.T) { "primary:0": "version1", "replica:0": "version2", } - getVersionFromTablet = testutil.MockGetVersionFromTablet(addrVersionMap) + SetVersionFunc(testutil.MockGetVersionFromTablet(addrVersionMap)) }, shouldErr: false, }, @@ -11924,3 +11927,7 @@ func TestValidateShard(t *testing.T) { }) } } +func TestMain(m *testing.M) { + _flag.ParseFlagsForTest() + os.Exit(m.Run()) +} diff --git a/go/vt/vtctl/schematools/copy.go b/go/vt/vtctl/schematools/copy.go deleted file mode 100644 index 6b99e63b79e..00000000000 --- a/go/vt/vtctl/schematools/copy.go +++ /dev/null @@ -1,101 +0,0 @@ -/* -Copyright 2021 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package schematools - -import ( - "bytes" - "context" - "fmt" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/topo" - "vitess.io/vitess/go/vt/topo/topoproto" - "vitess.io/vitess/go/vt/vttablet/tmclient" - - tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -// CopyShardMetadata copies the contents of the _vt.shard_metadata table from -// the source tablet to the destination tablet. -// -// NOTE: This function assumes that the destination tablet is a primary with -// binary logging enabled, in order to propagate the INSERT statements to any -// replicas in the destination shard. -func CopyShardMetadata(ctx context.Context, ts *topo.Server, tmc tmclient.TabletManagerClient, source *topodatapb.TabletAlias, dest *topodatapb.TabletAlias) error { - sourceTablet, err := ts.GetTablet(ctx, source) - if err != nil { - return fmt.Errorf("GetTablet(%v) failed: %w", topoproto.TabletAliasString(source), err) - } - - destTablet, err := ts.GetTablet(ctx, dest) - if err != nil { - return fmt.Errorf("GetTablet(%v) failed: %w", topoproto.TabletAliasString(dest), err) - } - - sql := "SELECT 1 FROM information_schema.tables WHERE table_schema = '_vt' AND table_name = 'shard_metadata'" - presenceResult, err := tmc.ExecuteFetchAsDba(ctx, sourceTablet.Tablet, false, &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{ - Query: []byte(sql), - MaxRows: 1, - }) - if err != nil { - return fmt.Errorf("ExecuteFetchAsDba(%v, false, %v, 1, false, false) failed: %v", topoproto.TabletAliasString(source), sql, err) - } - if len(presenceResult.Rows) == 0 { - log.Infof("_vt.shard_metadata doesn't exist on the source tablet %v, skipping its copy.", topoproto.TabletAliasString(source)) - return nil - } - - // (TODO|@ajm188,@deepthi): 100 may be too low here for row limit - sql = "SELECT db_name, name, value FROM _vt.shard_metadata" - p3qr, err := tmc.ExecuteFetchAsDba(ctx, sourceTablet.Tablet, false, &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{ - Query: []byte(sql), - MaxRows: 100, - }) - if err != nil { - return fmt.Errorf("ExecuteFetchAsDba(%v, false, %v, 100, false, false) failed: %v", topoproto.TabletAliasString(source), sql, err) - } - - qr := sqltypes.Proto3ToResult(p3qr) - queryBuf := bytes.NewBuffer(nil) - for _, row := range qr.Rows { - dbName := row[0] - name := row[1] - value := row[2] - queryBuf.WriteString("INSERT INTO _vt.shard_metadata (db_name, name, value) VALUES (") - dbName.EncodeSQL(queryBuf) - queryBuf.WriteByte(',') - name.EncodeSQL(queryBuf) - queryBuf.WriteByte(',') - value.EncodeSQL(queryBuf) - queryBuf.WriteString(") ON DUPLICATE KEY UPDATE value = ") - value.EncodeSQL(queryBuf) - - _, err := tmc.ExecuteFetchAsDba(ctx, destTablet.Tablet, false, &tabletmanagerdatapb.ExecuteFetchAsDbaRequest{ - Query: queryBuf.Bytes(), - MaxRows: 0, - }) - if err != nil { - return fmt.Errorf("ExecuteFetchAsDba(%v, false, %v, 0, false, false) failed: %v", topoproto.TabletAliasString(dest), queryBuf.String(), err) - } - - queryBuf.Reset() - } - - return nil -} diff --git a/go/vt/vtexplain/testdata/plan_test1941596715/uneven-keyspace-output.txt b/go/vt/vtexplain/testdata/plan_test1941596715/uneven-keyspace-output.txt new file mode 100644 index 00000000000..38a22b7d315 --- /dev/null +++ b/go/vt/vtexplain/testdata/plan_test1941596715/uneven-keyspace-output.txt @@ -0,0 +1,16 @@ +---------------------------------------------------------------------- +select * from user + +1 ks_sharded/-80: select * from `user` limit 10001 +1 ks_sharded/80-90: select * from `user` limit 10001 +1 ks_sharded/a0-e8: select * from `user` limit 10001 +1 ks_sharded/e8-: select * from `user` limit 10001 +2 ks_sharded/90-a0: select * from `user` limit 10001 + +---------------------------------------------------------------------- +select * from user where id in (10, 17, 42, 100000) + +1 ks_sharded/-80: select * from `user` where id in (10, 17, 42) limit 10001 +1 ks_sharded/80-90: select * from `user` where id in (100000) limit 10001 + +---------------------------------------------------------------------- diff --git a/go/vt/vtexplain/vtexplain_vtgate.go b/go/vt/vtexplain/vtexplain_vtgate.go index 2a53d1f68ae..a4287377c4b 100644 --- a/go/vt/vtexplain/vtexplain_vtgate.go +++ b/go/vt/vtexplain/vtexplain_vtgate.go @@ -75,7 +75,7 @@ func (vte *VTExplain) initVtgateExecutor(vSchemaStr, ksShardMapStr string, opts vte.vtgateExecutor = vtgate.NewExecutor(context.Background(), vte.explainTopo, vtexplainCell, resolver, opts.Normalize, false, streamSize, cache.DefaultConfig, schemaTracker, false, opts.PlannerVersion) queryLogBufferSize := 10 - vtgate.QueryLogger = streamlog.New("VTGate", queryLogBufferSize) + vtgate.SetQueryLogger(streamlog.New("VTGate", queryLogBufferSize)) return nil } diff --git a/go/vt/vtexplain/vtexplain_vttablet.go b/go/vt/vtexplain/vtexplain_vttablet.go index 4238d9e64a9..6784efe4cbd 100644 --- a/go/vt/vtexplain/vtexplain_vttablet.go +++ b/go/vt/vtexplain/vtexplain_vttablet.go @@ -24,13 +24,12 @@ import ( "strings" "sync" - "vitess.io/vitess/go/sqlescape" - "vitess.io/vitess/go/vt/vtgate/evalengine" - "vitess.io/vitess/go/vt/vttablet/onlineddl" + "vitess.io/vitess/go/vt/sidecardb" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/fakesqldb" + "vitess.io/vitess/go/sqlescape" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" @@ -38,6 +37,7 @@ import ( "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vttablet/queryservice" "vitess.io/vitess/go/vt/vttablet/tabletserver" @@ -105,6 +105,7 @@ var _ queryservice.QueryService = (*explainTablet)(nil) func (vte *VTExplain) newTablet(opts *Options, t *topodatapb.Tablet) *explainTablet { db := fakesqldb.New(nil) + sidecardb.AddSchemaInitQueries(db, true) config := tabletenv.NewCurrentConfig() config.TrackSchemaVersions = false @@ -388,12 +389,6 @@ func newTabletEnvironment(ddls []sqlparser.DDLStatement, opts *Options) (*tablet for query, result := range schemaQueries { tEnv.addResult(query, result) } - for _, query := range onlineddl.ApplyDDL { - tEnv.addResult(query, &sqltypes.Result{ - Fields: []*querypb.Field{{Type: sqltypes.Uint64}}, - Rows: [][]sqltypes.Value{}, - }) - } showTableRows := make([][]sqltypes.Value, 0, 4) for _, ddl := range ddls { @@ -499,7 +494,10 @@ 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) } diff --git a/go/vt/vtgate/querylog.go b/go/vt/vtgate/querylog.go index 13b1d689642..33361e79421 100644 --- a/go/vt/vtgate/querylog.go +++ b/go/vt/vtgate/querylog.go @@ -18,6 +18,7 @@ package vtgate import ( "net/http" + "sync" "vitess.io/vitess/go/streamlog" ) @@ -33,11 +34,18 @@ var ( QueryzHandler = "/debug/queryz" // QueryLogger enables streaming logging of queries - QueryLogger *streamlog.StreamLogger + QueryLogger *streamlog.StreamLogger + queryLoggerMu sync.Mutex ) +func SetQueryLogger(logger *streamlog.StreamLogger) { + queryLoggerMu.Lock() + defer queryLoggerMu.Unlock() + QueryLogger = logger +} + func initQueryLogger(vtg *VTGate) error { - QueryLogger = streamlog.New("VTGate", queryLogBufferSize) + SetQueryLogger(streamlog.New("VTGate", queryLogBufferSize)) QueryLogger.ServeLogs(QueryLogHandler, streamlog.GetFormatter(QueryLogger)) http.HandleFunc(QueryLogzHandler, func(w http.ResponseWriter, r *http.Request) { diff --git a/go/vt/vtgate/vschemaacl/vschemaacl.go b/go/vt/vtgate/vschemaacl/vschemaacl.go index 83837245a4c..5345d1437fc 100644 --- a/go/vt/vtgate/vschemaacl/vschemaacl.go +++ b/go/vt/vtgate/vschemaacl/vschemaacl.go @@ -18,6 +18,7 @@ package vschemaacl import ( "strings" + "sync" "github.com/spf13/pflag" @@ -35,6 +36,8 @@ var ( // ddlACL contains a set of allowed usernames acl map[string]struct{} + + initMu sync.Mutex ) // RegisterSchemaACLFlags installs log flags on the given FlagSet. @@ -54,6 +57,8 @@ func init() { // Init parses the users option and sets allowAll / acl accordingly func Init() { + initMu.Lock() + defer initMu.Unlock() acl = make(map[string]struct{}) allowAll = false diff --git a/go/vt/vttablet/onlineddl/executor.go b/go/vt/vttablet/onlineddl/executor.go index 1d8bed216af..b35f3c0f1a0 100644 --- a/go/vt/vttablet/onlineddl/executor.go +++ b/go/vt/vttablet/onlineddl/executor.go @@ -36,8 +36,6 @@ import ( "github.com/spf13/pflag" - "vitess.io/vitess/go/vt/withddl" - "google.golang.org/protobuf/proto" "google.golang.org/protobuf/encoding/prototext" @@ -293,42 +291,6 @@ func (e *Executor) TabletAliasString() string { return topoproto.TabletAliasString(e.tabletAlias) } -// PrepareForQueryExecutor is called by QueryExecutor, possibly before the backing -// _vt.schema_migrations table has had the chance to be created. -// This function prepares the schema. -func (e *Executor) PrepareForQueryExecutor(ctx context.Context) error { - return e.initSchema(ctx) -} - -func (e *Executor) initSchema(ctx context.Context) error { - e.initMutex.Lock() - defer e.initMutex.Unlock() - - if e.schemaInitialized { - return nil - } - - defer e.env.LogError() - - conn, err := dbconnpool.NewDBConnection(ctx, e.env.Config().DB.DbaConnector()) - if err != nil { - return err - } - defer conn.Close() - - for _, ddl := range ApplyDDL { - _, err := conn.ExecuteFetch(ddl, math.MaxInt32, false) - if mysql.IsSchemaApplyError(err) { - continue - } - if err != nil { - return err - } - } - e.schemaInitialized = true - return nil -} - // InitDBConfig initializes keysapce func (e *Executor) InitDBConfig(keyspace, shard, dbName string) { e.keyspace = keyspace @@ -3798,11 +3760,6 @@ func (e *Executor) vreplicationExec(ctx context.Context, tablet *topodatapb.Tabl tmClient := e.tabletManagerClient() defer tmClient.Close() - e.initVreplicationDDLOnce.Do(func() { - // Ensure vreplication schema is up-to-date by invoking a query with non-existing columns. - // This will make vreplication run through its WithDDL schema changes. - _, _ = tmClient.VReplicationExec(ctx, tablet, withddl.QueryToTriggerWithDDL) - }) return tmClient.VReplicationExec(ctx, tablet, query) } @@ -3952,10 +3909,6 @@ func (e *Executor) onMigrationCheckTick() { } ctx := context.Background() - if err := e.initSchema(ctx); err != nil { - log.Error(err) - return - } if err := e.retryTabletFailureMigrations(ctx); err != nil { log.Error(err) } @@ -4677,11 +4630,6 @@ func (e *Executor) SubmitMigration( return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "Error submitting migration %s: %v", sqlparser.String(stmt), err) } - if err := e.initSchema(ctx); err != nil { - log.Error(err) - return nil, err - } - // The logic below has multiple steps. We hence protect the rest of the code with a mutex, only used by this function. e.submitMutex.Lock() defer e.submitMutex.Unlock() @@ -4751,6 +4699,7 @@ func (e *Executor) SubmitMigration( ) if err != nil { return nil, vterrors.Wrapf(err, "submitting migration %v", onlineDDL.UUID) + } log.Infof("SubmitMigration: migration %s submitted", onlineDDL.UUID) @@ -4882,11 +4831,6 @@ func (e *Executor) VExec(ctx context.Context, vx *vexec.TabletVExec) (qr *queryp return sqltypes.ResultToProto3(result), nil } - if err := e.initSchema(ctx); err != nil { - log.Error(err) - return nil, err - } - switch stmt := vx.Stmt.(type) { case *sqlparser.Delete: return nil, fmt.Errorf("DELETE statements not supported for this table. query=%s", vx.Query) diff --git a/go/vt/vttablet/onlineddl/schema.go b/go/vt/vttablet/onlineddl/schema.go index 33ae3ab48df..adf550dabfe 100644 --- a/go/vt/vttablet/onlineddl/schema.go +++ b/go/vt/vttablet/onlineddl/schema.go @@ -17,76 +17,6 @@ limitations under the License. package onlineddl const ( - // SchemaMigrationsTableName is used by VExec interceptor to call the correct handler - sqlCreateSidecarDB = "create database if not exists _vt" - sqlCreateSchemaMigrationsTable = `CREATE TABLE IF NOT EXISTS _vt.schema_migrations ( - id bigint(20) unsigned NOT NULL AUTO_INCREMENT, - migration_uuid varchar(64) NOT NULL, - keyspace varchar(256) NOT NULL, - shard varchar(255) NOT NULL, - mysql_schema varchar(128) NOT NULL, - mysql_table varchar(128) NOT NULL, - migration_statement text NOT NULL, - strategy varchar(128) NOT NULL, - options varchar(8192) NOT NULL, - added_timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - requested_timestamp timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - ready_timestamp timestamp NULL DEFAULT NULL, - started_timestamp timestamp NULL DEFAULT NULL, - liveness_timestamp timestamp NULL DEFAULT NULL, - completed_timestamp timestamp NULL DEFAULT NULL, - cleanup_timestamp timestamp NULL DEFAULT NULL, - migration_status varchar(128) NOT NULL, - log_path varchar(1024) NOT NULL, - artifacts varchar(1024) NOT NULL, - PRIMARY KEY (id), - UNIQUE KEY uuid_idx (migration_uuid), - KEY keyspace_shard_idx (keyspace(64),shard(64)), - KEY status_idx (migration_status, liveness_timestamp), - KEY cleanup_status_idx (cleanup_timestamp, migration_status) - ) engine=InnoDB DEFAULT CHARSET=utf8mb4` - alterSchemaMigrationsTableRetries = "ALTER TABLE _vt.schema_migrations add column retries int unsigned NOT NULL DEFAULT 0" - alterSchemaMigrationsTableTablet = "ALTER TABLE _vt.schema_migrations add column tablet varchar(128) NOT NULL DEFAULT ''" - alterSchemaMigrationsTableArtifacts = "ALTER TABLE _vt.schema_migrations modify artifacts TEXT NOT NULL" - alterSchemaMigrationsTableTabletFailure = "ALTER TABLE _vt.schema_migrations add column tablet_failure tinyint unsigned NOT NULL DEFAULT 0" - alterSchemaMigrationsTableTabletFailureIndex = "ALTER TABLE _vt.schema_migrations add KEY tablet_failure_idx (tablet_failure, migration_status, retries)" - alterSchemaMigrationsTableProgress = "ALTER TABLE _vt.schema_migrations add column progress float NOT NULL DEFAULT 0" - alterSchemaMigrationsTableContext = "ALTER TABLE _vt.schema_migrations add column migration_context varchar(1024) NOT NULL DEFAULT ''" - alterSchemaMigrationsTableDDLAction = "ALTER TABLE _vt.schema_migrations add column ddl_action varchar(16) NOT NULL DEFAULT ''" - alterSchemaMigrationsTableMessage = "ALTER TABLE _vt.schema_migrations add column message TEXT NOT NULL" - alterSchemaMigrationsTableTableCompleteIndex = "ALTER TABLE _vt.schema_migrations add KEY table_complete_idx (migration_status, keyspace(64), mysql_table(64), completed_timestamp)" - alterSchemaMigrationsTableETASeconds = "ALTER TABLE _vt.schema_migrations add column eta_seconds bigint NOT NULL DEFAULT -1" - alterSchemaMigrationsTableRowsCopied = "ALTER TABLE _vt.schema_migrations add column rows_copied bigint unsigned NOT NULL DEFAULT 0" - alterSchemaMigrationsTableTableRows = "ALTER TABLE _vt.schema_migrations add column table_rows bigint NOT NULL DEFAULT 0" - alterSchemaMigrationsTableAddedUniqueKeys = "ALTER TABLE _vt.schema_migrations add column added_unique_keys int unsigned NOT NULL DEFAULT 0" - alterSchemaMigrationsTableRemovedUniqueKeys = "ALTER TABLE _vt.schema_migrations add column removed_unique_keys int unsigned NOT NULL DEFAULT 0" - alterSchemaMigrationsTableLogFile = "ALTER TABLE _vt.schema_migrations add column log_file varchar(1024) NOT NULL DEFAULT ''" - alterSchemaMigrationsTableRetainArtifacts = "ALTER TABLE _vt.schema_migrations add column retain_artifacts_seconds bigint NOT NULL DEFAULT 0" - alterSchemaMigrationsTablePostponeCompletion = "ALTER TABLE _vt.schema_migrations add column postpone_completion tinyint unsigned NOT NULL DEFAULT 0" - alterSchemaMigrationsTableContextIndex = "ALTER TABLE _vt.schema_migrations add KEY migration_context_idx (migration_context(64))" - alterSchemaMigrationsTableRemovedUniqueNames = "ALTER TABLE _vt.schema_migrations add column removed_unique_key_names text NOT NULL" - alterSchemaMigrationsTableRemovedNoDefaultColNames = "ALTER TABLE _vt.schema_migrations add column dropped_no_default_column_names text NOT NULL" - alterSchemaMigrationsTableExpandedColNames = "ALTER TABLE _vt.schema_migrations add column expanded_column_names text NOT NULL" - alterSchemaMigrationsTableRevertibleNotes = "ALTER TABLE _vt.schema_migrations add column revertible_notes text NOT NULL" - alterSchemaMigrationsTableAllowConcurrent = "ALTER TABLE _vt.schema_migrations add column allow_concurrent tinyint unsigned NOT NULL DEFAULT 0" - alterSchemaMigrationsTableRevertedUUID = "ALTER TABLE _vt.schema_migrations add column reverted_uuid varchar(64) NOT NULL DEFAULT ''" - alterSchemaMigrationsTableRevertedUUIDIndex = "ALTER TABLE _vt.schema_migrations add KEY reverted_uuid_idx (reverted_uuid(64))" - alterSchemaMigrationsTableIsView = "ALTER TABLE _vt.schema_migrations add column is_view tinyint unsigned NOT NULL DEFAULT 0" - alterSchemaMigrationsTableReadyToComplete = "ALTER TABLE _vt.schema_migrations add column ready_to_complete tinyint unsigned NOT NULL DEFAULT 0" - alterSchemaMigrationsTableStowawayTable = "ALTER TABLE _vt.schema_migrations add column stowaway_table tinytext NOT NULL" - alterSchemaMigrationsTableVreplLivenessIndicator = "ALTER TABLE _vt.schema_migrations add column vitess_liveness_indicator bigint NOT NULL DEFAULT 0" - alterSchemaMigrationsTableUserThrottleRatio = "ALTER TABLE _vt.schema_migrations add column user_throttle_ratio float NOT NULL DEFAULT 0" - alterSchemaMigrationsTableSpecialPlan = "ALTER TABLE _vt.schema_migrations add column special_plan text NOT NULL" - alterSchemaMigrationsLastThrottled = "ALTER TABLE _vt.schema_migrations add column last_throttled_timestamp timestamp NULL DEFAULT NULL" - alterSchemaMigrationsComponentThrottled = "ALTER TABLE _vt.schema_migrations add column component_throttled tinytext NOT NULL" - alterSchemaMigrationsCancelledTimestamp = "ALTER TABLE _vt.schema_migrations add column cancelled_timestamp timestamp NULL DEFAULT NULL" - alterSchemaMigrationsTablePostponeLaunch = "ALTER TABLE _vt.schema_migrations add column postpone_launch tinyint unsigned NOT NULL DEFAULT 0" - alterSchemaMigrationsStage = "ALTER TABLE _vt.schema_migrations add column stage text not null" - alterSchemaMigrationsCutoverAttempts = "ALTER TABLE _vt.schema_migrations add column cutover_attempts int unsigned NOT NULL DEFAULT 0" - alterSchemaMigrationsTableImmediateOperation = "ALTER TABLE _vt.schema_migrations add column is_immediate_operation tinyint unsigned NOT NULL DEFAULT 0" - alterSchemaMigrationsReviewedTimestamp = "ALTER TABLE _vt.schema_migrations add column reviewed_timestamp timestamp NULL DEFAULT NULL" - alterSchemaMigrationsCompletedTimestampResolution = "ALTER TABLE _vt.schema_migrations modify completed_timestamp timestamp(6) NULL DEFAULT NULL" - sqlInsertMigration = `INSERT IGNORE INTO _vt.schema_migrations ( migration_uuid, keyspace, @@ -642,50 +572,3 @@ var ( } sqlDropOnlineDDLUser = `DROP USER IF EXISTS %s` ) - -// ApplyDDL ddls to be applied at the start -var ApplyDDL = []string{ - sqlCreateSidecarDB, - sqlCreateSchemaMigrationsTable, - alterSchemaMigrationsTableRetries, - alterSchemaMigrationsTableTablet, - alterSchemaMigrationsTableArtifacts, - alterSchemaMigrationsTableTabletFailure, - alterSchemaMigrationsTableTabletFailureIndex, - alterSchemaMigrationsTableProgress, - alterSchemaMigrationsTableContext, - alterSchemaMigrationsTableDDLAction, - alterSchemaMigrationsTableMessage, - alterSchemaMigrationsTableTableCompleteIndex, - alterSchemaMigrationsTableETASeconds, - alterSchemaMigrationsTableRowsCopied, - alterSchemaMigrationsTableTableRows, - alterSchemaMigrationsTableAddedUniqueKeys, - alterSchemaMigrationsTableRemovedUniqueKeys, - alterSchemaMigrationsTableLogFile, - alterSchemaMigrationsTableRetainArtifacts, - alterSchemaMigrationsTablePostponeCompletion, - alterSchemaMigrationsTableContextIndex, - alterSchemaMigrationsTableRemovedUniqueNames, - alterSchemaMigrationsTableRemovedNoDefaultColNames, - alterSchemaMigrationsTableExpandedColNames, - alterSchemaMigrationsTableRevertibleNotes, - alterSchemaMigrationsTableAllowConcurrent, - alterSchemaMigrationsTableRevertedUUID, - alterSchemaMigrationsTableRevertedUUIDIndex, - alterSchemaMigrationsTableIsView, - alterSchemaMigrationsTableReadyToComplete, - alterSchemaMigrationsTableStowawayTable, - alterSchemaMigrationsTableVreplLivenessIndicator, - alterSchemaMigrationsTableUserThrottleRatio, - alterSchemaMigrationsTableSpecialPlan, - alterSchemaMigrationsLastThrottled, - alterSchemaMigrationsComponentThrottled, - alterSchemaMigrationsCancelledTimestamp, - alterSchemaMigrationsTablePostponeLaunch, - alterSchemaMigrationsStage, - alterSchemaMigrationsCutoverAttempts, - alterSchemaMigrationsTableImmediateOperation, - alterSchemaMigrationsReviewedTimestamp, - alterSchemaMigrationsCompletedTimestampResolution, -} diff --git a/go/vt/vttablet/tabletmanager/restore.go b/go/vt/vttablet/tabletmanager/restore.go index 181dce93c3c..9d4b3b174f3 100644 --- a/go/vt/vttablet/tabletmanager/restore.go +++ b/go/vt/vttablet/tabletmanager/restore.go @@ -159,8 +159,6 @@ func (tm *TabletManager) restoreDataLocked(ctx context.Context, logger logutil.L // Try to restore. Depending on the reason for failure, we may be ok. // If we're not ok, return an error and the tm will log.Fatalf, // causing the process to be restarted and the restore retried. - // Record local metadata values based on the original type. - localMetadata := tm.getLocalMetadataValues(originalType) keyspace := tablet.Keyspace keyspaceInfo, err := tm.TopoServer.GetKeyspace(ctx, keyspace) @@ -184,7 +182,6 @@ func (tm *TabletManager) restoreDataLocked(ctx context.Context, logger logutil.L Logger: logger, Concurrency: restoreConcurrency, HookExtraEnv: tm.hookExtraEnv(), - LocalMetadata: localMetadata, DeleteBeforeRestore: deleteBeforeRestore, DbName: topoproto.TabletDbName(tablet), Keyspace: keyspace, @@ -209,11 +206,7 @@ func (tm *TabletManager) restoreDataLocked(ctx context.Context, logger logutil.L } if !ok { params.Logger.Infof("Attempting to restore, but mysqld already contains data. Assuming vttablet was just restarted.") - // (NOTE:@ajm188) the legacy behavior is to always populate the metadata - // tables in this branch. Since tm.MetadataManager could be nil, we - // create a new instance for use here. - metadataManager := &mysqlctl.MetadataManager{} - return metadataManager.PopulateMetadataTables(params.Mysqld, params.LocalMetadata, params.DbName) + return nil } // We should not become primary after restore, because that would incorrectly // start a new primary term, and it's likely our data dir will be out of date. @@ -308,7 +301,7 @@ func (tm *TabletManager) restoreDataLocked(ctx context.Context, logger logutil.L params.Logger.Infof("Restore: will set tablet type to DRAINED as this is a point in time recovery") originalType = topodatapb.TabletType_DRAINED } - params.Logger.Infof("Restore: changing tablet type to %v", originalType) + params.Logger.Infof("Restore: changing tablet type to %v for %s", originalType, tm.tabletAlias.String()) // Change type back to original type if we're ok to serve. return tm.tmState.ChangeTabletType(ctx, originalType, DBActionNone) } diff --git a/go/vt/vttablet/tabletmanager/rpc_replication.go b/go/vt/vttablet/tabletmanager/rpc_replication.go index 588844088f7..f952a2f5cb8 100644 --- a/go/vt/vttablet/tabletmanager/rpc_replication.go +++ b/go/vt/vttablet/tabletmanager/rpc_replication.go @@ -301,7 +301,7 @@ func (tm *TabletManager) ResetReplication(ctx context.Context) error { // InitPrimary enables writes and returns the replication position. func (tm *TabletManager) InitPrimary(ctx context.Context, semiSync bool) (string, error) { - log.Infof("InitPrimary") + log.Infof("InitPrimary with semiSync as %t", semiSync) if err := tm.lock(ctx); err != nil { return "", err } @@ -321,17 +321,12 @@ func (tm *TabletManager) InitPrimary(ctx context.Context, semiSync bool) (string } } - // we need to insert something in the binlogs, so we can get the - // current position. Let's just use the mysqlctl.CreateReparentJournal commands. - cmds := mysqlctl.CreateReparentJournal() - if err := tm.MysqlDaemon.ExecuteSuperQueryList(ctx, cmds); err != nil { + // we need to generate a binlog entry so that we can get the current position. + cmd := mysqlctl.GenerateInitialBinlogEntry() + if err := tm.MysqlDaemon.ExecuteSuperQueryList(ctx, []string{cmd}); err != nil { return "", err } - // Execute ALTER statement on reparent_journal table and ignore errors - cmds = mysqlctl.AlterReparentJournal() - _ = tm.MysqlDaemon.ExecuteSuperQueryList(ctx, cmds) - // get the current replication position pos, err := tm.MysqlDaemon.PrimaryPosition() if err != nil { @@ -345,7 +340,7 @@ func (tm *TabletManager) InitPrimary(ctx context.Context, semiSync bool) (string return "", err } - // Enforce semi-sync after changing the tablet)type to PRIMARY. Otherwise, the + // Enforce semi-sync after changing the tablet type to PRIMARY. Otherwise, the // primary will hang while trying to create the database. if err := tm.fixSemiSync(topodatapb.TabletType_PRIMARY, convertBoolToSemiSyncAction(semiSync)); err != nil { return "", err @@ -356,21 +351,14 @@ func (tm *TabletManager) InitPrimary(ctx context.Context, semiSync bool) (string // PopulateReparentJournal adds an entry into the reparent_journal table. func (tm *TabletManager) PopulateReparentJournal(ctx context.Context, timeCreatedNS int64, actionName string, primaryAlias *topodatapb.TabletAlias, position string) error { - log.Infof("PopulateReparentJournal: action: %v parent: %v position: %v", actionName, primaryAlias, position) + log.Infof("PopulateReparentJournal: action: %v parent: %v position: %v timeCreatedNS: %d actionName: %s primaryAlias: %s", + actionName, primaryAlias, position, timeCreatedNS, actionName, primaryAlias) pos, err := mysql.DecodePosition(position) if err != nil { return err } - cmds := mysqlctl.CreateReparentJournal() - if err := tm.MysqlDaemon.ExecuteSuperQueryList(ctx, cmds); err != nil { - return err - } - // Execute ALTER statement on reparent_journal table and ignore errors - cmds = mysqlctl.AlterReparentJournal() - _ = tm.MysqlDaemon.ExecuteSuperQueryList(ctx, cmds) - - cmds = []string{mysqlctl.PopulateReparentJournal(timeCreatedNS, actionName, topoproto.TabletAliasString(primaryAlias), pos)} + cmds := []string{mysqlctl.PopulateReparentJournal(timeCreatedNS, actionName, topoproto.TabletAliasString(primaryAlias), pos)} return tm.MysqlDaemon.ExecuteSuperQueryList(ctx, cmds) } @@ -378,7 +366,7 @@ func (tm *TabletManager) PopulateReparentJournal(ctx context.Context, timeCreate // InitReplica sets replication primary and position, and waits for the // reparent_journal table entry up to context timeout func (tm *TabletManager) InitReplica(ctx context.Context, parent *topodatapb.TabletAlias, position string, timeCreatedNS int64, semiSync bool) error { - log.Infof("InitReplica: parent: %v position: %v", parent, position) + log.Infof("InitReplica: parent: %v position: %v timeCreatedNS: %d semisync: %t", parent, position, timeCreatedNS, semiSync) if err := tm.lock(ctx); err != nil { return err } @@ -401,7 +389,6 @@ func (tm *TabletManager) InitReplica(ctx context.Context, parent *topodatapb.Tab if err != nil { return err } - tm.replManager.setReplicationStopped(false) // If using semi-sync, we need to enable it before connecting to primary. @@ -418,7 +405,7 @@ func (tm *TabletManager) InitReplica(ctx context.Context, parent *topodatapb.Tab if err := tm.MysqlDaemon.SetReplicationPosition(ctx, pos); err != nil { return err } - if err := tm.MysqlDaemon.SetReplicationSource(ctx, ti.Tablet.MysqlHostname, int(ti.Tablet.MysqlPort), false /* stopReplicationBefore */, true /* stopReplicationAfter */); err != nil { + if err := tm.MysqlDaemon.SetReplicationSource(ctx, ti.Tablet.MysqlHostname, int(ti.Tablet.MysqlPort), false /* stopReplicationBefore */, true /* startReplicationAfter */); err != nil { return err } @@ -604,7 +591,7 @@ func (tm *TabletManager) ResetReplicationParameters(ctx context.Context) error { // SetReplicationSource sets replication primary, and waits for the // reparent_journal table entry up to context timeout func (tm *TabletManager) SetReplicationSource(ctx context.Context, parentAlias *topodatapb.TabletAlias, timeCreatedNS int64, waitPosition string, forceStartReplication bool, semiSync bool) error { - log.Infof("SetReplicationSource: parent: %v position: %v force: %v semiSync: %v", parentAlias, waitPosition, forceStartReplication, semiSync) + log.Infof("SetReplicationSource: parent: %v position: %s force: %v semiSync: %v timeCreatedNS: %d", parentAlias, waitPosition, forceStartReplication, semiSync, timeCreatedNS) if err := tm.lock(ctx); err != nil { return err } @@ -734,6 +721,7 @@ func (tm *TabletManager) setReplicationSourceLocked(ctx context.Context, parentA // GTID-based replication position or a Vitess reparent journal entry, // or both. if shouldbeReplicating { + log.Infof("Set up MySQL replication; should now be replicating from %s at %s", parentAlias, waitPosition) if waitPosition != "" { pos, err := mysql.DecodePosition(waitPosition) if err != nil { diff --git a/go/vt/vttablet/tabletmanager/tm_init.go b/go/vt/vttablet/tabletmanager/tm_init.go index 4e9cdce7e2f..297b1c5687a 100644 --- a/go/vt/vttablet/tabletmanager/tm_init.go +++ b/go/vt/vttablet/tabletmanager/tm_init.go @@ -98,6 +98,7 @@ func registerInitFlags(fs *pflag.FlagSet) { 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.") } @@ -150,11 +151,6 @@ type TabletManager struct { VREngine *vreplication.Engine VDiffEngine *vdiff.Engine - // MetadataManager manages the local metadata tables for a tablet. It - // exists, and is exported, to support swapping a nil pointer in test code, - // in which case metadata creation/population is skipped. - MetadataManager *mysqlctl.MetadataManager - // tmState manages the TabletManager state. tmState *tmState @@ -216,9 +212,9 @@ func BuildTabletFromInput(alias *topodatapb.TabletAlias, port, grpcPort int32, d if err != nil { return nil, err } - log.Infof("Using detected machine hostname: %v, to change this, fix your machine network configuration or override it with --tablet_hostname.", hostname) + log.Infof("Using detected machine hostname: %v, to change this, fix your machine network configuration or override it with --tablet_hostname. Tablet %s", hostname, alias.String()) } else { - log.Infof("Using hostname: %v from --tablet_hostname flag.", hostname) + log.Infof("Using hostname: %v from --tablet_hostname flag. Tablet %s", hostname, alias.String()) } if initKeyspace == "" || initShard == "" { @@ -343,6 +339,10 @@ func mergeTags(a, b map[string]string) map[string]string { // Start starts the TabletManager. func (tm *TabletManager) Start(tablet *topodatapb.Tablet, healthCheckInterval time.Duration) error { + defer func() { + log.Infof("TabletManager Start took ~%d ms", time.Since(servenv.GetInitStartTime()).Milliseconds()) + }() + log.Infof("TabletManager Start") tm.DBConfigs.DBName = topoproto.TabletDbName(tablet) tm.replManager = newReplManager(tm.BatchCtx, tm, healthCheckInterval) tm.tabletAlias = tablet.Alias @@ -408,7 +408,6 @@ func (tm *TabletManager) Start(tablet *topodatapb.Tablet, healthCheckInterval ti // of updating the tablet state and initializing replication. return nil } - // We should be re-read the tablet from tabletManager and use the type specified there. // We shouldn't use the base tablet type directly, since the type could have changed to PRIMARY // earlier in tm.checkPrimaryShip code. @@ -729,7 +728,6 @@ func (tm *TabletManager) initTablet(ctx context.Context) error { } func (tm *TabletManager) handleRestore(ctx context.Context) (bool, error) { - tablet := tm.Tablet() // Sanity check for inconsistent flags if tm.Cnf == nil && restoreFromBackup { return false, fmt.Errorf("you cannot enable --restore_from_backup without a my.cnf file") @@ -762,23 +760,6 @@ func (tm *TabletManager) handleRestore(ctx context.Context) (bool, error) { return true, nil } - // optionally populate metadata records - if initPopulateMetadata { - localMetadata := tm.getLocalMetadataValues(tablet.Type) - if tm.Cnf != nil { // we are managing mysqld - // we'll use batchCtx here because we are still initializing and can't proceed unless this succeeds - if err := tm.MysqlDaemon.Wait(ctx, tm.Cnf); err != nil { - return false, err - } - } - - if tm.MetadataManager != nil { - err := tm.MetadataManager.PopulateMetadataTables(tm.MysqlDaemon, localMetadata, topoproto.TabletDbName(tablet)) - if err != nil { - return false, vterrors.Wrap(err, "failed to --init_populate_metadata") - } - } - } return false, nil } diff --git a/go/vt/vttablet/tabletmanager/tm_init_test.go b/go/vt/vttablet/tabletmanager/tm_init_test.go index 933a9501a55..596ba1884fb 100644 --- a/go/vt/vttablet/tabletmanager/tm_init_test.go +++ b/go/vt/vttablet/tabletmanager/tm_init_test.go @@ -646,19 +646,6 @@ func newTestMysqlDaemon(t *testing.T, port int32) *fakemysqldaemon.FakeMysqlDaem db.AddQueryPattern("BEGIN", &sqltypes.Result{}) db.AddQueryPattern("COMMIT", &sqltypes.Result{}) - db.AddQueryPattern("CREATE DATABASE IF NOT EXISTS _vt", &sqltypes.Result{}) - db.AddQueryPattern("CREATE TABLE IF NOT EXISTS _vt\\.(local|shard)_metadata.*", &sqltypes.Result{}) - - db.AddQueryPattern("ALTER TABLE _vt\\.local_metadata ADD COLUMN (db_name).*", &sqltypes.Result{}) - db.AddQueryPattern("ALTER TABLE _vt\\.local_metadata DROP PRIMARY KEY, ADD PRIMARY KEY\\(name, db_name\\)", &sqltypes.Result{}) - db.AddQueryPattern("ALTER TABLE _vt\\.local_metadata CHANGE value.*", &sqltypes.Result{}) - - db.AddQueryPattern("ALTER TABLE _vt\\.shard_metadata ADD COLUMN (db_name).*", &sqltypes.Result{}) - db.AddQueryPattern("ALTER TABLE _vt\\.shard_metadata DROP PRIMARY KEY, ADD PRIMARY KEY\\(name, db_name\\)", &sqltypes.Result{}) - - db.AddQueryPattern("UPDATE _vt\\.(local|shard)_metadata SET db_name='.+' WHERE db_name=''", &sqltypes.Result{}) - db.AddQueryPattern("INSERT INTO _vt\\.local_metadata \\(.+\\) VALUES \\(.+\\) ON DUPLICATE KEY UPDATE value ?= ?'.+'.*", &sqltypes.Result{}) - mysqld := fakemysqldaemon.NewFakeMysqlDaemon(db) mysqld.MysqlPort = sync2.NewAtomicInt32(port) diff --git a/go/vt/vttablet/tabletmanager/tm_state.go b/go/vt/vttablet/tabletmanager/tm_state.go index da43cbf9a25..9666525d244 100644 --- a/go/vt/vttablet/tabletmanager/tm_state.go +++ b/go/vt/vttablet/tabletmanager/tm_state.go @@ -68,17 +68,16 @@ type tmState struct { // Because mu can be held for long, we publish the current state // of these variables into displayState, which can be accessed // more freely even while tmState is busy transitioning. - mu sync.Mutex - isOpen bool - isOpening bool - isResharding bool - isInSrvKeyspace bool - isShardServing map[topodatapb.TabletType]bool - tabletControls map[topodatapb.TabletType]bool - deniedTables map[topodatapb.TabletType][]string - tablet *topodatapb.Tablet - isPublishing bool - hasCreatedMetadataTables bool + mu sync.Mutex + isOpen bool + isOpening bool + isResharding bool + isInSrvKeyspace bool + isShardServing map[topodatapb.TabletType]bool + tabletControls map[topodatapb.TabletType]bool + deniedTables map[topodatapb.TabletType][]string + tablet *topodatapb.Tablet + isPublishing bool // displayState contains the current snapshot of the internal state // and has its own mutex. @@ -99,6 +98,7 @@ func newTMState(tm *TabletManager, tablet *topodatapb.Tablet) *tmState { } func (ts *tmState) Open() { + log.Infof("In tmState.Open()") ts.mu.Lock() defer ts.mu.Unlock() if ts.isOpen { @@ -113,6 +113,7 @@ func (ts *tmState) Open() { } func (ts *tmState) Close() { + log.Infof("In tmState.Close()") ts.mu.Lock() defer ts.mu.Unlock() @@ -182,7 +183,7 @@ func (ts *tmState) RefreshFromTopoInfo(ctx context.Context, shardInfo *topo.Shar func (ts *tmState) ChangeTabletType(ctx context.Context, tabletType topodatapb.TabletType, action DBAction) error { ts.mu.Lock() defer ts.mu.Unlock() - log.Infof("Changing Tablet Type: %v", tabletType) + log.Infof("Changing Tablet Type: %v for %s", tabletType, ts.tablet.Alias.String()) if tabletType == topodatapb.TabletType_PRIMARY { PrimaryTermStartTime := logutil.TimeToProto(time.Now()) @@ -211,6 +212,7 @@ func (ts *tmState) ChangeTabletType(ctx context.Context, tabletType topodatapb.T return err } } + if action == DBActionSetReadWrite { // We call SetReadOnly only after the topo has been updated to avoid // situations where two tablets are primary at the DB level but not at the vitess level @@ -336,33 +338,6 @@ func (ts *tmState) updateLocked(ctx context.Context) error { return returnErr } -func (ts *tmState) populateLocalMetadataLocked() { - if ts.tm.MetadataManager == nil { - return - } - - if ts.isOpening && !initPopulateMetadata { - return - } - - localMetadata := ts.tm.getLocalMetadataValues(ts.tablet.Type) - dbName := topoproto.TabletDbName(ts.tablet) - - if !ts.hasCreatedMetadataTables { - if err := ts.tm.MetadataManager.PopulateMetadataTables(ts.tm.MysqlDaemon, localMetadata, dbName); err != nil { - log.Errorf("PopulateMetadataTables(%v) failed: %v", localMetadata, err) - return - } - - ts.hasCreatedMetadataTables = true - return - } - - if err := ts.tm.MetadataManager.UpsertLocalMetadata(ts.tm.MysqlDaemon, localMetadata, dbName); err != nil { - log.Errorf("UpsertMetadataTables(%v) failed: %v", localMetadata, err) - } -} - func (ts *tmState) canServe(tabletType topodatapb.TabletType) string { if !topo.IsRunningQueryService(tabletType) { return fmt.Sprintf("not a serving tablet type(%v)", tabletType) diff --git a/go/vt/vttablet/tabletmanager/vdiff/action.go b/go/vt/vttablet/tabletmanager/vdiff/action.go index 2966f2be0b6..7a18015fc24 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/action.go +++ b/go/vt/vttablet/tabletmanager/vdiff/action.go @@ -25,7 +25,6 @@ import ( "vitess.io/vitess/go/vt/topo/topoproto" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/withddl" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/binlog/binlogplayer" @@ -70,10 +69,6 @@ func (vde *Engine) PerformVDiffAction(ctx context.Context, req *tabletmanagerdat } defer dbClient.Close() - vde.vdiffSchemaCreateOnce.Do(func() { - _, _ = withDDL.Exec(ctx, withddl.QueryToTriggerWithDDL, dbClient.ExecuteFetch, dbClient.ExecuteFetch) - }) - action := VDiffAction(req.Action) switch action { case CreateAction, ResumeAction: diff --git a/go/vt/vttablet/tabletmanager/vdiff/engine.go b/go/vt/vttablet/tabletmanager/vdiff/engine.go index 97194c249e9..27747b8a5cb 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/engine.go +++ b/go/vt/vttablet/tabletmanager/vdiff/engine.go @@ -278,7 +278,7 @@ func (vde *Engine) getVDiffsToRun(ctx context.Context) (*sqltypes.Result, error) // We have to use ExecIgnore here so as not to block quick tablet state // transitions from primary to non-primary when starting the engine - qr, err := withDDL.ExecIgnore(ctx, sqlGetVDiffsToRun, dbClient.ExecuteFetch) + qr, err := dbClient.ExecuteFetch(sqlGetVDiffsToRun, -1) if err != nil { return nil, err } @@ -289,7 +289,7 @@ func (vde *Engine) getVDiffsToRun(ctx context.Context) (*sqltypes.Result, error) } func (vde *Engine) getVDiffsToRetry(ctx context.Context, dbClient binlogplayer.DBClient) (*sqltypes.Result, error) { - qr, err := withDDL.ExecIgnore(ctx, sqlGetVDiffsToRetry, dbClient.ExecuteFetch) + qr, err := dbClient.ExecuteFetch(sqlGetVDiffsToRetry, -1) if err != nil { return nil, err } diff --git a/go/vt/vttablet/tabletmanager/vdiff/framework_test.go b/go/vt/vttablet/tabletmanager/vdiff/framework_test.go index a546b866bfd..5889720648b 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vdiff/framework_test.go @@ -44,7 +44,6 @@ import ( "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer/testenv" "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/vttablet/tmclienttest" - "vitess.io/vitess/go/vt/withddl" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" @@ -384,10 +383,6 @@ func (dbc *realDBClient) ExecuteFetch(query string, maxrows int) (*sqltypes.Resu // Use Clone() because the contents of memory region referenced by // string can change when clients (e.g. vcopier) use unsafe string methods. query = strings.Clone(query) - if strings.HasPrefix(query, "use") || - query == withddl.QueryToTriggerWithDDL { // this query breaks unit tests since it errors out - return nil, nil - } qr, err := dbc.conn.ExecuteFetch(query, 10000, true) if doNotLogDBQueries { return qr, err @@ -503,9 +498,7 @@ func newTestVDiffEnv(t *testing.T) *testVDiffEnv { vdiffenv.vse.Open() once.Do(func() { - ddls := binlogplayer.CreateVReplicationTable() - ddls = append(ddls, binlogplayer.AlterVReplicationTable...) - ddls = append(ddls, withDDL.DDLs()...) + var ddls []string // This is needed for the vstreamer engine and the snapshotConn which // use the real DB started by vttestserver diff --git a/go/vt/vttablet/tabletmanager/vdiff/schema.go b/go/vt/vttablet/tabletmanager/vdiff/schema.go index 48dd41dcb50..96040c3fa07 100644 --- a/go/vt/vttablet/tabletmanager/vdiff/schema.go +++ b/go/vt/vttablet/tabletmanager/vdiff/schema.go @@ -16,71 +16,7 @@ limitations under the License. package vdiff -import "vitess.io/vitess/go/vt/withddl" - -var withDDL *withddl.WithDDL - -func init() { - var ddls []string - // Initial VDiff related schema - ddls = append(ddls, sqlCreateSidecarDB, sqlCreateVDiffTable, sqlCreateVDiffTableTable, sqlCreateVDiffLogTable) - // Changes to VDiff related schema over time - ddls = append(ddls, - `ALTER TABLE _vt.vdiff MODIFY COLUMN id bigint AUTO_INCREMENT`, - `ALTER TABLE _vt.vdiff CHANGE started_timestamp started_at timestamp NULL DEFAULT NULL`, - `ALTER TABLE _vt.vdiff CHANGE completed_timestamp completed_at timestamp NULL DEFAULT NULL`, - `ALTER TABLE _vt.vdiff MODIFY COLUMN state varbinary(64)`, - `ALTER TABLE _vt.vdiff MODIFY COLUMN keyspace varbinary(256)`, - `ALTER TABLE _vt.vdiff ADD COLUMN last_error varbinary(512)`, - `ALTER TABLE _vt.vdiff ADD INDEX (state)`, - `ALTER TABLE _vt.vdiff ADD INDEX ks_wf_idx (keyspace(64), workflow(64))`, - `ALTER TABLE _vt.vdiff_table MODIFY COLUMN table_name varbinary(128)`, - `ALTER TABLE _vt.vdiff_table MODIFY COLUMN state varbinary(64)`, - `ALTER TABLE _vt.vdiff_table MODIFY COLUMN lastpk varbinary(2000)`, - `ALTER TABLE _vt.vdiff_table MODIFY COLUMN table_rows bigint not null default 0`, - `ALTER TABLE _vt.vdiff_table MODIFY COLUMN rows_compared bigint not null default 0`, - ) - withDDL = withddl.New(ddls) -} - const ( - sqlCreateSidecarDB = "CREATE DATABASE IF NOT EXISTS _vt" - sqlCreateVDiffTable = `CREATE TABLE IF NOT EXISTS _vt.vdiff ( - id int AUTO_INCREMENT, - vdiff_uuid varchar(64) NOT NULL, - workflow varbinary(1024), - keyspace varbinary(1024), - shard varchar(255) NOT NULL, - db_name varbinary(1024), - state varbinary(1024), - options json, - created_at timestamp DEFAULT CURRENT_TIMESTAMP, - started_timestamp timestamp NULL DEFAULT NULL, - liveness_timestamp timestamp NULL DEFAULT NULL, - completed_timestamp timestamp NULL DEFAULT NULL, - unique key uuid_idx (vdiff_uuid), - primary key (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4` - - sqlCreateVDiffTableTable = `CREATE TABLE IF NOT EXISTS _vt.vdiff_table( - vdiff_id varchar(64) NOT NULL, - table_name varbinary(1024), - state varbinary(128), - lastpk varbinary(1024), - table_rows int not null default 0, - rows_compared int not null default 0, - mismatch bool not null default false, - report json, - created_at timestamp DEFAULT CURRENT_TIMESTAMP, - updated_at timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - primary key (vdiff_id, table_name)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4` - - sqlCreateVDiffLogTable = `CREATE TABLE IF NOT EXISTS _vt.vdiff_log ( - id int AUTO_INCREMENT, - vdiff_id int not null, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - message text NOT NULL, - primary key (id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4` - sqlNewVDiff = "insert into _vt.vdiff(keyspace, workflow, state, options, shard, db_name, vdiff_uuid) values(%s, %s, '%s', %s, '%s', '%s', '%s')" sqlResumeVDiff = `update _vt.vdiff as vd, _vt.vdiff_table as vdt set vd.options = %s, vd.started_at = NULL, vd.completed_at = NULL, vd.state = 'pending', vdt.state = 'pending' where vd.vdiff_uuid = %s and vd.id = vdt.vdiff_id and vd.state in ('completed', 'stopped') diff --git a/go/vt/vttablet/tabletmanager/vreplication/controller.go b/go/vt/vttablet/tabletmanager/vreplication/controller.go index 29caca5c3e3..28ba823154d 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/controller.go +++ b/go/vt/vttablet/tabletmanager/vreplication/controller.go @@ -203,11 +203,6 @@ func (ct *controller) runBlp(ctx context.Context) (err error) { if err := dbClient.Connect(); err != nil { return vterrors.Wrap(err, "can't connect to database") } - for _, query := range withDDLInitialQueries { - if _, err := withDDL.Exec(ctx, query, dbClient.ExecuteFetch, dbClient.ExecuteFetch); err != nil { - log.Errorf("cannot apply withDDL init query '%s': %v", query, err) - } - } defer dbClient.Close() var tablet *topodatapb.Tablet diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine.go b/go/vt/vttablet/tabletmanager/vreplication/engine.go index 58799131eb6..69872af237b 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine.go @@ -42,7 +42,6 @@ import ( "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/throttle" - "vitess.io/vitess/go/vt/withddl" ) const ( @@ -51,32 +50,7 @@ const ( copyStateTableName = "_vt.copy_state" postCopyActionTableName = "_vt.post_copy_action" - createReshardingJournalTable = `create table if not exists _vt.resharding_journal( - id bigint, - db_name varbinary(255), - val blob, - primary key (id) -)` - - createCopyState = `create table if not exists _vt.copy_state ( - vrepl_id int, - table_name varbinary(128), - lastpk varbinary(2000), - primary key (vrepl_id, table_name))` - - alterCopyState = `alter table _vt.copy_state - add column id bigint unsigned not null auto_increment first, - drop primary key, add primary key(id), - add key (vrepl_id, table_name)` - - createPostCopyAction = `create table if not exists _vt.post_copy_action( - id bigint not null auto_increment, - vrepl_id int, - table_name varbinary(128), - action JSON, - unique key (vrepl_id, table_name), - primary key(id))` - + maxRows = 10000 throttlerVReplicationAppName = "vreplication" throttlerOnlineDDLAppName = "online-ddl" ) @@ -86,21 +60,6 @@ const ( PostCopyActionSQL ) -var withDDL *withddl.WithDDL -var withDDLInitialQueries []string - -func init() { - allddls := append([]string{}, binlogplayer.CreateVReplicationTable()...) - allddls = append(allddls, binlogplayer.AlterVReplicationTable...) - allddls = append(allddls, createReshardingJournalTable, createCopyState) - allddls = append(allddls, createVReplicationLogTable) - allddls = append(allddls, alterCopyState) - allddls = append(allddls, createPostCopyAction) - withDDL = withddl.New(allddls) - - withDDLInitialQueries = append(withDDLInitialQueries, binlogplayer.WithDDLInitialQueries...) -} - // waitRetryTime can be changed to a smaller value for tests. // A VReplication stream can be created by sending an insert statement // to the Engine. Such a stream can also be updated or deleted. The fields @@ -415,13 +374,13 @@ func (vre *Engine) exec(query string, runAsAdmin bool) (*sqltypes.Result, error) // Change the database to ensure that these events don't get // replicated by another vreplication. This can happen when // we reverse replication. - if _, err := withDDL.Exec(vre.ctx, "use _vt", dbClient.ExecuteFetch, dbClient.ExecuteFetch); err != nil { + if _, err := dbClient.ExecuteFetch("use _vt", 1); err != nil { return nil, err } switch plan.opcode { case insertQuery: - qr, err := withDDL.Exec(vre.ctx, plan.query, dbClient.ExecuteFetch, dbClient.ExecuteFetch) + qr, err := dbClient.ExecuteFetch(plan.query, 1) if err != nil { return nil, err } @@ -465,11 +424,11 @@ func (vre *Engine) exec(query string, runAsAdmin bool) (*sqltypes.Result, error) blpStats[id] = ct.blpStats } } - query, err := plan.applier.GenerateQuery(bv, nil) + query, err = plan.applier.GenerateQuery(bv, nil) if err != nil { return nil, err } - qr, err := withDDL.Exec(vre.ctx, query, dbClient.ExecuteFetch, dbClient.ExecuteFetch) + qr, err := dbClient.ExecuteFetch(query, maxRows) if err != nil { return nil, err } @@ -517,7 +476,7 @@ func (vre *Engine) exec(query string, runAsAdmin bool) (*sqltypes.Result, error) if err != nil { return nil, err } - qr, err := withDDL.Exec(vre.ctx, query, dbClient.ExecuteFetch, dbClient.ExecuteFetch) + qr, err := dbClient.ExecuteFetch(query, maxRows) if err != nil { return nil, err } @@ -525,14 +484,16 @@ func (vre *Engine) exec(query string, runAsAdmin bool) (*sqltypes.Result, error) if err != nil { return nil, err } - if _, err := withDDL.ExecIgnore(vre.ctx, delQuery, dbClient.ExecuteFetch); err != nil { + _, err = dbClient.ExecuteFetch(delQuery, maxRows) + if err != nil { return nil, err } delQuery, err = plan.delPostCopyAction.GenerateQuery(bv, nil) if err != nil { return nil, err } - if _, err := withDDL.ExecIgnore(vre.ctx, delQuery, dbClient.ExecuteFetch); err != nil { + _, err = dbClient.ExecuteFetch(delQuery, maxRows) + if err != nil { return nil, err } if err := dbClient.Commit(); err != nil { @@ -541,7 +502,7 @@ func (vre *Engine) exec(query string, runAsAdmin bool) (*sqltypes.Result, error) return qr, nil case selectQuery, reshardingJournalQuery: // select and resharding journal queries are passed through. - return withDDL.Exec(vre.ctx, plan.query, dbClient.ExecuteFetch, dbClient.ExecuteFetch) + return dbClient.ExecuteFetch(plan.query, maxRows) } panic("unreachable") } @@ -704,7 +665,7 @@ func (vre *Engine) transitionJournal(je *journalEvent) { deferSecondaryKeys, _ := strconv.ParseBool(params["defer_secondary_keys"]) ig := NewInsertGenerator(binlogplayer.BlpRunning, vre.dbName) ig.AddRow(params["workflow"], bls, sgtid.Gtid, params["cell"], params["tablet_types"], workflowType, workflowSubType, deferSecondaryKeys) - qr, err := withDDL.Exec(vre.ctx, ig.String(), dbClient.ExecuteFetch, dbClient.ExecuteFetch) + qr, err := dbClient.ExecuteFetch(ig.String(), maxRows) if err != nil { log.Errorf("transitionJournal: %v", err) return @@ -714,7 +675,7 @@ func (vre *Engine) transitionJournal(je *journalEvent) { } for _, ks := range participants { id := je.participants[ks] - _, err := withDDL.Exec(vre.ctx, binlogplayer.DeleteVReplication(uint32(id)), dbClient.ExecuteFetch, dbClient.ExecuteFetch) + _, err := dbClient.ExecuteFetch(binlogplayer.DeleteVReplication(uint32(id)), maxRows) if err != nil { log.Errorf("transitionJournal: %v", err) return @@ -858,7 +819,7 @@ func (vre *Engine) readAllRows(ctx context.Context) ([]map[string]string, error) return nil, err } defer dbClient.Close() - qr, err := withDDL.ExecIgnore(ctx, fmt.Sprintf("select * from _vt.vreplication where db_name=%v", encodeString(vre.dbName)), dbClient.ExecuteFetch) + qr, err := dbClient.ExecuteFetch(fmt.Sprintf("select * from _vt.vreplication where db_name=%v", encodeString(vre.dbName)), maxRows) if err != nil { return nil, err } diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine_test.go b/go/vt/vttablet/tabletmanager/vreplication/engine_test.go index 6093a5cacef..b560761273b 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine_test.go @@ -28,7 +28,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/sync2" "vitess.io/vitess/go/vt/binlog/binlogplayer" @@ -469,106 +468,6 @@ func TestWaitForPosCancel(t *testing.T) { } } -func TestCreateDBAndTable(t *testing.T) { - defer func() { globalStats = &vrStats{} }() - - defer deleteTablet(addTablet(100)) - resetBinlogClient() - dbClient := binlogplayer.NewMockDBClient(t) - dbClientFactory := func() binlogplayer.DBClient { return dbClient } - mysqld := &fakemysqldaemon.FakeMysqlDaemon{MysqlPort: sync2.NewAtomicInt32(3306)} - - // Test Insert - - vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClientFactory, dbClient.DBName(), nil) - - tableNotFound := mysql.SQLError{Num: 1146, Message: "table not found"} - dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", nil, &tableNotFound) - vre.Open(context.Background()) - defer vre.Close() - - // Missing db. Statement should get retried after creating everything. - dbNotFound := mysql.SQLError{Num: 1049, Message: "db not found"} - dbClient.ExpectRequest("use _vt", &sqltypes.Result{}, &dbNotFound) - - expectDDLs := func() { - t.Helper() - ddls := []string{ - "CREATE DATABASE IF NOT EXISTS _vt", - "DROP TABLE IF EXISTS _vt.blp_checkpoint", - } - for _, ddl := range ddls { - dbClient.ExpectRequest(ddl, &sqltypes.Result{}, nil) - } - - ddls = []string{ - "CREATE TABLE IF NOT EXISTS _vt.vreplication.*", - "ALTER TABLE _vt.vreplication ADD COLUMN db_name.*", - "ALTER TABLE _vt.vreplication MODIFY source.*", - "ALTER TABLE _vt.vreplication ADD KEY.*", - "ALTER TABLE _vt.vreplication ADD COLUMN rows_copied.*", - "ALTER TABLE _vt.vreplication ADD COLUMN tags.*", - "ALTER TABLE _vt.vreplication ADD COLUMN time_heartbeat.*", - "ALTER TABLE _vt.vreplication ADD COLUMN workflow_type int NOT NULL DEFAULT 0", - "ALTER TABLE _vt.vreplication ADD COLUMN time_throttled.*", - "ALTER TABLE _vt.vreplication ADD COLUMN component_throttled.*", - "ALTER TABLE _vt.vreplication ADD COLUMN workflow_sub_type int NOT NULL DEFAULT 0", - "ALTER TABLE _vt.vreplication ADD COLUMN defer_secondary_keys bool NOT NULL DEFAULT false", - "create table if not exists _vt.resharding_journal.*", - "create table if not exists _vt.copy_state.*", - "alter table _vt.copy_state.*", - "create table if not exists _vt.post_copy_action.*", - } - for _, ddl := range ddls { - dbClient.ExpectRequestRE(ddl, &sqltypes.Result{}, nil) - } - } - expectDDLs() - dbClient.ExpectRequest("use _vt", &sqltypes.Result{}, nil) - - // Non-recoverable error. - unrecoverableError := &mysql.SQLError{Num: 1234, Message: "random error"} - dbClient.ExpectRequest("select fail_query from _vt.vreplication", &sqltypes.Result{}, unrecoverableError) - - // Missing table. Statement should get retried after creating everything. - dbClient.ExpectRequest("use _vt", &sqltypes.Result{}, nil) - dbClient.ExpectRequest("insert into _vt.vreplication values(null)", &sqltypes.Result{}, &tableNotFound) - expectDDLs() - dbClient.ExpectRequest("insert into _vt.vreplication values(null)", &sqltypes.Result{InsertID: 1}, nil) - - // The rest of this test is normal with no db errors or extra queries. - - dbClient.ExpectRequest("select * from _vt.vreplication where id = 1", sqltypes.MakeTestResult( - sqltypes.MakeTestFields( - "id|state|source", - "int64|varchar|varchar", - ), - fmt.Sprintf(`1|Running|keyspace:"%s" shard:"0" key_range:{end:"\x80"}`, env.KeyspaceName), - ), nil) - dbClient.ExpectRequestRE("update _vt.vreplication set message='Picked source tablet.*", testDMLResponse, nil) - dbClient.ExpectRequest("update _vt.vreplication set state='Running', message='' where id=1", testDMLResponse, nil) - dbClient.ExpectRequest("select pos, stop_pos, max_tps, max_replication_lag, state, workflow_type, workflow, workflow_sub_type, defer_secondary_keys from _vt.vreplication where id=1", testSettingsResponse, nil) - dbClient.ExpectRequest("begin", nil, nil) - dbClient.ExpectRequest("insert into t values(1)", testDMLResponse, nil) - dbClient.ExpectRequestRE("update _vt.vreplication set pos='MariaDB/0-1-1235', time_updated=.*", testDMLResponse, nil) - dbClient.ExpectRequest("commit", nil, nil) - - _, err := vre.Exec("select fail_query from _vt.vreplication") - if err != unrecoverableError { - t.Errorf("Want: %v, Got: %v", unrecoverableError, err) - } - - qr, err := vre.Exec("insert into _vt.vreplication values(null)") - if err != nil { - t.Fatal(err) - } - wantqr := &sqltypes.Result{InsertID: 1} - if !qr.Equal(wantqr) { - t.Errorf("Exec: %v, want %v", qr, wantqr) - } - dbClient.Wait() -} - func TestGetDBClient(t *testing.T) { dbClientDba := binlogplayer.NewMockDbaClient(t) dbClientFiltered := binlogplayer.NewMockDBClient(t) diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index d56457a8f51..cdb07485427 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -39,6 +39,7 @@ import ( "vitess.io/vitess/go/vt/grpcclient" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/servenv" + "vitess.io/vitess/go/vt/sidecardb" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/vttablet/queryservice" "vitess.io/vitess/go/vt/vttablet/queryservice/fakes" @@ -47,7 +48,6 @@ import ( qh "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication/queryhistory" "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer" "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer/testenv" - "vitess.io/vitess/go/vt/withddl" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" @@ -148,34 +148,6 @@ func TestMain(m *testing.M) { playerEngine = NewTestEngine(env.TopoServ, env.Cells[0], env.Mysqld, realDBClientFactory, realDBClientFactory, vrepldb, externalConfig) playerEngine.Open(context.Background()) defer playerEngine.Close() - if err := env.Mysqld.ExecuteSuperQueryList(context.Background(), binlogplayer.CreateVReplicationTable()); err != nil { - fmt.Fprintf(os.Stderr, "%v", err) - return 1 - } - - for _, query := range binlogplayer.AlterVReplicationTable { - env.Mysqld.ExecuteSuperQuery(context.Background(), query) - } - - if err := env.Mysqld.ExecuteSuperQuery(context.Background(), createCopyState); err != nil { - fmt.Fprintf(os.Stderr, "%v", err) - return 1 - } - - if err := env.Mysqld.ExecuteSuperQuery(context.Background(), alterCopyState); err != nil { - fmt.Fprintf(os.Stderr, "%v", err) - return 1 - } - - if err := env.Mysqld.ExecuteSuperQuery(context.Background(), createPostCopyAction); err != nil { - fmt.Fprintf(os.Stderr, "%v", err) - return 1 - } - - if err := env.Mysqld.ExecuteSuperQuery(context.Background(), createVReplicationLogTable); err != nil { - fmt.Fprintf(os.Stderr, "%v", err) - return 1 - } return m.Run() }() @@ -448,10 +420,6 @@ func (dbc *realDBClient) ExecuteFetch(query string, maxrows int) (*sqltypes.Resu // Use Clone() because the contents of memory region referenced by // string can change when clients (e.g. vcopier) use unsafe string methods. query = strings.Clone(query) - if strings.HasPrefix(query, "use") || - query == withddl.QueryToTriggerWithDDL { // this query breaks unit tests since it errors out - return nil, nil - } qr, err := dbc.conn.ExecuteFetch(query, 10000, true) if doNotLogDBQueries { return qr, err @@ -521,6 +489,9 @@ func shouldIgnoreQuery(query string) bool { ", component_throttled=", // update of last throttle time, can happen out-of-band, so can't test for it "context cancel", } + if sidecardb.MatchesInitQuery(query) { + return true + } for _, q := range queriesToIgnore { if strings.Contains(query, q) { return true @@ -530,9 +501,6 @@ func shouldIgnoreQuery(query string) bool { } func expectDBClientQueries(t *testing.T, expectations qh.ExpectationSequence, skippableOnce ...string) { - extraQueries := withDDL.DDLs() - extraQueries = append(extraQueries, withDDLInitialQueries...) - // Either 'queries' or 'queriesWithDDLs' must match globalDBQueries t.Helper() failed := false skippedOnce := false @@ -552,11 +520,6 @@ func expectDBClientQueries(t *testing.T, expectations qh.ExpectationSequence, sk if shouldIgnoreQuery(got) { goto retry } - for _, extraQuery := range extraQueries { - if got == extraQuery { - goto retry - } - } result := validator.AcceptQuery(got) @@ -602,8 +565,6 @@ func expectNontxQueries(t *testing.T, expectations qh.ExpectationSequence) { failed := false - skipQueries := withDDLInitialQueries - skipQueries = append(skipQueries, withDDL.DDLs()...) validator := qh.NewVerifier(expectations) for len(validator.Pending()) > 0 { @@ -618,11 +579,6 @@ func expectNontxQueries(t *testing.T, expectations qh.ExpectationSequence) { if got == "begin" || got == "commit" || got == "rollback" || strings.Contains(got, "update _vt.vreplication set pos") || shouldIgnoreQuery(got) { goto retry } - for _, skipQuery := range skipQueries { - if got == skipQuery { - goto retry - } - } result := validator.AcceptQuery(got) diff --git a/go/vt/vttablet/tabletmanager/vreplication/journal_test.go b/go/vt/vttablet/tabletmanager/vreplication/journal_test.go index fa1c68e2888..9dfdee766d1 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/journal_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/journal_test.go @@ -68,11 +68,10 @@ func TestJournalOneToOne(t *testing.T) { }}, } query := fmt.Sprintf("insert into _vt.resharding_journal(id, db_name, val) values (1, 'vttest', %v)", encodeString(journal.String())) - execStatements(t, []string{createReshardingJournalTable, query}) + execStatements(t, []string{query}) defer execStatements(t, []string{"delete from _vt.resharding_journal"}) expectDBClientQueries(t, qh.Expect( - "/update _vt.vreplication set pos=", "begin", `/insert into _vt.vreplication.*workflow, source, pos.*values.*'test', 'keyspace:\\"other_keyspace\\" shard:\\"0\\.*'MySQL56/7b04699f-f5e9-11e9-bf88-9cb6d089e1c3:1-10'`, fmt.Sprintf("delete from _vt.vreplication where id=%d", firstID), @@ -135,11 +134,10 @@ func TestJournalOneToMany(t *testing.T) { }}, } query := fmt.Sprintf("insert into _vt.resharding_journal(id, db_name, val) values (1, 'vttest', %v)", encodeString(journal.String())) - execStatements(t, []string{createReshardingJournalTable, query}) + execStatements(t, []string{query}) defer execStatements(t, []string{"delete from _vt.resharding_journal"}) expectDBClientQueries(t, qh.Expect( - "/update _vt.vreplication set pos=", "begin", `/insert into _vt.vreplication.*workflow, source, pos.*values.*'test', 'keyspace:\\"other_keyspace\\" shard:\\"-80\\.*'MySQL56/7b04699f-f5e9-11e9-bf88-9cb6d089e1c3:1-5'`, `/insert into _vt.vreplication.*workflow, source, pos.*values.*'test', 'keyspace:\\"other_keyspace\\" shard:\\"80-\\.*'MySQL56/7b04699f-f5e9-11e9-bf88-9cb6d089e1c3:5-10'`, @@ -200,11 +198,10 @@ func TestJournalTablePresent(t *testing.T) { }}, } query := fmt.Sprintf("insert into _vt.resharding_journal(id, db_name, val) values (1, 'vttest', %v)", encodeString(journal.String())) - execStatements(t, []string{createReshardingJournalTable, query}) + execStatements(t, []string{query}) defer execStatements(t, []string{"delete from _vt.resharding_journal"}) expectDBClientQueries(t, qh.Expect( - "/update _vt.vreplication set pos=", "begin", `/insert into _vt.vreplication.*workflow, source, pos.*values.*'test', 'keyspace:\\"other_keyspace\\" shard:\\"0\\.*'MySQL56/7b04699f-f5e9-11e9-bf88-9cb6d089e1c3:1-10'`, fmt.Sprintf("delete from _vt.vreplication where id=%d", firstID), @@ -263,14 +260,9 @@ func TestJournalTableNotPresent(t *testing.T) { }}, } query := fmt.Sprintf("insert into _vt.resharding_journal(id, db_name, val) values (1, 'vttest', %v)", encodeString(journal.String())) - execStatements(t, []string{createReshardingJournalTable, query}) + execStatements(t, []string{query}) defer execStatements(t, []string{"delete from _vt.resharding_journal"}) - // Wait for a heartbeat based update to confirm that the existing vreplication was not transitioned. - expectDBClientQueries(t, qh.Expect( - "/update _vt.vreplication set pos=", - )) - // Delete all vreplication streams. There should be only one, but we don't know its id. if _, err := playerEngine.Exec("delete from _vt.vreplication"); err != nil { t.Fatal(err) @@ -326,11 +318,10 @@ func TestJournalTableMixed(t *testing.T) { }}, } query := fmt.Sprintf("insert into _vt.resharding_journal(id, db_name, val) values (1, 'vttest', %v)", encodeString(journal.String())) - execStatements(t, []string{createReshardingJournalTable, query}) + execStatements(t, []string{query}) defer execStatements(t, []string{"delete from _vt.resharding_journal"}) expectDBClientQueries(t, qh.Expect( - "/update _vt.vreplication set pos=", "/update _vt.vreplication set state='Stopped', message='unable to handle journal event: tables were partially matched' where id", )) diff --git a/go/vt/vttablet/tabletmanager/vreplication/queryhistory/verifier.go b/go/vt/vttablet/tabletmanager/vreplication/queryhistory/verifier.go index 638853eec1f..a7015b0daf5 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/queryhistory/verifier.go +++ b/go/vt/vttablet/tabletmanager/vreplication/queryhistory/verifier.go @@ -115,8 +115,8 @@ func (v *Verifier) checkQueryAgainstExpectation(query string, expectation Sequen if expectation.ImmediatelyAfter() != nil { if len(v.matched) == 0 { result.Message = fmt.Sprintf( - "expected immediately after %q, but it is first", - expectation.ImmediatelyAfter().Query(), + "%q expected immediately after %q, but it is first", + query, expectation.ImmediatelyAfter().Query(), ) return false } diff --git a/go/vt/vttablet/tabletmanager/vreplication/utils.go b/go/vt/vttablet/tabletmanager/vreplication/utils.go index 0eaa6ea8b50..d43e98c6f97 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/utils.go +++ b/go/vt/vttablet/tabletmanager/vreplication/utils.go @@ -17,7 +17,6 @@ limitations under the License. package vreplication import ( - "context" "encoding/json" "fmt" "strconv" @@ -32,17 +31,7 @@ import ( ) const ( - vreplicationLogTableName = "_vt.vreplication_log" - createVReplicationLogTable = `CREATE TABLE IF NOT EXISTS _vt.vreplication_log ( - id BIGINT(20) AUTO_INCREMENT, - vrepl_id INT NOT NULL, - type VARBINARY(256) NOT NULL, - state VARBINARY(100) NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - message text NOT NULL, - count BIGINT(20) NOT NULL DEFAULT 1, - PRIMARY KEY (id))` + vreplicationLogTableName = "_vt.vreplication_log" ) const ( @@ -77,7 +66,7 @@ const ( func getLastLog(dbClient *vdbClient, vreplID uint32) (id int64, typ, state, message string, err error) { var qr *sqltypes.Result query := fmt.Sprintf("select id, type, state, message from _vt.vreplication_log where vrepl_id = %d order by id desc limit 1", vreplID) - if qr, err = withDDL.Exec(context.Background(), query, dbClient.ExecuteFetch, dbClient.ExecuteFetch); err != nil { + if qr, err = dbClient.Execute(query); err != nil { return 0, "", "", "", err } if len(qr.Rows) != 1 { @@ -111,7 +100,7 @@ func insertLog(dbClient *vdbClient, typ string, vreplID uint32, state, message s strconv.Itoa(int(vreplID)), encodeString(typ), encodeString(state), encodeString(message)) query = buf.ParsedQuery().Query } - if _, err = withDDL.Exec(context.Background(), query, dbClient.ExecuteFetch, dbClient.ExecuteFetch); err != nil { + if _, err = dbClient.ExecuteFetch(query, 10000); err != nil { return fmt.Errorf("could not insert into log table: %v: %v", query, err) } return nil diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer_flaky_test.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer_flaky_test.go index b63f99e36d2..f508c632c9f 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer_flaky_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer_flaky_test.go @@ -141,7 +141,6 @@ func TestHeartbeatFrequencyFlag(t *testing.T) { func TestVReplicationTimeUpdated(t *testing.T) { ctx := context.Background() defer deleteTablet(addTablet(100)) - execStatements(t, []string{ "create table t1(id int, val varbinary(128), primary key(id))", fmt.Sprintf("create table %s.t1(id int, val varbinary(128), primary key(id))", vrepldb), @@ -192,12 +191,6 @@ func TestVReplicationTimeUpdated(t *testing.T) { require.Greater(t, timeUpdated2, timeUpdated1, "time_updated not updated") require.Greater(t, timeUpdated2, transactionTimestamp1, "transaction_timestamp should not be < time_updated") require.Greater(t, timeHeartbeat2, timeHeartbeat1, "time_heartbeat not updated") - - // drop time_heartbeat column to test that heartbeat is updated using WithDDL and can self-heal by creating the column again - env.Mysqld.ExecuteSuperQuery(ctx, "alter table _vt.vreplication drop column time_heartbeat") - time.Sleep(2 * time.Second) - _, _, timeHeartbeat3 := getTimestamps() - require.Greater(t, timeHeartbeat3, timeHeartbeat2, "time_heartbeat not updated") } func TestCharPK(t *testing.T) { diff --git a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go index 1f7c7486752..09944f888f6 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go @@ -388,7 +388,7 @@ func (vr *vreplicator) readSettings(ctx context.Context, dbClient *vdbClient) (s } query := fmt.Sprintf("select count(distinct table_name) from _vt.copy_state where vrepl_id=%d", vr.id) - qr, err := withDDL.Exec(ctx, query, vr.dbClient.ExecuteFetch, vr.dbClient.ExecuteFetch) + qr, err := vr.dbClient.ExecuteFetch(query, maxRows) if err != nil { return settings, numTablesToCopy, err } @@ -537,7 +537,7 @@ func (vr *vreplicator) updateTimeThrottled(componentThrottled ComponentName) err if err != nil { return err } - if _, err := withDDL.Exec(vr.vre.ctx, update, vr.dbClient.ExecuteFetch, vr.dbClient.ExecuteFetch); err != nil { + if _, err := vr.dbClient.ExecuteFetch(update, maxRows); err != nil { return fmt.Errorf("error %v updating time throttled", err) } return nil @@ -550,7 +550,7 @@ func (vr *vreplicator) updateHeartbeatTime(tm int64) error { if err != nil { return err } - if _, err := withDDL.Exec(vr.vre.ctx, update, vr.dbClient.ExecuteFetch, vr.dbClient.ExecuteFetch); err != nil { + if _, err := vr.dbClient.ExecuteFetch(update, maxRows); err != nil { return fmt.Errorf("error %v updating time", err) } return nil diff --git a/go/vt/vttablet/tabletserver/health_streamer.go b/go/vt/vttablet/tabletserver/health_streamer.go index 1475a2ade32..436942beaca 100644 --- a/go/vt/vttablet/tabletserver/health_streamer.go +++ b/go/vt/vttablet/tabletserver/health_streamer.go @@ -87,7 +87,6 @@ type healthStreamer struct { ticks *timer.Timer dbConfig dbconfigs.Connector conns *connpool.Pool - initSuccess bool signalWhenSchemaChange bool views map[string]string @@ -339,13 +338,6 @@ func (hs *healthStreamer) reload() error { } defer conn.Recycle() - if !hs.initSuccess { - hs.initSuccess, err = hs.InitSchemaLocked(conn) - if err != nil { - return err - } - } - tables, err := getChangedTableNames(ctx, conn) if err != nil { return err @@ -371,17 +363,6 @@ func (hs *healthStreamer) reload() error { return nil } -func (hs *healthStreamer) InitSchemaLocked(conn *connpool.DBConn) (bool, error) { - for _, query := range mysql.VTDatabaseInit { - _, err := conn.Exec(hs.ctx, query, 1, false) - if err != nil { - return false, err - } - } - - return true, nil -} - func getChangedTableNames(ctx context.Context, conn *connpool.DBConn) ([]string, error) { var tables []string var tableNames []string diff --git a/go/vt/vttablet/tabletserver/health_streamer_test.go b/go/vt/vttablet/tabletserver/health_streamer_test.go index 60d1cda4620..c1a36f938e0 100644 --- a/go/vt/vttablet/tabletserver/health_streamer_test.go +++ b/go/vt/vttablet/tabletserver/health_streamer_test.go @@ -175,9 +175,6 @@ func TestReloadSchema(t *testing.T) { target := &querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} configs := config.DB - db.AddQuery(mysql.CreateVTDatabase, &sqltypes.Result{}) - db.AddQuery(mysql.CreateSchemaCopyTable, &sqltypes.Result{}) - db.AddQuery(mysql.CreateViewsTable, &sqltypes.Result{}) db.AddQueryPattern(mysql.ClearSchemaCopy+".*", &sqltypes.Result{}) db.AddQueryPattern(mysql.InsertIntoSchemaCopy+".*", &sqltypes.Result{}) db.AddQuery("begin", &sqltypes.Result{}) @@ -290,9 +287,6 @@ func TestInitialReloadSchema(t *testing.T) { target := &querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} configs := config.DB - db.AddQuery(mysql.CreateVTDatabase, &sqltypes.Result{}) - db.AddQuery(mysql.CreateSchemaCopyTable, &sqltypes.Result{}) - db.AddQuery(mysql.CreateViewsTable, &sqltypes.Result{}) db.AddQueryPattern(mysql.ClearSchemaCopy+".*", &sqltypes.Result{}) db.AddQueryPattern(mysql.InsertIntoSchemaCopy+".*", &sqltypes.Result{}) db.AddQuery("begin", &sqltypes.Result{}) @@ -350,9 +344,6 @@ func TestReloadView(t *testing.T) { target := &querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} configs := config.DB - db.AddQuery(mysql.CreateVTDatabase, &sqltypes.Result{}) - db.AddQuery(mysql.CreateSchemaCopyTable, &sqltypes.Result{}) - db.AddQuery(mysql.CreateViewsTable, &sqltypes.Result{}) db.AddQuery(mysql.DetectSchemaChange, &sqltypes.Result{}) db.AddQuery(mysql.SelectAllViews, &sqltypes.Result{}) diff --git a/go/vt/vttablet/tabletserver/output.txt b/go/vt/vttablet/tabletserver/output.txt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/go/vt/vttablet/tabletserver/query_executor.go b/go/vt/vttablet/tabletserver/query_executor.go index f0266c068b0..79af1d4a88f 100644 --- a/go/vt/vttablet/tabletserver/query_executor.go +++ b/go/vt/vttablet/tabletserver/query_executor.go @@ -26,8 +26,6 @@ import ( "google.golang.org/protobuf/proto" - "vitess.io/vitess/go/vt/withddl" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/pools" @@ -83,9 +81,6 @@ var ( Type: sqltypes.Int64, }, } - withDDL = withddl.New([]string{ - mysql.CreateViewsTable, - }) ) func returnStreamResult(result *sqltypes.Result) error { @@ -390,7 +385,7 @@ func (qre *QueryExecutor) execDropViewDDL(conn *StatefulConnection, stmt *sqlpar } func execWithDDLView(ctx context.Context, conn *StatefulConnection, sql string) (*sqltypes.Result, error) { - return withDDL.Exec(ctx, sql, conn.Exec, conn.Exec) + return conn.Exec(ctx, sql, 10000, true) } func (qre *QueryExecutor) checkViewExists(conn *StatefulConnection, stmt *sqlparser.DropView, bindVars map[string]*querypb.BindVariable, viewsMap map[string]int, viewNames []string) error { @@ -1036,11 +1031,6 @@ func (qre *QueryExecutor) execAlterMigration() (*sqltypes.Result, error) { return nil, vterrors.New(vtrpcpb.Code_INTERNAL, "Expecting ALTER VITESS_MIGRATION plan") } - // Make sure schema exists - if err := qre.tsv.onlineDDLExecutor.PrepareForQueryExecutor(qre.ctx); err != nil { - return nil, err - } - switch alterMigration.Type { case sqlparser.RetryMigrationType: return qre.tsv.onlineDDLExecutor.RetryMigration(qre.ctx, alterMigration.UUID) diff --git a/go/vt/vttablet/tabletserver/query_executor_test.go b/go/vt/vttablet/tabletserver/query_executor_test.go index 59ed812996a..ca8fc7ebf9d 100644 --- a/go/vt/vttablet/tabletserver/query_executor_test.go +++ b/go/vt/vttablet/tabletserver/query_executor_test.go @@ -24,6 +24,8 @@ import ( "strings" "testing" + "vitess.io/vitess/go/vt/sidecardb" + "vitess.io/vitess/go/vt/vttablet/tabletserver/tx" "github.com/stretchr/testify/assert" @@ -1548,6 +1550,7 @@ func initQueryExecutorTestDB(db *fakesqldb.DB) { "varchar|int64"), "Innodb_rows_read|0", )) + sidecardb.AddSchemaInitQueries(db, true) } func getTestTableFields() []*querypb.Field { @@ -1560,16 +1563,6 @@ func getTestTableFields() []*querypb.Field { func addQueryExecutorSupportedQueries(db *fakesqldb.DB) { queryResultMap := map[string]*sqltypes.Result{ - // queries for twopc - fmt.Sprintf(sqlCreateSidecarDB, "_vt"): {}, - fmt.Sprintf(sqlDropLegacy1, "_vt"): {}, - fmt.Sprintf(sqlDropLegacy2, "_vt"): {}, - fmt.Sprintf(sqlDropLegacy3, "_vt"): {}, - fmt.Sprintf(sqlDropLegacy4, "_vt"): {}, - fmt.Sprintf(sqlCreateTableRedoState, "_vt"): {}, - fmt.Sprintf(sqlCreateTableRedoStatement, "_vt"): {}, - fmt.Sprintf(sqlCreateTableDTState, "_vt"): {}, - fmt.Sprintf(sqlCreateTableDTParticipant, "_vt"): {}, // queries for schema info "select unix_timestamp()": { Fields: []*querypb.Field{{ @@ -1650,6 +1643,7 @@ func addQueryExecutorSupportedQueries(db *fakesqldb.DB) { fmt.Sprintf(sqlReadAllRedo, "_vt", "_vt"): {}, } + sidecardb.AddSchemaInitQueries(db, true) for query, result := range queryResultMap { db.AddQuery(query, result) } diff --git a/go/vt/vttablet/tabletserver/repltracker/writer.go b/go/vt/vttablet/tabletserver/repltracker/writer.go index 3fd0fbb9864..4f8a2120834 100644 --- a/go/vt/vttablet/tabletserver/repltracker/writer.go +++ b/go/vt/vttablet/tabletserver/repltracker/writer.go @@ -24,8 +24,6 @@ import ( "google.golang.org/protobuf/proto" - "vitess.io/vitess/go/vt/withddl" - "context" "vitess.io/vitess/go/sqltypes" @@ -42,20 +40,9 @@ import ( ) const ( - sqlCreateSidecarDB = "create database if not exists %s" - sqlCreateHeartbeatTable = `CREATE TABLE IF NOT EXISTS %s.heartbeat ( - keyspaceShard VARBINARY(256) NOT NULL PRIMARY KEY, - tabletUid INT UNSIGNED NOT NULL, - ts BIGINT UNSIGNED NOT NULL - ) engine=InnoDB` sqlUpsertHeartbeat = "INSERT INTO %s.heartbeat (ts, tabletUid, keyspaceShard) VALUES (%a, %a, %a) ON DUPLICATE KEY UPDATE ts=VALUES(ts), tabletUid=VALUES(tabletUid)" ) -var withDDL = withddl.New([]string{ - fmt.Sprintf(sqlCreateSidecarDB, "_vt"), - fmt.Sprintf(sqlCreateHeartbeatTable, "_vt"), -}) - // heartbeatWriter runs on primary tablets and writes heartbeats to the _vt.heartbeat // table at a regular interval, defined by heartbeat_interval. type heartbeatWriter struct { @@ -221,7 +208,7 @@ func (w *heartbeatWriter) write() error { return err } defer appConn.Recycle() - _, err = withDDL.Exec(ctx, upsert, appConn.ExecuteFetch, allPrivsConn.ExecuteFetch) + _, err = appConn.ExecuteFetch(upsert, 1, false) if err != nil { return err } diff --git a/go/vt/vttablet/tabletserver/repltracker/writer_test.go b/go/vt/vttablet/tabletserver/repltracker/writer_test.go index f678381ec2b..07ae3186877 100644 --- a/go/vt/vttablet/tabletserver/repltracker/writer_test.go +++ b/go/vt/vttablet/tabletserver/repltracker/writer_test.go @@ -22,9 +22,7 @@ import ( "time" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/dbconfigs" @@ -39,29 +37,6 @@ var ( } ) -func TestCreateSchema(t *testing.T) { - db := fakesqldb.New(t) - defer db.Close() - tw := newTestWriter(db, mockNowFunc) - defer tw.Close() - writes.Reset() - - db.OrderMatters() - upsert := fmt.Sprintf("INSERT INTO %s.heartbeat (ts, tabletUid, keyspaceShard) VALUES (%d, %d, '%s') ON DUPLICATE KEY UPDATE ts=VALUES(ts), tabletUid=VALUES(tabletUid)", - "_vt", now.UnixNano(), tw.tabletAlias.Uid, tw.keyspaceShard) - failInsert := fakesqldb.ExpectedExecuteFetch{ - Query: upsert, - Error: mysql.NewSQLError(mysql.ERBadDb, "", "bad db error"), - } - db.AddExpectedExecuteFetch(failInsert) - db.AddExpectedQuery(fmt.Sprintf(sqlCreateSidecarDB, "_vt"), nil) - db.AddExpectedQuery(fmt.Sprintf(sqlCreateHeartbeatTable, "_vt"), nil) - db.AddExpectedQuery(upsert, nil) - - err := tw.write() - require.NoError(t, err) -} - func TestWriteHeartbeat(t *testing.T) { db := fakesqldb.New(t) defer db.Close() diff --git a/go/vt/vttablet/tabletserver/report.xml b/go/vt/vttablet/tabletserver/report.xml new file mode 100644 index 00000000000..e69de29bb2d diff --git a/go/vt/vttablet/tabletserver/schema/engine.go b/go/vt/vttablet/tabletserver/schema/engine.go index 2e4368a86e2..0f3907685ac 100644 --- a/go/vt/vttablet/tabletserver/schema/engine.go +++ b/go/vt/vttablet/tabletserver/schema/engine.go @@ -25,6 +25,9 @@ import ( "sync" "time" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/sidecardb" + "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/dbconnpool" "vitess.io/vitess/go/vt/schema" @@ -126,15 +129,56 @@ func (se *Engine) InitDBConfig(cp dbconfigs.Connector) { se.cp = cp } +// syncSidecarDB is called either the first time a primary starts, or on subsequent loads, to possibly upgrade to a +// new Vitess version. This is the only entry point into the sidecardb module to get the _vt database to the desired +// schema for the running Vitess version. +// There is some extra logging in here which can be removed in a future version (>v16) once the new schema init +// functionality is stable. +func (se *Engine) syncSidecarDB(ctx context.Context, conn *dbconnpool.DBConnection) error { + log.Infof("In syncSidecarDB") + defer func(start time.Time) { + log.Infof("syncSidecarDB took %d ms", time.Since(start).Milliseconds()) + }(time.Now()) + + var exec sidecardb.Exec = func(ctx context.Context, query string, maxRows int, useDB bool) (*sqltypes.Result, error) { + if useDB { + _, err := conn.ExecuteFetch(sidecardb.UseSidecarDatabaseQuery, maxRows, false) + if err != nil { + return nil, err + } + } + return conn.ExecuteFetch(query, maxRows, false) + } + if err := sidecardb.Init(ctx, exec); err != nil { + log.Errorf("Error in sidecardb.Init: %+v", err) + if se.env.Config().DB.HasGlobalSettings() { + log.Warning("Ignoring sidecardb.Init error for unmanaged tablets") + return nil + } + log.Errorf("syncSidecarDB error %+v", err) + return err + } + log.Infof("syncSidecarDB done") + return nil +} + // EnsureConnectionAndDB ensures that we can connect to mysql. // If tablet type is primary and there is no db, then the database is created. // This function can be called before opening the Engine. func (se *Engine) EnsureConnectionAndDB(tabletType topodatapb.TabletType) error { ctx := tabletenv.LocalContext() - conn, err := dbconnpool.NewDBConnection(ctx, se.env.Config().DB.AppWithDB()) + // We use AllPrivs since syncSidecarDB() might need to upgrade the schema + conn, err := dbconnpool.NewDBConnection(ctx, se.env.Config().DB.AllPrivsWithDB()) if err == nil { - conn.Close() se.dbCreationFailed = false + // upgrade _vt if required, for a tablet with an existing database + if tabletType == topodatapb.TabletType_PRIMARY { + if err := se.syncSidecarDB(ctx, conn); err != nil { + conn.Close() + return err + } + } + conn.Close() return nil } if tabletType != topodatapb.TabletType_PRIMARY { @@ -165,6 +209,10 @@ func (se *Engine) EnsureConnectionAndDB(tabletType topodatapb.TabletType) error log.Infof("db %v created", dbname) se.dbCreationFailed = false + // creates sidecar schema, the first time the database is created + if err := se.syncSidecarDB(ctx, conn); err != nil { + return err + } return nil } diff --git a/go/vt/vttablet/tabletserver/schema/tracker.go b/go/vt/vttablet/tabletserver/schema/tracker.go index 51204e4db3f..f3345b0a972 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker.go +++ b/go/vt/vttablet/tabletserver/schema/tracker.go @@ -36,28 +36,8 @@ import ( binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" - "vitess.io/vitess/go/vt/withddl" ) -const createSidecarDB = "CREATE DATABASE IF NOT EXISTS _vt" -const createSchemaTrackingTable = `CREATE TABLE IF NOT EXISTS _vt.schema_version ( - id INT AUTO_INCREMENT, - pos VARBINARY(10000) NOT NULL, - time_updated BIGINT(20) NOT NULL, - ddl VARBINARY(1000) DEFAULT NULL, - schemax BLOB NOT NULL, - PRIMARY KEY (id) - ) ENGINE=InnoDB` -const alterSchemaTrackingTableDDLBlob = "alter table _vt.schema_version modify column ddl BLOB NOT NULL" -const alterSchemaTrackingTableSchemaxBlob = "alter table _vt.schema_version modify column schemax LONGBLOB NOT NULL" - -var withDDL = withddl.New([]string{ - createSidecarDB, - createSchemaTrackingTable, - alterSchemaTrackingTableDDLBlob, - alterSchemaTrackingTableSchemaxBlob, -}) - // VStreamer defines the functions of VStreamer // that the replicationWatcher needs. type VStreamer interface { @@ -192,7 +172,7 @@ func (tr *Tracker) isSchemaVersionTableEmpty(ctx context.Context) (bool, error) return false, err } defer conn.Recycle() - result, err := withDDL.Exec(ctx, "select id from _vt.schema_version limit 1", conn.Exec, conn.Exec) + result, err := conn.Exec(ctx, "select id from _vt.schema_version limit 1", 1, false) if err != nil { return false, err } @@ -258,7 +238,7 @@ func (tr *Tracker) saveCurrentSchemaToDb(ctx context.Context, gtid, ddl string, query := fmt.Sprintf("insert into _vt.schema_version "+ "(pos, ddl, schemax, time_updated) "+ "values (%v, %v, %v, %d)", encodeString(gtid), encodeString(ddl), encodeString(string(blob)), timestamp) - _, err = withDDL.Exec(ctx, query, conn.Exec, conn.Exec) + _, err = conn.Exec(ctx, query, 1, false) if err != nil { return err } diff --git a/go/vt/vttablet/tabletserver/state_manager.go b/go/vt/vttablet/tabletserver/state_manager.go index 60d3aaa3792..625d2188272 100644 --- a/go/vt/vttablet/tabletserver/state_manager.go +++ b/go/vt/vttablet/tabletserver/state_manager.go @@ -22,6 +22,8 @@ import ( "sync" "time" + "vitess.io/vitess/go/vt/servenv" + "google.golang.org/protobuf/proto" "vitess.io/vitess/go/sync2" @@ -598,6 +600,9 @@ func (sm *stateManager) setTimeBomb() chan struct{} { // setState changes the state and logs the event. func (sm *stateManager) setState(tabletType topodatapb.TabletType, state servingState) { + defer func() { + log.Infof("Tablet Init took %d ms", time.Since(servenv.GetInitStartTime()).Milliseconds()) + }() sm.mu.Lock() defer sm.mu.Unlock() if tabletType == topodatapb.TabletType_UNKNOWN { diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 5d2f5cc1cd8..daf32edc8b2 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -89,8 +89,7 @@ var logComputeRowSerializerKey = logutil.NewThrottledLogger("ComputeRowSerialize // the db config is not initially available. For this reason, // the initialization is done in two phases. // Some subcomponents have Init functions. Such functions usually -// perform one-time initializations like creating metadata tables -// in the sidecar database. These functions must be idempotent. +// perform one-time initializations and must be idempotent. // Open and Close can be called repeatedly during the lifetime of // a subcomponent. These should also be idempotent. type TabletServer struct { diff --git a/go/vt/vttablet/tabletserver/tabletserver_test.go b/go/vt/vttablet/tabletserver/tabletserver_test.go index 61f9c411000..ac6b20cab8a 100644 --- a/go/vt/vttablet/tabletserver/tabletserver_test.go +++ b/go/vt/vttablet/tabletserver/tabletserver_test.go @@ -32,6 +32,8 @@ import ( "testing" "time" + "vitess.io/vitess/go/vt/sidecardb" + "vitess.io/vitess/go/vt/callerid" "vitess.io/vitess/go/mysql/fakesqldb" @@ -2232,6 +2234,7 @@ func setupTabletServerTest(t *testing.T, keyspaceName string) (*fakesqldb.DB, *T func setupTabletServerTestCustom(t *testing.T, config *tabletenv.TabletConfig, keyspaceName string) (*fakesqldb.DB, *TabletServer) { db := setupFakeDB(t) + sidecardb.AddSchemaInitQueries(db, true) tsv := NewTabletServer("TabletServerTest", config, memorytopo.NewServer(""), &topodatapb.TabletAlias{}) require.Equal(t, StateNotConnected, tsv.sm.State()) dbcfgs := newDBConfigs(db) @@ -2279,16 +2282,6 @@ func addTabletServerSupportedQueries(db *fakesqldb.DB) { "update test_table set name_string = 'tx3' where pk = 2 and `name` = 1 limit 10001": { RowsAffected: 1, }, - // queries for twopc - fmt.Sprintf(sqlCreateSidecarDB, "_vt"): {}, - fmt.Sprintf(sqlDropLegacy1, "_vt"): {}, - fmt.Sprintf(sqlDropLegacy2, "_vt"): {}, - fmt.Sprintf(sqlDropLegacy3, "_vt"): {}, - fmt.Sprintf(sqlDropLegacy4, "_vt"): {}, - fmt.Sprintf(sqlCreateTableRedoState, "_vt"): {}, - fmt.Sprintf(sqlCreateTableRedoStatement, "_vt"): {}, - fmt.Sprintf(sqlCreateTableDTState, "_vt"): {}, - fmt.Sprintf(sqlCreateTableDTParticipant, "_vt"): {}, // queries for schema info "select unix_timestamp()": { Fields: []*querypb.Field{{ @@ -2389,6 +2382,7 @@ func addTabletServerSupportedQueries(db *fakesqldb.DB) { "rollback": {}, fmt.Sprintf(sqlReadAllRedo, "_vt", "_vt"): {}, } + sidecardb.AddSchemaInitQueries(db, true) for query, result := range queryResultMap { db.AddQuery(query, result) } diff --git a/go/vt/vttablet/tabletserver/twopc.go b/go/vt/vttablet/tabletserver/twopc.go index 972de83d244..aaeaf8c900a 100644 --- a/go/vt/vttablet/tabletserver/twopc.go +++ b/go/vt/vttablet/tabletserver/twopc.go @@ -40,51 +40,16 @@ import ( ) const ( - sqlCreateSidecarDB = "create database if not exists %s" - - sqlDropLegacy1 = "drop table if exists %s.redo_log_transaction" - sqlDropLegacy2 = "drop table if exists %s.redo_log_statement" - sqlDropLegacy3 = "drop table if exists %s.transaction" - sqlDropLegacy4 = "drop table if exists %s.participant" - // RedoStateFailed represents the Failed state for redo_state. RedoStateFailed = 0 // RedoStatePrepared represents the Prepared state for redo_state. - RedoStatePrepared = 1 - sqlCreateTableRedoState = `create table if not exists %s.redo_state( - dtid varbinary(512), - state bigint, - time_created bigint, - primary key(dtid) - ) engine=InnoDB` - - sqlCreateTableRedoStatement = `create table if not exists %s.redo_statement( - dtid varbinary(512), - id bigint, - statement mediumblob, - primary key(dtid, id) - ) engine=InnoDB` - + RedoStatePrepared = 1 // DTStatePrepare represents the PREPARE state for dt_state. DTStatePrepare = querypb.TransactionState_PREPARE // DTStateCommit represents the COMMIT state for dt_state. DTStateCommit = querypb.TransactionState_COMMIT // DTStateRollback represents the ROLLBACK state for dt_state. - DTStateRollback = querypb.TransactionState_ROLLBACK - sqlCreateTableDTState = `create table if not exists %s.dt_state( - dtid varbinary(512), - state bigint, - time_created bigint, - primary key(dtid) - ) engine=InnoDB` - - sqlCreateTableDTParticipant = `create table if not exists %s.dt_participant( - dtid varbinary(512), - id bigint, - keyspace varchar(256), - shard varchar(256), - primary key(dtid, id) - ) engine=InnoDB` + DTStateRollback = querypb.TransactionState_ROLLBACK sqlReadAllRedo = `select t.dtid, t.state, t.time_created, s.statement from %s.redo_state t @@ -174,28 +139,11 @@ func NewTwoPC(readPool *connpool.Pool) *TwoPC { // Open starts the TwoPC service. func (tpc *TwoPC) Open(dbconfigs *dbconfigs.DBConfigs) error { - dbname := "_vt" conn, err := dbconnpool.NewDBConnection(context.TODO(), dbconfigs.DbaWithDB()) if err != nil { return err } defer conn.Close() - statements := []string{ - fmt.Sprintf(sqlCreateSidecarDB, dbname), - fmt.Sprintf(sqlDropLegacy1, dbname), - fmt.Sprintf(sqlDropLegacy2, dbname), - fmt.Sprintf(sqlDropLegacy3, dbname), - fmt.Sprintf(sqlDropLegacy4, dbname), - fmt.Sprintf(sqlCreateTableRedoState, dbname), - fmt.Sprintf(sqlCreateTableRedoStatement, dbname), - fmt.Sprintf(sqlCreateTableDTState, dbname), - fmt.Sprintf(sqlCreateTableDTParticipant, dbname), - } - for _, s := range statements { - if _, err := conn.ExecuteFetch(s, 0, false); err != nil { - return err - } - } tpc.readPool.Open(dbconfigs.AppWithDB(), dbconfigs.DbaWithDB(), dbconfigs.DbaWithDB()) log.Infof("TwoPC: Engine open succeeded") return nil diff --git a/go/vt/vttablet/tabletserver/tx_engine.go b/go/vt/vttablet/tabletserver/tx_engine.go index 8d425003ac3..4e1ceeeb1eb 100644 --- a/go/vt/vttablet/tabletserver/tx_engine.go +++ b/go/vt/vttablet/tabletserver/tx_engine.go @@ -192,7 +192,7 @@ func (te *TxEngine) transition(state txEngineState) { func (te *TxEngine) Close() { log.Infof("TxEngine - started Close. Acquiring stateLock lock") te.stateLock.Lock() - log.Infof("TxEngine - arcquired stateLock") + log.Infof("TxEngine - acquired stateLock") defer func() { te.state = NotServing te.stateLock.Unlock() diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_flaky_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_flaky_test.go index 1eaec093040..72ad570ac66 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_flaky_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_flaky_test.go @@ -268,7 +268,8 @@ func TestVersion(t *testing.T) { defer engine.Close() execStatements(t, []string{ - "create table _vt.schema_version(id int, pos varbinary(10000), time_updated bigint(20), ddl varchar(10000), schemax blob, primary key(id))", + "create database if not exists _vt", + "create table if not exists _vt.schema_version(id int, pos varbinary(10000), time_updated bigint(20), ddl varchar(10000), schemax blob, primary key(id))", }) defer execStatements(t, []string{ "drop table _vt.schema_version", @@ -1821,7 +1822,7 @@ func TestJournal(t *testing.T) { } execStatements(t, []string{ - "create table _vt.resharding_journal(id int, db_name varchar(128), val blob, primary key(id))", + "create table if not exists _vt.resharding_journal(id int, db_name varchar(128), val blob, primary key(id))", }) defer execStatements(t, []string{ "drop table _vt.resharding_journal", diff --git a/go/vt/vttest/local_cluster.go b/go/vt/vttest/local_cluster.go index ce8d16ec075..3d954de860d 100644 --- a/go/vt/vttest/local_cluster.go +++ b/go/vt/vttest/local_cluster.go @@ -30,6 +30,8 @@ import ( "time" "unicode" + "vitess.io/vitess/go/vt/sidecardb" + "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" @@ -497,9 +499,31 @@ func (db *LocalCluster) loadSchema(shouldRunDatabaseMigrations bool) error { return nil } +func (db *LocalCluster) createVTSchema() error { + var sidecardbExec sidecardb.Exec = func(ctx context.Context, query string, maxRows int, useDB bool) (*sqltypes.Result, error) { + if useDB { + if err := db.Execute([]string{sidecardb.UseSidecarDatabaseQuery}, ""); err != nil { + return nil, err + } + } + err := db.Execute([]string{query}, "") + return &sqltypes.Result{}, err + } + + if err := sidecardb.Init(context.Background(), sidecardbExec); err != nil { + return err + } + return nil +} func (db *LocalCluster) createDatabases() error { log.Info("Creating databases in cluster...") + // The tablets created in vttest do not follow the same tablet init process, so we need to explicitly create + // the sidecar database tables + if err := db.createVTSchema(); err != nil { + return err + } + var sql []string for _, kpb := range db.Topology.Keyspaces { if kpb.ServedFrom != "" { @@ -530,7 +554,7 @@ func (db *LocalCluster) Execute(sql []string, dbname string) error { for _, cmd := range sql { log.Infof("Execute(%s): \"%s\"", dbname, cmd) - _, err := conn.ExecuteFetch(cmd, 0, false) + _, err := conn.ExecuteFetch(cmd, -1, false) if err != nil { return err } diff --git a/go/vt/vttest/mysqlctl.go b/go/vt/vttest/mysqlctl.go index 8eaa8c65f61..8646e344ea5 100644 --- a/go/vt/vttest/mysqlctl.go +++ b/go/vt/vttest/mysqlctl.go @@ -25,6 +25,8 @@ import ( "strings" "time" + "vitess.io/vitess/go/vt/log" + "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/mysqlctl" ) @@ -97,7 +99,7 @@ func (ctl *Mysqlctl) Start() error { cmd.Env = append(cmd.Env, os.Environ()...) cmd.Env = append(cmd.Env, ctl.Env...) cmd.Env = append(cmd.Env, fmt.Sprintf("EXTRA_MY_CNF=%s", myCnf)) - + log.Infof("Starting MySQL using: %+v", cmd.Env) _, err := cmd.Output() return err } diff --git a/go/vt/withddl/withddl.go b/go/vt/withddl/withddl.go deleted file mode 100644 index 15c78509777..00000000000 --- a/go/vt/withddl/withddl.go +++ /dev/null @@ -1,145 +0,0 @@ -/* -Copyright 2020 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Package withddl allows you to automatically ensure -// the tables against which you want to apply statements -// are up-to-date. -package withddl - -import ( - "context" - "fmt" - - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/sqlparser" -) - -const QueryToTriggerWithDDL = "SELECT _vt_no_such_column__init_schema FROM _vt.vreplication LIMIT 1" - -// WithDDL allows you to execute statements against -// tables whose schema may not be up-to-date. If the tables -// don't exist or result in a schema error, it can apply a series -// of idempotent DDLs that will create or bring the tables -// to the desired state and retry. -type WithDDL struct { - ddls []string -} - -// New creates a new WithDDL. -func New(ddls []string) *WithDDL { - return &WithDDL{ - ddls: ddls, - } -} - -// DDLs returns a copy of the ddls -func (wd *WithDDL) DDLs() []string { - return wd.ddls[:] -} - -// Exec executes the query using the supplied function. -// If there are any schema errors, it applies the DDLs and retries. -// It takes 2 functions, one to run the query and the other to run the -// DDL commands. This is generally needed so that different users can be used -// to run the commands. i.e. AllPrivs user for DDLs and App user for query commands. -// Funcs can be any of these types: -// func(query string) (*sqltypes.Result, error) -// func(query string, maxrows int) (*sqltypes.Result, error) -// func(query string, maxrows int, wantfields bool) (*sqltypes.Result, error) -// func(ctx context.Context, query string, maxrows int, wantfields bool) (*sqltypes.Result, error) -func (wd *WithDDL) Exec(ctx context.Context, query string, fQuery any, fDDL any) (*sqltypes.Result, error) { - execQuery, err := wd.unify(ctx, fQuery) - if err != nil { - return nil, err - } - execDDL, err := wd.unify(ctx, fDDL) - if err != nil { - return nil, err - } - qr, err := execQuery(query) - if err == nil { - return qr, nil - } - if !wd.isSchemaError(err) { - return nil, err - } - - log.Infof("Updating schema for %v and retrying: %v", sqlparser.TruncateForUI(err.Error()), err) - for _, applyQuery := range wd.ddls { - _, merr := execDDL(applyQuery) - if merr == nil { - continue - } - if mysql.IsSchemaApplyError(merr) { - continue - } - log.Warningf("DDL apply %v failed: %v", applyQuery, merr) - // Return the original error. - return nil, err - } - return execQuery(query) -} - -// ExecIgnore executes the query using the supplied function. -// If there are any schema errors, it returns an empty result. -func (wd *WithDDL) ExecIgnore(ctx context.Context, query string, f any) (*sqltypes.Result, error) { - exec, err := wd.unify(ctx, f) - if err != nil { - return nil, err - } - qr, err := exec(query) - if err == nil { - return qr, nil - } - if !wd.isSchemaError(err) { - return nil, err - } - return &sqltypes.Result{}, nil -} - -func (wd *WithDDL) unify(ctx context.Context, f any) (func(query string) (*sqltypes.Result, error), error) { - switch f := f.(type) { - case func(query string) (*sqltypes.Result, error): - return f, nil - case func(query string, maxrows int) (*sqltypes.Result, error): - return func(query string) (*sqltypes.Result, error) { - return f(query, 10000) - }, nil - case func(query string, maxrows int, wantfields bool) (*sqltypes.Result, error): - return func(query string) (*sqltypes.Result, error) { - return f(query, 10000, true) - }, nil - case func(ctx context.Context, query string, maxrows int, wantfields bool) (*sqltypes.Result, error): - return func(query string) (*sqltypes.Result, error) { - return f(ctx, query, 10000, true) - }, nil - } - return nil, fmt.Errorf("BUG: supplied function does not match expected signatures") -} - -func (wd *WithDDL) isSchemaError(err error) bool { - merr, isSQLErr := err.(*mysql.SQLError) - if !isSQLErr { - return false - } - switch merr.Num { - case mysql.ERNoSuchTable, mysql.ERBadDb, mysql.ERWrongValueCountOnRow, mysql.ERBadFieldError: - return true - } - return false -} diff --git a/go/vt/withddl/withddl_test.go b/go/vt/withddl/withddl_test.go deleted file mode 100644 index 6a36dd14992..00000000000 --- a/go/vt/withddl/withddl_test.go +++ /dev/null @@ -1,316 +0,0 @@ -/* -Copyright 2020 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package withddl - -import ( - "context" - "fmt" - "os" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "vitess.io/vitess/go/mysql" - "vitess.io/vitess/go/sqltypes" - vttestpb "vitess.io/vitess/go/vt/proto/vttest" - "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" - "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv/tabletenvtest" - "vitess.io/vitess/go/vt/vttest" -) - -var connParams mysql.ConnParams - -func TestExec(t *testing.T) { - ctx := context.Background() - conn, err := mysql.Connect(ctx, &connParams) - require.NoError(t, err) - defer conn.Close() - _, err = conn.ExecuteFetch("create database t", 10000, true) - require.NoError(t, err) - defer conn.ExecuteFetch("drop database t", 10000, true) // nolint:errcheck - - testcases := []struct { - name string - ddls []string - init []string - query string - qr *sqltypes.Result - err string - cleanup []string - }{{ - name: "TableExists", - ddls: []string{ - "invalid sql", - }, - init: []string{ - "create table a(id int, primary key(id))", - }, - query: "insert into a values(1)", - qr: &sqltypes.Result{RowsAffected: 1}, - cleanup: []string{ - "drop table a", - }, - }, { - name: "TableDoesNotExist", - ddls: []string{ - "create table if not exists a(id int, primary key(id))", - }, - query: "insert into a values(1)", - qr: &sqltypes.Result{RowsAffected: 1}, - cleanup: []string{ - "drop table a", - }, - }, { - name: "TableMismatch", - ddls: []string{ - "create table if not exists a(id int, val int, primary key(id))", - }, - init: []string{ - "create table a(id int, primary key(id))", - }, - query: "insert into a values(1, 2)", - err: "Column count doesn't match value", - cleanup: []string{ - "drop table a", - }, - }, { - name: "TableMustBeAltered", - ddls: []string{ - "create table if not exists a(id int, primary key(id))", - "alter table a add column val int", - }, - init: []string{ - "create table a(id int, primary key(id))", - }, - query: "insert into a values(1, 2)", - qr: &sqltypes.Result{RowsAffected: 1}, - cleanup: []string{ - "drop table a", - }, - }, { - name: "NonidempotentDDL", - ddls: []string{ - "create table a(id int, primary key(id))", - "alter table a add column val int", - }, - init: []string{ - "create table a(id int, primary key(id))", - }, - query: "insert into a values(1, 2)", - qr: &sqltypes.Result{RowsAffected: 1}, - cleanup: []string{ - "drop table a", - }, - }, { - name: "DupFieldInDDL", - ddls: []string{ - // error for adding v1 should be ignored. - "alter table a add column v1 int", - "alter table a add column v2 int", - }, - init: []string{ - "create table a(id int, v1 int, primary key(id))", - }, - query: "insert into a values(1, 2, 3)", - qr: &sqltypes.Result{RowsAffected: 1}, - cleanup: []string{ - "drop table a", - }, - }, { - name: "NonSchemaError", - ddls: []string{ - "invalid sql", - }, - query: "syntax error", - err: "error in your SQL syntax", - }, { - name: "BadDDL", - ddls: []string{ - "invalid sql", - }, - query: "insert into a values(1)", - err: "doesn't exist", - }} - - withdb := connParams - withdb.DbName = "t" - execconn, err := mysql.Connect(ctx, &withdb) - require.NoError(t, err) - defer execconn.Close() - - funcs := []struct { - name string - f any - }{{ - name: "f1", - f: func(query string) (*sqltypes.Result, error) { - return execconn.ExecuteFetch(query, 10000, true) - }, - }, { - name: "f2", - f: func(query string, maxrows int) (*sqltypes.Result, error) { - return execconn.ExecuteFetch(query, maxrows, true) - }, - }, { - name: "f3", - f: execconn.ExecuteFetch, - }, { - name: "f4", - f: func(ctx context.Context, query string, maxrows int, wantfields bool) (*sqltypes.Result, error) { - return execconn.ExecuteFetch(query, maxrows, wantfields) - }, - }} - - for _, test := range testcases { - for _, fun := range funcs { - t.Run(fmt.Sprintf("%v-%v", test.name, fun.name), func(t *testing.T) { - for _, query := range test.init { - _, err = execconn.ExecuteFetch(query, 10000, true) - require.NoError(t, err) - } - - wd := New(test.ddls) - qr, err := wd.Exec(ctx, test.query, fun.f, fun.f) - if test.qr != nil { - test.qr.StatusFlags = sqltypes.ServerStatusAutocommit - } - checkResult(t, test.qr, test.err, qr, err) - - for _, query := range test.cleanup { - _, err = execconn.ExecuteFetch(query, 10000, true) - require.NoError(t, err) - } - }) - } - } -} - -func TestExecIgnore(t *testing.T) { - ctx := context.Background() - conn, err := mysql.Connect(ctx, &connParams) - require.NoError(t, err) - defer conn.Close() - _, err = conn.ExecuteFetch("create database t", 10000, true) - require.NoError(t, err) - defer conn.ExecuteFetch("drop database t", 10000, true) // nolint:errcheck - - withdb := connParams - withdb.DbName = "t" - execconn, err := mysql.Connect(ctx, &withdb) - require.NoError(t, err) - defer execconn.Close() - - wd := New([]string{}) - qr, err := wd.ExecIgnore(ctx, "select * from a", execconn.ExecuteFetch) - require.NoError(t, err) - assert.Equal(t, &sqltypes.Result{}, qr) - - _, err = wd.ExecIgnore(ctx, "syntax error", execconn.ExecuteFetch) - // This should fail. - assert.Error(t, err) - - _, _ = execconn.ExecuteFetch("create table a(id int, primary key(id))", 10000, false) - defer execconn.ExecuteFetch("drop table a", 10000, false) // nolint:errcheck - _, _ = execconn.ExecuteFetch("insert into a values(1)", 10000, false) - qr, err = wd.ExecIgnore(ctx, "select * from a", execconn.ExecuteFetch) - require.NoError(t, err) - assert.Equal(t, 1, len(qr.Rows)) -} - -func TestDifferentExecFunctions(t *testing.T) { - ctx := context.Background() - conn, err := mysql.Connect(ctx, &connParams) - require.NoError(t, err) - defer conn.Close() - defer conn.ExecuteFetch("drop database t", 10000, true) // nolint:errcheck - - execconn, err := mysql.Connect(ctx, &connParams) - require.NoError(t, err) - defer execconn.Close() - - wd := New([]string{"create database t"}) - _, err = wd.Exec(ctx, "select * from a", func(query string) (*sqltypes.Result, error) { - return nil, mysql.NewSQLError(mysql.ERNoSuchTable, mysql.SSUnknownSQLState, "error in execution") - }, execconn.ExecuteFetch) - require.EqualError(t, err, "error in execution (errno 1146) (sqlstate HY000)") - - res, err := execconn.ExecuteFetch("show databases", 10000, false) - require.NoError(t, err) - foundDatabase := false - for _, row := range res.Rows { - if row[0].ToString() == "t" { - foundDatabase = true - } - } - require.True(t, foundDatabase, "database should be created since DDL should have executed") -} - -func checkResult(t *testing.T, wantqr *sqltypes.Result, wanterr string, qr *sqltypes.Result, err error) { - t.Helper() - - assert.Equal(t, wantqr, qr) - var goterr string - if err != nil { - goterr = err.Error() - } - if wanterr == "" { - assert.Equal(t, "", goterr) - } - assert.Contains(t, goterr, wanterr) -} - -func TestMain(m *testing.M) { - tabletenvtest.LoadTabletEnvFlags() - tabletenv.Init() - - exitCode := func() int { - // Launch MySQL. - // We need a Keyspace in the topology, so the DbName is set. - // We need a Shard too, so the database 'vttest' is created. - cfg := vttest.Config{ - Topology: &vttestpb.VTTestTopology{ - Keyspaces: []*vttestpb.Keyspace{ - { - Name: "vttest", - Shards: []*vttestpb.Shard{ - { - Name: "0", - DbNameOverride: "vttest", - }, - }, - }, - }, - }, - OnlyMySQL: true, - } - defer os.RemoveAll(cfg.SchemaDir) - cluster := vttest.LocalCluster{ - Config: cfg, - } - if err := cluster.Setup(); err != nil { - fmt.Fprintf(os.Stderr, "could not launch mysql: %v\n", err) - return 1 - } - defer cluster.TearDown() - - connParams = cluster.MySQLConnParams() - - return m.Run() - }() - os.Exit(exitCode) -} diff --git a/go/vt/wrangler/resharder_test.go b/go/vt/wrangler/resharder_test.go index 074d7f7fcf4..9fdb5889a9e 100644 --- a/go/vt/wrangler/resharder_test.go +++ b/go/vt/wrangler/resharder_test.go @@ -454,10 +454,6 @@ func TestResharderCopySchema(t *testing.T) { env.expectValidation() env.expectNoRefStream() - // These queries confirm that the copy schema function is getting called. - env.tmc.expectVRQuery(100, "SELECT 1 FROM information_schema.tables WHERE table_schema = '_vt' AND table_name = 'shard_metadata'", &sqltypes.Result{}) - env.tmc.expectVRQuery(100, "SELECT 1 FROM information_schema.tables WHERE table_schema = '_vt' AND table_name = 'shard_metadata'", &sqltypes.Result{}) - env.tmc.expectVRQuery( 200, insertPrefix+ diff --git a/go/vt/wrangler/schema.go b/go/vt/wrangler/schema.go index dc2ba1f1c90..487098f818c 100644 --- a/go/vt/wrangler/schema.go +++ b/go/vt/wrangler/schema.go @@ -215,11 +215,6 @@ func (wr *Wrangler) CopySchemaShard(ctx context.Context, sourceTabletAlias *topo return fmt.Errorf("no primary in shard record %v/%v. Consider running 'vtctl InitShardPrimary' in case of a new shard or reparenting the shard to fix the topology data", destKeyspace, destShard) } - err = schematools.CopyShardMetadata(ctx, wr.ts, wr.tmc, sourceTabletAlias, destShardInfo.PrimaryAlias) - if err != nil { - return fmt.Errorf("copyShardMetadata(%v, %v) failed: %v", sourceTabletAlias, destShardInfo.PrimaryAlias, err) - } - diffs, err := schematools.CompareSchemas(ctx, wr.ts, wr.tmc, sourceTabletAlias, destShardInfo.PrimaryAlias, tables, excludeTables, includeViews) if err != nil { return fmt.Errorf("CopySchemaShard failed because schemas could not be compared initially: %v", err) diff --git a/go/vt/wrangler/testlib/backup_test.go b/go/vt/wrangler/testlib/backup_test.go index 7cdb7172e19..56ca6392864 100644 --- a/go/vt/wrangler/testlib/backup_test.go +++ b/go/vt/wrangler/testlib/backup_test.go @@ -92,17 +92,10 @@ func testBackupRestore(t *testing.T, cDetails *compressionDetails) error { defer vp.Close() // Set up mock query results. - db.AddQuery("CREATE DATABASE IF NOT EXISTS _vt", &sqltypes.Result{}) + db.AddQuery(mysqlctl.GenerateInitialBinlogEntry(), &sqltypes.Result{}) db.AddQuery("BEGIN", &sqltypes.Result{}) db.AddQuery("COMMIT", &sqltypes.Result{}) db.AddQueryPattern(`SET @@session\.sql_log_bin = .*`, &sqltypes.Result{}) - db.AddQueryPattern(`CREATE TABLE IF NOT EXISTS _vt\.shard_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`CREATE TABLE IF NOT EXISTS _vt\.local_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`ALTER TABLE _vt\.local_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`ALTER TABLE _vt\.shard_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`UPDATE _vt\.local_metadata SET db_name=.*`, &sqltypes.Result{}) - db.AddQueryPattern(`UPDATE _vt\.shard_metadata SET db_name=.*`, &sqltypes.Result{}) - db.AddQueryPattern(`INSERT INTO _vt\.local_metadata .*`, &sqltypes.Result{}) // Initialize our temp dirs root := t.TempDir() @@ -343,17 +336,10 @@ func TestBackupRestoreLagged(t *testing.T) { defer vp.Close() // Set up mock query results. - db.AddQuery("CREATE DATABASE IF NOT EXISTS _vt", &sqltypes.Result{}) + db.AddQuery(mysqlctl.GenerateInitialBinlogEntry(), &sqltypes.Result{}) db.AddQuery("BEGIN", &sqltypes.Result{}) db.AddQuery("COMMIT", &sqltypes.Result{}) db.AddQueryPattern(`SET @@session\.sql_log_bin = .*`, &sqltypes.Result{}) - db.AddQueryPattern(`CREATE TABLE IF NOT EXISTS _vt\.shard_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`CREATE TABLE IF NOT EXISTS _vt\.local_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`ALTER TABLE _vt\.local_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`ALTER TABLE _vt\.shard_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`UPDATE _vt\.local_metadata SET db_name=.*`, &sqltypes.Result{}) - db.AddQueryPattern(`UPDATE _vt\.shard_metadata SET db_name=.*`, &sqltypes.Result{}) - db.AddQueryPattern(`INSERT INTO _vt\.local_metadata .*`, &sqltypes.Result{}) // Initialize our temp dirs root := t.TempDir() @@ -567,17 +553,10 @@ func TestRestoreUnreachablePrimary(t *testing.T) { defer vp.Close() // Set up mock query results. - db.AddQuery("CREATE DATABASE IF NOT EXISTS _vt", &sqltypes.Result{}) + db.AddQuery(mysqlctl.GenerateInitialBinlogEntry(), &sqltypes.Result{}) db.AddQuery("BEGIN", &sqltypes.Result{}) db.AddQuery("COMMIT", &sqltypes.Result{}) db.AddQueryPattern(`SET @@session\.sql_log_bin = .*`, &sqltypes.Result{}) - db.AddQueryPattern(`CREATE TABLE IF NOT EXISTS _vt\.shard_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`CREATE TABLE IF NOT EXISTS _vt\.local_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`ALTER TABLE _vt\.local_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`ALTER TABLE _vt\.shard_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`UPDATE _vt\.local_metadata SET db_name=.*`, &sqltypes.Result{}) - db.AddQueryPattern(`UPDATE _vt\.shard_metadata SET db_name=.*`, &sqltypes.Result{}) - db.AddQueryPattern(`INSERT INTO _vt\.local_metadata .*`, &sqltypes.Result{}) // Initialize our temp dirs root := t.TempDir() @@ -747,17 +726,10 @@ func TestDisableActiveReparents(t *testing.T) { defer vp.Close() // Set up mock query results. - db.AddQuery("CREATE DATABASE IF NOT EXISTS _vt", &sqltypes.Result{}) + db.AddQuery(mysqlctl.GenerateInitialBinlogEntry(), &sqltypes.Result{}) db.AddQuery("BEGIN", &sqltypes.Result{}) db.AddQuery("COMMIT", &sqltypes.Result{}) db.AddQueryPattern(`SET @@session\.sql_log_bin = .*`, &sqltypes.Result{}) - db.AddQueryPattern(`CREATE TABLE IF NOT EXISTS _vt\.shard_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`CREATE TABLE IF NOT EXISTS _vt\.local_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`ALTER TABLE _vt\.local_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`ALTER TABLE _vt\.shard_metadata .*`, &sqltypes.Result{}) - db.AddQueryPattern(`UPDATE _vt\.local_metadata SET db_name=.*`, &sqltypes.Result{}) - db.AddQueryPattern(`UPDATE _vt\.shard_metadata SET db_name=.*`, &sqltypes.Result{}) - db.AddQueryPattern(`INSERT INTO _vt\.local_metadata .*`, &sqltypes.Result{}) // Initialize our temp dirs root := t.TempDir() diff --git a/go/vt/wrangler/testlib/copy_schema_shard_test.go b/go/vt/wrangler/testlib/copy_schema_shard_test.go index 8620d7e7565..e46dab25bb0 100644 --- a/go/vt/wrangler/testlib/copy_schema_shard_test.go +++ b/go/vt/wrangler/testlib/copy_schema_shard_test.go @@ -35,7 +35,6 @@ import ( "vitess.io/vitess/go/vt/vttablet/tmclient" "vitess.io/vitess/go/vt/wrangler" - querypb "vitess.io/vitess/go/vt/proto/query" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" topodatapb "vitess.io/vitess/go/vt/proto/topodata" ) @@ -130,8 +129,6 @@ func copySchema(t *testing.T, useShardAsSource bool) { " PRIMARY KEY (`id`),\n" + " KEY `by_msg` (`msg`)\n" + ") ENGINE=InnoDB DEFAULT CHARSET=utf8" - selectInformationSchema := "SELECT 1 FROM information_schema.tables WHERE table_schema = '_vt' AND table_name = 'shard_metadata'" - selectShardMetadata := "SELECT db_name, name, value FROM _vt.shard_metadata" // The source table is asked about its schema. // It may be the primary or the rdonly. @@ -140,19 +137,6 @@ func copySchema(t *testing.T, useShardAsSource bool) { sourceDb = sourcePrimaryDb } sourceDb.AddQuery(changeToDb, &sqltypes.Result{}) - sourceDb.AddQuery(selectInformationSchema, &sqltypes.Result{ - Fields: []*querypb.Field{ - { - Type: querypb.Type_INT64, - }, - }, - Rows: [][]sqltypes.Value{ - { - sqltypes.Value{}, - }, - }, - }) - sourceDb.AddQuery(selectShardMetadata, &sqltypes.Result{}) // The destination table is asked to create the new schema. destinationPrimaryDb.AddQuery(setSQLMode, &sqltypes.Result{}) @@ -176,17 +160,6 @@ func copySchema(t *testing.T, useShardAsSource bool) { t.Fatalf("CopySchemaShard failed: %v", err) } - // Check call count on the source. - if count := sourceDb.GetQueryCalledNum(changeToDb); count != 2 { - t.Errorf("CopySchemaShard did not change to the db 2 times. Query count: %v", count) - } - if count := sourceDb.GetQueryCalledNum(selectInformationSchema); count != 1 { - t.Errorf("CopySchemaShard did not select data from information_schema.tables exactly once. Query count: %v", count) - } - if count := sourceDb.GetQueryCalledNum(selectShardMetadata); count != 1 { - t.Errorf("CopySchemaShard did not select data from _vt.shard_metadata exactly once. Query count: %v", count) - } - // Check call count on destinationPrimaryDb if count := destinationPrimaryDb.GetQueryCalledNum(createDb); count != 1 { t.Errorf("CopySchemaShard did not create the db exactly once. Query count: %v", count) diff --git a/go/vt/wrangler/testlib/emergency_reparent_shard_test.go b/go/vt/wrangler/testlib/emergency_reparent_shard_test.go index 014029504a9..e7fa3159733 100644 --- a/go/vt/wrangler/testlib/emergency_reparent_shard_test.go +++ b/go/vt/wrangler/testlib/emergency_reparent_shard_test.go @@ -91,9 +91,6 @@ func TestEmergencyReparentShard(t *testing.T) { newPrimary.FakeMysqlDaemon.WaitPrimaryPositions = append(newPrimary.FakeMysqlDaemon.WaitPrimaryPositions, newPrimary.FakeMysqlDaemon.CurrentSourceFilePosition) newPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ "STOP SLAVE IO_THREAD", - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.FakeMysqlDaemon.PromoteResult = mysql.Position{ @@ -238,9 +235,6 @@ func TestEmergencyReparentShardPrimaryElectNotBest(t *testing.T) { "RESET SLAVE ALL", "FAKE SET MASTER", "START SLAVE", - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.StartActionLoop(t, wr) diff --git a/go/vt/wrangler/testlib/planned_reparent_shard_test.go b/go/vt/wrangler/testlib/planned_reparent_shard_test.go index 50e27926cdd..0d80405aa43 100644 --- a/go/vt/wrangler/testlib/planned_reparent_shard_test.go +++ b/go/vt/wrangler/testlib/planned_reparent_shard_test.go @@ -22,6 +22,8 @@ import ( "testing" "time" + "vitess.io/vitess/go/vt/mysqlctl" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -82,9 +84,6 @@ func TestPlannedReparentShardNoPrimaryProvided(t *testing.T) { "RESET SLAVE ALL", "FAKE SET MASTER", "START SLAVE", - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.StartActionLoop(t, wr) @@ -201,9 +200,6 @@ func TestPlannedReparentShardNoError(t *testing.T) { "RESET SLAVE ALL", "FAKE SET MASTER", "START SLAVE", - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.StartActionLoop(t, wr) @@ -326,12 +322,7 @@ func TestPlannedReparentInitialization(t *testing.T) { }, } newPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", + mysqlctl.GenerateInitialBinlogEntry(), "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.StartActionLoop(t, wr) @@ -430,9 +421,6 @@ func TestPlannedReparentShardWaitForPositionFail(t *testing.T) { "RESET SLAVE ALL", "FAKE SET MASTER", "START SLAVE", - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.StartActionLoop(t, wr) @@ -544,9 +532,6 @@ func TestPlannedReparentShardWaitForPositionTimeout(t *testing.T) { "RESET SLAVE ALL", "FAKE SET MASTER", "START SLAVE", - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.StartActionLoop(t, wr) @@ -640,9 +625,6 @@ func TestPlannedReparentShardRelayLogError(t *testing.T) { }, } primary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } primary.StartActionLoop(t, wr) @@ -721,9 +703,6 @@ func TestPlannedReparentShardRelayLogErrorStartReplication(t *testing.T) { }, } primary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } primary.StartActionLoop(t, wr) @@ -822,9 +801,6 @@ func TestPlannedReparentShardPromoteReplicaFail(t *testing.T) { "RESET SLAVE ALL", "FAKE SET MASTER", "START SLAVE", - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } newPrimary.StartActionLoop(t, wr) @@ -901,9 +877,6 @@ func TestPlannedReparentShardPromoteReplicaFail(t *testing.T) { "RESET SLAVE ALL", "FAKE SET MASTER", "START SLAVE", - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ @@ -959,9 +932,6 @@ func TestPlannedReparentShardSamePrimary(t *testing.T) { }, } oldPrimary.FakeMysqlDaemon.ExpectedExecuteSuperQueryList = []string{ - "CREATE DATABASE IF NOT EXISTS _vt", - "SUBCREATE TABLE IF NOT EXISTS _vt.reparent_journal", - "ALTER TABLE _vt.reparent_journal CHANGE COLUMN master_alias primary_alias VARBINARY(32) NOT NULL", "SUBINSERT INTO _vt.reparent_journal (time_created_ns, action_name, primary_alias, replication_position) VALUES", } oldPrimary.StartActionLoop(t, wr) diff --git a/test/config.json b/test/config.json index adea2218c3a..4e9accdb8c4 100644 --- a/test/config.json +++ b/test/config.json @@ -1017,6 +1017,15 @@ "RetryMax": 2, "Tags": [] }, + "sidecardb": { + "File": "unused.go", + "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestSidecarDB"], + "Command": [], + "Manual": false, + "Shard": "schemadiff_vrepl", + "RetryMax": 2, + "Tags": [] + }, "vreplication_basic": { "File": "unused.go", "Args": ["vitess.io/vitess/go/test/endtoend/vreplication", "-run", "TestBasicVreplicationWorkflow"],