From 512f6e5faa7c3d6ee8256c112155f7e40a9ece25 Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 28 Apr 2020 11:18:34 +0200 Subject: [PATCH 01/17] Update replication watcher to look for DDLs Signed-off-by: Andres Taylor --- .../tabletserver/replication_watcher.go | 11 ++ .../tabletserver/replication_watcher_test.go | 158 ++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 go/vt/vttablet/tabletserver/replication_watcher_test.go diff --git a/go/vt/vttablet/tabletserver/replication_watcher.go b/go/vt/vttablet/tabletserver/replication_watcher.go index febfbbd1948..19afcb7cfa8 100644 --- a/go/vt/vttablet/tabletserver/replication_watcher.go +++ b/go/vt/vttablet/tabletserver/replication_watcher.go @@ -33,6 +33,11 @@ type VStreamer interface { Stream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error } +//SchemaSubscriber will get notified when the schema has been updated +type SchemaSubscriber interface { + SchemaUpdated(gtid string, ddl string, timestamp int64) +} + // ReplicationWatcher is a tabletserver service that watches the // replication stream. It will trigger schema reloads if a DDL // is encountered. @@ -40,6 +45,7 @@ type ReplicationWatcher struct { env tabletenv.Env watchReplication bool vs VStreamer + subscriber SchemaSubscriber cancel context.CancelFunc } @@ -86,6 +92,11 @@ func (rpw *ReplicationWatcher) Process(ctx context.Context) { for { // VStreamer will reload the schema when it encounters a DDL. err := rpw.vs.Stream(ctx, "current", filter, func(events []*binlogdatapb.VEvent) error { + for _, event := range events { + if event.Type == binlogdatapb.VEventType_DDL { + rpw.subscriber.SchemaUpdated(event.Gtid, event.Ddl, event.Timestamp) + } + } return nil }) select { diff --git a/go/vt/vttablet/tabletserver/replication_watcher_test.go b/go/vt/vttablet/tabletserver/replication_watcher_test.go new file mode 100644 index 00000000000..726e4669941 --- /dev/null +++ b/go/vt/vttablet/tabletserver/replication_watcher_test.go @@ -0,0 +1,158 @@ +/* +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 tabletserver + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + "golang.org/x/net/context" + "vitess.io/vitess/go/test/utils" + "vitess.io/vitess/go/vt/dbconfigs" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + "vitess.io/vitess/go/vt/servenv" + "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" +) + +func (t *testableSubscriber) SchemaUpdated(gtid string, ddl string, timestamp int64) { + t.gtids = append(t.gtids, gtid) + t.ddls = append(t.ddls, ddl) + t.timestamps = append(t.timestamps, timestamp) +} + +var _ SchemaSubscriber = (*testableSubscriber)(nil) +var _ VStreamer = (*fakeVstreamer)(nil) +var _ tabletenv.Env = (*fakeEnv)(nil) + +var env = &fakeEnv{} + +func TestReplicationWatcher(t *testing.T) { + testCases := []struct { + name string + input [][]*binlogdatapb.VEvent + expected []string + }{ + { + name: "empty", + input: [][]*binlogdatapb.VEvent{{}}, + expected: nil, + }, { + name: "single create table", + input: [][]*binlogdatapb.VEvent{{{ + Type: binlogdatapb.VEventType_DDL, + Timestamp: 643, + CurrentTime: 943, + Gtid: "gtid", + Ddl: "create table", + }}}, + expected: []string{"create table"}, + }, { + name: "mixed load", + input: [][]*binlogdatapb.VEvent{{{ + Type: binlogdatapb.VEventType_DDL, + Timestamp: 643, + CurrentTime: 943, + Gtid: "gtid", + Ddl: "create table", + }, { + Type: binlogdatapb.VEventType_INSERT, + Timestamp: 643, + CurrentTime: 943, + Gtid: "gtid2", + Ddl: "insert", + }, { + Type: binlogdatapb.VEventType_DDL, + Timestamp: 643, + CurrentTime: 943, + Gtid: "gtid3", + Ddl: "alter table", + }}}, + expected: []string{"create table", "alter table"}, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + subscriber := &testableSubscriber{} + streamer := &fakeVstreamer{ + events: testCase.input, + } + watcher := &ReplicationWatcher{ + env: env, + watchReplication: true, + vs: streamer, + subscriber: subscriber, + } + + // when + watcher.Open() + time.Sleep(1 * time.Millisecond) + watcher.Close() + + // then + require.True(t, streamer.called, "streamer never called") + utils.MustMatch(t, testCase.expected, subscriber.ddls, "didnt see ddls") + }) + } +} + +type testableSubscriber struct { + gtids []string + ddls []string + timestamps []int64 +} + +type fakeVstreamer struct { + called bool + events [][]*binlogdatapb.VEvent +} + +type fakeEnv struct{} + +func (f *fakeEnv) CheckMySQL() { +} + +func (f *fakeEnv) Config() *tabletenv.TabletConfig { + panic("implement me") +} + +func (f *fakeEnv) DBConfigs() *dbconfigs.DBConfigs { + panic("implement me") +} + +func (f *fakeEnv) Exporter() *servenv.Exporter { + panic("implement me") +} + +func (f *fakeEnv) Stats() *tabletenv.Stats { + panic("implement me") +} + +func (f *fakeEnv) LogError() { +} + +func (f *fakeVstreamer) Stream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { + f.called = true + for _, events := range f.events { + err := send(events) + if err != nil { + return err + } + } + return nil +} From 12c3da6f517eb8ea7ee96c08e328b8656a8be9ac Mon Sep 17 00:00:00 2001 From: Andres Taylor Date: Tue, 28 Apr 2020 13:10:07 +0200 Subject: [PATCH 02/17] wip Signed-off-by: Rohit Nayak Signed-off-by: Rohit Nayak --- go/vt/binlog/binlogplayer/binlog_player.go | 26 ++++-- .../tabletmanager/vreplication/engine.go | 4 +- .../tabletmanager/vreplication/engine_test.go | 1 + .../vttablet/tabletserver/schema_historian.go | 82 +++++++++++++++++++ .../tabletserver/schema_historian_test.go | 30 +++++++ .../tabletserver/vstreamer/vstreamer.go | 4 +- proto/vstreamer.proto | 17 ++++ 7 files changed, 152 insertions(+), 12 deletions(-) create mode 100644 go/vt/vttablet/tabletserver/schema_historian.go create mode 100644 go/vt/vttablet/tabletserver/schema_historian_test.go create mode 100644 proto/vstreamer.proto diff --git a/go/vt/binlog/binlogplayer/binlog_player.go b/go/vt/binlog/binlogplayer/binlog_player.go index 3e3b47d8c96..9c603ebae77 100644 --- a/go/vt/binlog/binlogplayer/binlog_player.go +++ b/go/vt/binlog/binlogplayer/binlog_player.go @@ -492,11 +492,10 @@ func (blp *BinlogPlayer) setVReplicationState(state, message string) error { // transaction_timestamp: timestamp of the transaction (from the master). // 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 ( +var CreateVtReplication = []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, @@ -513,12 +512,23 @@ func CreateVReplicationTable() []string { db_name VARBINARY(255) NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB`, - } } // AlterVReplicationTable adds new columns to vreplication table -func AlterVReplicationTable() []string { - return []string{"ALTER TABLE _vt.vreplication ADD COLUMN db_name VARBINARY(255) NOT NULL"} +var AlterVReplicationTable = []string{ + "ALTER TABLE _vt.vreplication ADD COLUMN db_name VARBINARY(255) NOT NULL", +} + +var CreateVersionTable = []string{ + "CREATE DATABASE IF NOT EXISTS _vt", + `CREATE TABLE IF NOT EXISTS _vt.schema_tracking ( + id INT AUTO_INCREMENT, + pos VARBINARY(10000) NOT NULL, + time_updated BIGINT(20) NOT NULL, + ddl VARBINARY(1000) DEFAULT NULL, + schema LONGBLOB(10000) NOT NULL, + PRIMARY KEY (id) + ) ENGINE=InnoDB`, } // VRSettings contains the settings of a vreplication table. diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine.go b/go/vt/vttablet/tabletmanager/vreplication/engine.go index e5ca360ed4d..489bbe5b4b5 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine.go @@ -175,7 +175,7 @@ func (vre *Engine) executeFetchMaybeCreateTable(dbClient binlogplayer.DBClient, log.Info("Looks like the vreplication tables may not exist. Trying to recreate... ") if merr.Num == mysql.ERNoSuchTable || merr.Num == mysql.ERBadDb { - for _, query := range binlogplayer.CreateVReplicationTable() { + for _, query := range binlogplayer.CreateVtReplication { if _, merr := dbClient.ExecuteFetch(query, 0); merr != nil { log.Warningf("Failed to ensure %s exists: %v", vreplicationTableName, merr) return nil, err @@ -188,7 +188,7 @@ func (vre *Engine) executeFetchMaybeCreateTable(dbClient binlogplayer.DBClient, } if merr.Num == mysql.ERBadFieldError { log.Infof("Adding column to table %s", vreplicationTableName) - for _, query := range binlogplayer.AlterVReplicationTable() { + for _, query := range binlogplayer.AlterVReplicationTable { if _, merr := dbClient.ExecuteFetch(query, 0); merr != nil { merr, isSQLErr := err.(*mysql.SQLError) if !isSQLErr || !(merr.Num == mysql.ERDupFieldName) { diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine_test.go b/go/vt/vttablet/tabletmanager/vreplication/engine_test.go index feccaa71b3f..2e78b93e4c4 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine_test.go @@ -450,6 +450,7 @@ func TestCreateDBAndTable(t *testing.T) { dbClient.ExpectRequest("CREATE DATABASE IF NOT EXISTS _vt", &sqltypes.Result{}, nil) dbClient.ExpectRequest("DROP TABLE IF EXISTS _vt.blp_checkpoint", &sqltypes.Result{}, nil) dbClient.ExpectRequestRE("CREATE TABLE IF NOT EXISTS _vt.vreplication.*", &sqltypes.Result{}, nil) + dbClient.ExpectRequestRE("CREATE TABLE IF NOT EXISTS _vt.schema_version.*", &sqltypes.Result{}, nil) dbClient.ExpectRequestRE("create table if not exists _vt.resharding_journal.*", &sqltypes.Result{}, nil) dbClient.ExpectRequest("use _vt", &sqltypes.Result{}, nil) diff --git a/go/vt/vttablet/tabletserver/schema_historian.go b/go/vt/vttablet/tabletserver/schema_historian.go new file mode 100644 index 00000000000..01e7aaf6c5c --- /dev/null +++ b/go/vt/vttablet/tabletserver/schema_historian.go @@ -0,0 +1,82 @@ +/* +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 tabletserver + +import ( + "bytes" + "context" + "encoding/gob" + "fmt" + "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" + "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" +) + +type schemaEngine interface { + Reload(ctx context.Context) error + GetSchema() map[string]*schema.Table +} + +type schemaTracker struct { + engine schemaEngine + conn *connpool.Pool +} + +func (s *schemaTracker) SchemaUpdated(gtid string, ddl string, timestamp int64) { + ctx := context.Background() + + s.engine.Reload(ctx) + //newSchema := s.engine.GetSchema() + //conn, err := s.conn.Get(ctx) + //if err != nil { + // panic(err) + //} + + //for name, table := range newSchema { + // + // //td := tabletmanagerdatapb.TableDefinition{ + // // Name: name, + // // Fields: table.Fields, + // //} + // + //} +} + +var _ SchemaSubscriber = (*schemaTracker)(nil) + +// go binary encoder +func ToGOB64(m map[string]*schema.Table) []byte { + b := bytes.Buffer{} + e := gob.NewEncoder(&b) + err := e.Encode(m) + if err != nil { + fmt.Println(`failed gob Encode`, err) + } + return b.Bytes() +} + +// go binary decoder +func FromGOB64(bites []byte) map[string]*schema.Table { + m := map[string]*schema.Table{} + b := bytes.Buffer{} + b.Write(bites) + d := gob.NewDecoder(&b) + err := d.Decode(&m) + if err != nil { + fmt.Println(`failed gob Decode`, err) + } + return m +} diff --git a/go/vt/vttablet/tabletserver/schema_historian_test.go b/go/vt/vttablet/tabletserver/schema_historian_test.go new file mode 100644 index 00000000000..c9c700b5158 --- /dev/null +++ b/go/vt/vttablet/tabletserver/schema_historian_test.go @@ -0,0 +1,30 @@ + +package tabletserver + +import ( + "fmt" + "testing" + querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" +) + +func TestSerialization(t *testing.T) { + m := map[string]*schema.Table{ + "foo":&schema.Table{ + Name: sqlparser.NewTableIdent("oh noes"), + Fields: []*querypb.Field{{ + Name: "column", + Type: querypb.Type_VARCHAR, + }}, + PKColumns: nil, + Type: 0, + SequenceInfo: nil, + MessageInfo: nil, + }, + } + + b := ToGOB64(m) + newMap := FromGOB64(b) + fmt.Printf("%v", newMap) +} \ No newline at end of file diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index afe047fb3bf..3e75fed8d6e 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -562,7 +562,7 @@ func (vs *vstreamer) buildJournalPlan(id uint64, tm *mysql.TableMap) error { } func (vs *vstreamer) buildTablePlan(id uint64, tm *mysql.TableMap) (*binlogdatapb.VEvent, error) { - cols, err := vs.buildTableColumns(id, tm) + cols, err := vs.buildTableColumns(tm) if err != nil { return nil, err } @@ -592,7 +592,7 @@ func (vs *vstreamer) buildTablePlan(id uint64, tm *mysql.TableMap) (*binlogdatap }, nil } -func (vs *vstreamer) buildTableColumns(id uint64, tm *mysql.TableMap) ([]*querypb.Field, error) { +func (vs *vstreamer) buildTableColumns(tm *mysql.TableMap) ([]*querypb.Field, error) { var fields []*querypb.Field for i, typ := range tm.Types { t, err := sqltypes.MySQLToType(int64(typ), 0) diff --git a/proto/vstreamer.proto b/proto/vstreamer.proto new file mode 100644 index 00000000000..d713f890ad2 --- /dev/null +++ b/proto/vstreamer.proto @@ -0,0 +1,17 @@ +/* +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. +*/ + +// This file contains the proto types needed for vstreamer serializing \ No newline at end of file From 7e1706d8a43d24f6fa79fbc37392ccaa1ec0dbde Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Thu, 30 Apr 2020 15:33:37 +0200 Subject: [PATCH 03/17] WIP Signed-off-by: Rohit Nayak --- go/vt/binlog/binlogplayer/binlog_player.go | 12 - go/vt/proto/binlogdata/binlogdata.pb.go | 411 +++++++++++++----- .../tabletserver/replication_watcher.go | 11 +- .../tabletserver/replication_watcher_test.go | 10 +- go/vt/vttablet/tabletserver/schema/engine.go | 15 + .../vttablet/tabletserver/schema/historian.go | 53 +++ .../tabletserver/schema/historian_test.go | 17 + go/vt/vttablet/tabletserver/schema/tracker.go | 112 +++++ .../tracker_test.go} | 6 +- .../vttablet/tabletserver/schema_historian.go | 82 ---- go/vt/vttablet/tabletserver/tabletserver.go | 11 +- .../tabletserver/vstreamer/vstreamer.go | 119 ++++- .../tabletserver/vstreamer/vstreamer_test.go | 91 +++- proto/binlogdata.proto | 26 ++ 14 files changed, 737 insertions(+), 239 deletions(-) create mode 100644 go/vt/vttablet/tabletserver/schema/historian.go create mode 100644 go/vt/vttablet/tabletserver/schema/historian_test.go create mode 100644 go/vt/vttablet/tabletserver/schema/tracker.go rename go/vt/vttablet/tabletserver/{schema_historian_test.go => schema/tracker_test.go} (90%) delete mode 100644 go/vt/vttablet/tabletserver/schema_historian.go diff --git a/go/vt/binlog/binlogplayer/binlog_player.go b/go/vt/binlog/binlogplayer/binlog_player.go index 9c603ebae77..92baba07bd7 100644 --- a/go/vt/binlog/binlogplayer/binlog_player.go +++ b/go/vt/binlog/binlogplayer/binlog_player.go @@ -519,18 +519,6 @@ var AlterVReplicationTable = []string{ "ALTER TABLE _vt.vreplication ADD COLUMN db_name VARBINARY(255) NOT NULL", } -var CreateVersionTable = []string{ - "CREATE DATABASE IF NOT EXISTS _vt", - `CREATE TABLE IF NOT EXISTS _vt.schema_tracking ( - id INT AUTO_INCREMENT, - pos VARBINARY(10000) NOT NULL, - time_updated BIGINT(20) NOT NULL, - ddl VARBINARY(1000) DEFAULT NULL, - schema LONGBLOB(10000) NOT NULL, - PRIMARY KEY (id) - ) ENGINE=InnoDB`, -} - // VRSettings contains the settings of a vreplication table. type VRSettings struct { StartPos mysql.Position diff --git a/go/vt/proto/binlogdata/binlogdata.pb.go b/go/vt/proto/binlogdata/binlogdata.pb.go index 564619addf2..eb808fdb43b 100644 --- a/go/vt/proto/binlogdata/binlogdata.pb.go +++ b/go/vt/proto/binlogdata/binlogdata.pb.go @@ -86,6 +86,7 @@ const ( // GTIDs. VEventType_VGTID VEventType = 15 VEventType_JOURNAL VEventType = 16 + VEventType_VERSION VEventType = 17 ) var VEventType_name = map[int32]string{ @@ -106,6 +107,7 @@ var VEventType_name = map[int32]string{ 14: "HEARTBEAT", 15: "VGTID", 16: "JOURNAL", + 17: "VERSION", } var VEventType_value = map[string]int32{ @@ -126,6 +128,7 @@ var VEventType_value = map[string]int32{ "HEARTBEAT": 14, "VGTID": 15, "JOURNAL": 16, + "VERSION": 17, } func (x VEventType) String() string { @@ -1273,6 +1276,8 @@ type VEvent struct { Journal *Journal `protobuf:"bytes,8,opt,name=journal,proto3" json:"journal,omitempty"` // Dml is set if the event type is INSERT, REPLACE, UPDATE or DELETE. Dml string `protobuf:"bytes,9,opt,name=dml,proto3" json:"dml,omitempty"` + // Version is set if the event type is VERSION. + Version *Version `protobuf:"bytes,10,opt,name=version,proto3" json:"version,omitempty"` // CurrentTime specifies the current time when the message was sent. // This can be used to compenssate for clock skew. CurrentTime int64 `protobuf:"varint,20,opt,name=current_time,json=currentTime,proto3" json:"current_time,omitempty"` @@ -1369,6 +1374,13 @@ func (m *VEvent) GetDml() string { return "" } +func (m *VEvent) GetVersion() *Version { + if m != nil { + return m.Version + } + return nil +} + func (m *VEvent) GetCurrentTime() int64 { if m != nil { return m.CurrentTime @@ -1376,6 +1388,168 @@ func (m *VEvent) GetCurrentTime() int64 { return 0 } +type TableMetaData struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Fields []*query.Field `protobuf:"bytes,2,rep,name=fields,proto3" json:"fields,omitempty"` + Pkcolumns []int64 `protobuf:"varint,3,rep,packed,name=pkcolumns,proto3" json:"pkcolumns,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TableMetaData) Reset() { *m = TableMetaData{} } +func (m *TableMetaData) String() string { return proto.CompactTextString(m) } +func (*TableMetaData) ProtoMessage() {} +func (*TableMetaData) Descriptor() ([]byte, []int) { + return fileDescriptor_5fd02bcb2e350dad, []int{17} +} + +func (m *TableMetaData) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TableMetaData.Unmarshal(m, b) +} +func (m *TableMetaData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TableMetaData.Marshal(b, m, deterministic) +} +func (m *TableMetaData) XXX_Merge(src proto.Message) { + xxx_messageInfo_TableMetaData.Merge(m, src) +} +func (m *TableMetaData) XXX_Size() int { + return xxx_messageInfo_TableMetaData.Size(m) +} +func (m *TableMetaData) XXX_DiscardUnknown() { + xxx_messageInfo_TableMetaData.DiscardUnknown(m) +} + +var xxx_messageInfo_TableMetaData proto.InternalMessageInfo + +func (m *TableMetaData) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *TableMetaData) GetFields() []*query.Field { + if m != nil { + return m.Fields + } + return nil +} + +func (m *TableMetaData) GetPkcolumns() []int64 { + if m != nil { + return m.Pkcolumns + } + return nil +} + +type TableMetaDataCollection struct { + Tables []*TableMetaData `protobuf:"bytes,1,rep,name=tables,proto3" json:"tables,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TableMetaDataCollection) Reset() { *m = TableMetaDataCollection{} } +func (m *TableMetaDataCollection) String() string { return proto.CompactTextString(m) } +func (*TableMetaDataCollection) ProtoMessage() {} +func (*TableMetaDataCollection) Descriptor() ([]byte, []int) { + return fileDescriptor_5fd02bcb2e350dad, []int{18} +} + +func (m *TableMetaDataCollection) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_TableMetaDataCollection.Unmarshal(m, b) +} +func (m *TableMetaDataCollection) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_TableMetaDataCollection.Marshal(b, m, deterministic) +} +func (m *TableMetaDataCollection) XXX_Merge(src proto.Message) { + xxx_messageInfo_TableMetaDataCollection.Merge(m, src) +} +func (m *TableMetaDataCollection) XXX_Size() int { + return xxx_messageInfo_TableMetaDataCollection.Size(m) +} +func (m *TableMetaDataCollection) XXX_DiscardUnknown() { + xxx_messageInfo_TableMetaDataCollection.DiscardUnknown(m) +} + +var xxx_messageInfo_TableMetaDataCollection proto.InternalMessageInfo + +func (m *TableMetaDataCollection) GetTables() []*TableMetaData { + if m != nil { + return m.Tables + } + return nil +} + +type Version struct { + // Timestamp is the binlog timestamp in seconds. + // The value should be ignored if 0. + Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + // Gtid position where version was set + Gtid string `protobuf:"bytes,2,opt,name=gtid,proto3" json:"gtid,omitempty"` + // Ddl which caused this version event + Ddl string `protobuf:"bytes,3,opt,name=ddl,proto3" json:"ddl,omitempty"` + // + Tables *TableMetaDataCollection `protobuf:"bytes,4,opt,name=tables,proto3" json:"tables,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Version) Reset() { *m = Version{} } +func (m *Version) String() string { return proto.CompactTextString(m) } +func (*Version) ProtoMessage() {} +func (*Version) Descriptor() ([]byte, []int) { + return fileDescriptor_5fd02bcb2e350dad, []int{19} +} + +func (m *Version) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_Version.Unmarshal(m, b) +} +func (m *Version) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_Version.Marshal(b, m, deterministic) +} +func (m *Version) XXX_Merge(src proto.Message) { + xxx_messageInfo_Version.Merge(m, src) +} +func (m *Version) XXX_Size() int { + return xxx_messageInfo_Version.Size(m) +} +func (m *Version) XXX_DiscardUnknown() { + xxx_messageInfo_Version.DiscardUnknown(m) +} + +var xxx_messageInfo_Version proto.InternalMessageInfo + +func (m *Version) GetTimestamp() int64 { + if m != nil { + return m.Timestamp + } + return 0 +} + +func (m *Version) GetGtid() string { + if m != nil { + return m.Gtid + } + return "" +} + +func (m *Version) GetDdl() string { + if m != nil { + return m.Ddl + } + return "" +} + +func (m *Version) GetTables() *TableMetaDataCollection { + if m != nil { + return m.Tables + } + return nil +} + // VStreamRequest is the payload for VStreamer type VStreamRequest struct { EffectiveCallerId *vtrpc.CallerID `protobuf:"bytes,1,opt,name=effective_caller_id,json=effectiveCallerId,proto3" json:"effective_caller_id,omitempty"` @@ -1392,7 +1566,7 @@ func (m *VStreamRequest) Reset() { *m = VStreamRequest{} } func (m *VStreamRequest) String() string { return proto.CompactTextString(m) } func (*VStreamRequest) ProtoMessage() {} func (*VStreamRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5fd02bcb2e350dad, []int{17} + return fileDescriptor_5fd02bcb2e350dad, []int{20} } func (m *VStreamRequest) XXX_Unmarshal(b []byte) error { @@ -1460,7 +1634,7 @@ func (m *VStreamResponse) Reset() { *m = VStreamResponse{} } func (m *VStreamResponse) String() string { return proto.CompactTextString(m) } func (*VStreamResponse) ProtoMessage() {} func (*VStreamResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5fd02bcb2e350dad, []int{18} + return fileDescriptor_5fd02bcb2e350dad, []int{21} } func (m *VStreamResponse) XXX_Unmarshal(b []byte) error { @@ -1504,7 +1678,7 @@ func (m *VStreamRowsRequest) Reset() { *m = VStreamRowsRequest{} } func (m *VStreamRowsRequest) String() string { return proto.CompactTextString(m) } func (*VStreamRowsRequest) ProtoMessage() {} func (*VStreamRowsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5fd02bcb2e350dad, []int{19} + return fileDescriptor_5fd02bcb2e350dad, []int{22} } func (m *VStreamRowsRequest) XXX_Unmarshal(b []byte) error { @@ -1576,7 +1750,7 @@ func (m *VStreamRowsResponse) Reset() { *m = VStreamRowsResponse{} } func (m *VStreamRowsResponse) String() string { return proto.CompactTextString(m) } func (*VStreamRowsResponse) ProtoMessage() {} func (*VStreamRowsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5fd02bcb2e350dad, []int{20} + return fileDescriptor_5fd02bcb2e350dad, []int{23} } func (m *VStreamRowsResponse) XXX_Unmarshal(b []byte) error { @@ -1634,6 +1808,7 @@ func (m *VStreamRowsResponse) GetLastpk() *query.Row { // VStreamResultsRequest is the payload for VStreamResults // The ids match VStreamRows, in case we decide to merge the two. +// The ids match VStreamRows, in case we decide to merge the two. type VStreamResultsRequest struct { EffectiveCallerId *vtrpc.CallerID `protobuf:"bytes,1,opt,name=effective_caller_id,json=effectiveCallerId,proto3" json:"effective_caller_id,omitempty"` ImmediateCallerId *query.VTGateCallerID `protobuf:"bytes,2,opt,name=immediate_caller_id,json=immediateCallerId,proto3" json:"immediate_caller_id,omitempty"` @@ -1648,7 +1823,7 @@ func (m *VStreamResultsRequest) Reset() { *m = VStreamResultsRequest{} } func (m *VStreamResultsRequest) String() string { return proto.CompactTextString(m) } func (*VStreamResultsRequest) ProtoMessage() {} func (*VStreamResultsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5fd02bcb2e350dad, []int{21} + return fileDescriptor_5fd02bcb2e350dad, []int{24} } func (m *VStreamResultsRequest) XXX_Unmarshal(b []byte) error { @@ -1712,7 +1887,7 @@ func (m *VStreamResultsResponse) Reset() { *m = VStreamResultsResponse{} func (m *VStreamResultsResponse) String() string { return proto.CompactTextString(m) } func (*VStreamResultsResponse) ProtoMessage() {} func (*VStreamResultsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5fd02bcb2e350dad, []int{22} + return fileDescriptor_5fd02bcb2e350dad, []int{25} } func (m *VStreamResultsResponse) XXX_Unmarshal(b []byte) error { @@ -1778,6 +1953,9 @@ func init() { proto.RegisterType((*KeyspaceShard)(nil), "binlogdata.KeyspaceShard") proto.RegisterType((*Journal)(nil), "binlogdata.Journal") proto.RegisterType((*VEvent)(nil), "binlogdata.VEvent") + proto.RegisterType((*TableMetaData)(nil), "binlogdata.TableMetaData") + proto.RegisterType((*TableMetaDataCollection)(nil), "binlogdata.TableMetaDataCollection") + proto.RegisterType((*Version)(nil), "binlogdata.Version") proto.RegisterType((*VStreamRequest)(nil), "binlogdata.VStreamRequest") proto.RegisterType((*VStreamResponse)(nil), "binlogdata.VStreamResponse") proto.RegisterType((*VStreamRowsRequest)(nil), "binlogdata.VStreamRowsRequest") @@ -1789,112 +1967,119 @@ func init() { func init() { proto.RegisterFile("binlogdata.proto", fileDescriptor_5fd02bcb2e350dad) } var fileDescriptor_5fd02bcb2e350dad = []byte{ - // 1709 bytes of a gzipped FileDescriptorProto + // 1821 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x58, 0x4b, 0x73, 0x23, 0x49, - 0x11, 0x9e, 0xd6, 0x5b, 0xd9, 0xb6, 0xdc, 0x2e, 0x3f, 0x10, 0x13, 0x2c, 0xe1, 0xed, 0x60, 0x76, - 0xbc, 0x8e, 0x40, 0x06, 0x01, 0xc3, 0x69, 0x59, 0xf4, 0x68, 0x7b, 0x34, 0xd3, 0x92, 0x3c, 0xa5, - 0x1e, 0x0f, 0xb1, 0x97, 0x8e, 0xb6, 0x54, 0xf6, 0x34, 0xee, 0xd7, 0x74, 0x97, 0xec, 0xd5, 0x0f, - 0x20, 0xf8, 0x01, 0xfc, 0x0a, 0xce, 0x5c, 0xe1, 0xca, 0x9d, 0x3b, 0x57, 0x4e, 0x9c, 0xf8, 0x07, - 0x44, 0x3d, 0xba, 0xd5, 0xad, 0x59, 0x76, 0x3c, 0x1b, 0xc1, 0x01, 0x2e, 0x8a, 0xac, 0xec, 0xcc, - 0xac, 0xcc, 0x2f, 0x1f, 0x55, 0x25, 0xd0, 0xae, 0xdc, 0xc0, 0x0b, 0x6f, 0x16, 0x0e, 0x75, 0x3a, - 0x51, 0x1c, 0xd2, 0x10, 0xc1, 0x9a, 0xf3, 0x58, 0xbd, 0xa3, 0x71, 0x34, 0x17, 0x1f, 0x1e, 0xab, - 0xef, 0x96, 0x24, 0x5e, 0xc9, 0x45, 0x8b, 0x86, 0x51, 0xb8, 0xd6, 0xd2, 0xc7, 0x50, 0x1f, 0xbc, - 0x75, 0xe2, 0x84, 0x50, 0x74, 0x08, 0xb5, 0xb9, 0xe7, 0x92, 0x80, 0xb6, 0x95, 0x23, 0xe5, 0xb8, - 0x8a, 0xe5, 0x0a, 0x21, 0xa8, 0xcc, 0xc3, 0x20, 0x68, 0x97, 0x38, 0x97, 0xd3, 0x4c, 0x36, 0x21, - 0xf1, 0x1d, 0x89, 0xdb, 0x65, 0x21, 0x2b, 0x56, 0xfa, 0x3f, 0xca, 0xb0, 0xdb, 0xe7, 0x7e, 0x58, - 0xb1, 0x13, 0x24, 0xce, 0x9c, 0xba, 0x61, 0x80, 0xce, 0x01, 0x12, 0xea, 0x50, 0xe2, 0x93, 0x80, - 0x26, 0x6d, 0xe5, 0xa8, 0x7c, 0xac, 0x76, 0x9f, 0x76, 0x72, 0x11, 0xbc, 0xa7, 0xd2, 0x99, 0xa5, - 0xf2, 0x38, 0xa7, 0x8a, 0xba, 0xa0, 0x92, 0x3b, 0x12, 0x50, 0x9b, 0x86, 0xb7, 0x24, 0x68, 0x57, - 0x8e, 0x94, 0x63, 0xb5, 0xbb, 0xdb, 0x11, 0x01, 0x1a, 0xec, 0x8b, 0xc5, 0x3e, 0x60, 0x20, 0x19, - 0xfd, 0xf8, 0xaf, 0x25, 0x68, 0x66, 0xd6, 0x90, 0x09, 0x8d, 0xb9, 0x43, 0xc9, 0x4d, 0x18, 0xaf, - 0x78, 0x98, 0xad, 0xee, 0x4f, 0x1e, 0xe8, 0x48, 0x67, 0x20, 0xf5, 0x70, 0x66, 0x01, 0xfd, 0x18, - 0xea, 0x73, 0x81, 0x1e, 0x47, 0x47, 0xed, 0xee, 0xe5, 0x8d, 0x49, 0x60, 0x71, 0x2a, 0x83, 0x34, - 0x28, 0x27, 0xef, 0x3c, 0x0e, 0xd9, 0x16, 0x66, 0xa4, 0xfe, 0x47, 0x05, 0x1a, 0xa9, 0x5d, 0xb4, - 0x07, 0x3b, 0x7d, 0xd3, 0x7e, 0x3d, 0xc1, 0xc6, 0x60, 0x7a, 0x3e, 0x19, 0x7d, 0x65, 0x0c, 0xb5, - 0x47, 0x68, 0x0b, 0x1a, 0x7d, 0xd3, 0xee, 0x1b, 0xe7, 0xa3, 0x89, 0xa6, 0xa0, 0x6d, 0x68, 0xf6, - 0x4d, 0x7b, 0x30, 0x1d, 0x8f, 0x47, 0x96, 0x56, 0x42, 0x3b, 0xa0, 0xf6, 0x4d, 0x1b, 0x4f, 0x4d, - 0xb3, 0xdf, 0x1b, 0xbc, 0xd4, 0xca, 0xe8, 0x00, 0x76, 0xfb, 0xa6, 0x3d, 0x1c, 0x9b, 0xf6, 0xd0, - 0xb8, 0xc0, 0xc6, 0xa0, 0x67, 0x19, 0x43, 0xad, 0x82, 0x00, 0x6a, 0x8c, 0x3d, 0x34, 0xb5, 0xaa, - 0xa4, 0x67, 0x86, 0xa5, 0xd5, 0xa4, 0xb9, 0xd1, 0x64, 0x66, 0x60, 0x4b, 0xab, 0xcb, 0xe5, 0xeb, - 0x8b, 0x61, 0xcf, 0x32, 0xb4, 0x86, 0x5c, 0x0e, 0x0d, 0xd3, 0xb0, 0x0c, 0xad, 0xf9, 0xa2, 0xd2, - 0x28, 0x69, 0xe5, 0x17, 0x95, 0x46, 0x59, 0xab, 0xe8, 0x7f, 0x50, 0xe0, 0x60, 0x46, 0x63, 0xe2, - 0xf8, 0x2f, 0xc9, 0x0a, 0x3b, 0xc1, 0x0d, 0xc1, 0xe4, 0xdd, 0x92, 0x24, 0x14, 0x3d, 0x86, 0x46, - 0x14, 0x26, 0x2e, 0xc3, 0x8e, 0x03, 0xdc, 0xc4, 0xd9, 0x1a, 0x9d, 0x42, 0xf3, 0x96, 0xac, 0xec, - 0x98, 0xc9, 0x4b, 0xc0, 0x50, 0x27, 0x2b, 0xc8, 0xcc, 0x52, 0xe3, 0x56, 0x52, 0x79, 0x7c, 0xcb, - 0x1f, 0xc6, 0x57, 0xbf, 0x86, 0xc3, 0x4d, 0xa7, 0x92, 0x28, 0x0c, 0x12, 0x82, 0x4c, 0x40, 0x42, - 0xd1, 0xa6, 0xeb, 0xdc, 0x72, 0xff, 0xd4, 0xee, 0x27, 0xdf, 0x5a, 0x00, 0x78, 0xf7, 0x6a, 0x93, - 0xa5, 0x7f, 0x0d, 0x7b, 0x62, 0x1f, 0xcb, 0xb9, 0xf2, 0x48, 0xf2, 0x90, 0xd0, 0x0f, 0xa1, 0x46, - 0xb9, 0x70, 0xbb, 0x74, 0x54, 0x3e, 0x6e, 0x62, 0xb9, 0xfa, 0xd8, 0x08, 0x17, 0xb0, 0x5f, 0xdc, - 0xf9, 0xbf, 0x12, 0xdf, 0xcf, 0xa1, 0x82, 0x97, 0x1e, 0x41, 0xfb, 0x50, 0xf5, 0x1d, 0x3a, 0x7f, - 0x2b, 0xa3, 0x11, 0x0b, 0x16, 0xca, 0xb5, 0xeb, 0x51, 0x12, 0xf3, 0x14, 0x36, 0xb1, 0x5c, 0xe9, - 0x7f, 0x52, 0xa0, 0x76, 0xc6, 0x49, 0xf4, 0x19, 0x54, 0xe3, 0x25, 0x0b, 0x56, 0xf4, 0xba, 0x96, - 0xf7, 0x80, 0x59, 0xc6, 0xe2, 0x33, 0x1a, 0x41, 0xeb, 0xda, 0x25, 0xde, 0x82, 0xb7, 0xee, 0x38, - 0x5c, 0x88, 0xaa, 0x68, 0x75, 0x3f, 0xcd, 0x2b, 0x08, 0x9b, 0x9d, 0xb3, 0x82, 0x20, 0xde, 0x50, - 0xd4, 0x9f, 0x41, 0xab, 0x28, 0xc1, 0xda, 0xc9, 0xc0, 0xd8, 0x9e, 0x4e, 0xec, 0xf1, 0x68, 0x36, - 0xee, 0x59, 0x83, 0xe7, 0xda, 0x23, 0xde, 0x31, 0xc6, 0xcc, 0xb2, 0x8d, 0xb3, 0xb3, 0x29, 0xb6, - 0x34, 0x45, 0xff, 0x67, 0x09, 0xb6, 0x04, 0x28, 0xb3, 0x70, 0x19, 0xcf, 0x09, 0xcb, 0xe2, 0x2d, - 0x59, 0x25, 0x91, 0x33, 0x27, 0x69, 0x16, 0xd3, 0x35, 0x03, 0x24, 0x79, 0xeb, 0xc4, 0x0b, 0x19, - 0xb9, 0x58, 0xa0, 0x5f, 0x80, 0xca, 0xb3, 0x49, 0x6d, 0xba, 0x8a, 0x08, 0xcf, 0x63, 0xab, 0xbb, - 0xbf, 0x2e, 0x6c, 0x9e, 0x2b, 0x6a, 0xad, 0x22, 0x82, 0x81, 0x66, 0x74, 0xb1, 0x1b, 0x2a, 0x0f, - 0xe8, 0x86, 0x75, 0x0d, 0x55, 0x0b, 0x35, 0x74, 0x92, 0x25, 0xa4, 0x26, 0xad, 0xbc, 0x87, 0x5e, - 0x9a, 0x24, 0xd4, 0x81, 0x5a, 0x18, 0xd8, 0x8b, 0x85, 0xd7, 0xae, 0x73, 0x37, 0xbf, 0x97, 0x97, - 0x9d, 0x06, 0xc3, 0xa1, 0xd9, 0x13, 0x65, 0x51, 0x0d, 0x83, 0xe1, 0xc2, 0x43, 0x4f, 0xa0, 0x45, - 0xbe, 0xa6, 0x24, 0x0e, 0x1c, 0xcf, 0xf6, 0x57, 0x6c, 0x7a, 0x35, 0x78, 0xe8, 0xdb, 0x29, 0x77, - 0xcc, 0x98, 0xe8, 0x33, 0xd8, 0x49, 0x68, 0x18, 0xd9, 0xce, 0x35, 0x25, 0xb1, 0x3d, 0x0f, 0xa3, - 0x55, 0xbb, 0x79, 0xa4, 0x1c, 0x37, 0xf0, 0x36, 0x63, 0xf7, 0x18, 0x77, 0x10, 0x46, 0x2b, 0xfd, - 0x15, 0x34, 0x71, 0x78, 0x3f, 0x78, 0xcb, 0xe3, 0xd1, 0xa1, 0x76, 0x45, 0xae, 0xc3, 0x98, 0xc8, - 0x42, 0x05, 0x39, 0xc8, 0x71, 0x78, 0x8f, 0xe5, 0x17, 0x74, 0x04, 0x55, 0x6e, 0x53, 0x8e, 0x8b, - 0xbc, 0x88, 0xf8, 0xa0, 0x3b, 0xd0, 0xc0, 0xe1, 0x3d, 0x4f, 0x3b, 0xfa, 0x04, 0x04, 0xc0, 0x76, - 0xe0, 0xf8, 0x69, 0xf6, 0x9a, 0x9c, 0x33, 0x71, 0x7c, 0x82, 0x9e, 0x81, 0x1a, 0x87, 0xf7, 0xf6, - 0x9c, 0x6f, 0x2f, 0x3a, 0x51, 0xed, 0x1e, 0x14, 0x8a, 0x33, 0x75, 0x0e, 0x43, 0x9c, 0x92, 0x89, - 0xfe, 0x0a, 0x60, 0x5d, 0x5b, 0x1f, 0xda, 0xe4, 0x47, 0x2c, 0x1b, 0xc4, 0x5b, 0xa4, 0xf6, 0xb7, - 0xa4, 0xcb, 0xdc, 0x02, 0x96, 0xdf, 0x18, 0x10, 0x33, 0x56, 0x3c, 0xe7, 0xd4, 0x5d, 0x7c, 0x87, - 0x92, 0x43, 0x50, 0xb9, 0xa1, 0xee, 0x82, 0xd7, 0x5a, 0x13, 0x73, 0x5a, 0xff, 0x12, 0xaa, 0x97, - 0xdc, 0xdc, 0x33, 0x50, 0xb9, 0x94, 0xcd, 0xd8, 0x69, 0x0f, 0x16, 0xc2, 0xcc, 0xb6, 0xc6, 0x90, - 0xa4, 0x64, 0xa2, 0xf7, 0x60, 0xfb, 0xa5, 0xdc, 0x96, 0x0b, 0x7c, 0xbc, 0x5f, 0xfa, 0x9f, 0x4b, - 0x50, 0x7f, 0x11, 0x2e, 0x59, 0x61, 0xa0, 0x16, 0x94, 0xdc, 0x05, 0xd7, 0x2b, 0xe3, 0x92, 0xbb, - 0x40, 0xbf, 0x86, 0x96, 0xef, 0xde, 0xc4, 0x0e, 0x2b, 0x2f, 0xd1, 0x29, 0xa2, 0xd9, 0xbf, 0x9f, - 0xf7, 0x6c, 0x9c, 0x4a, 0xf0, 0x76, 0xd9, 0xf6, 0xf3, 0xcb, 0x5c, 0x03, 0x94, 0x0b, 0x0d, 0xf0, - 0x04, 0x5a, 0x5e, 0x38, 0x77, 0x3c, 0x3b, 0x1b, 0xbf, 0x15, 0x51, 0xa4, 0x9c, 0x7b, 0x91, 0xce, - 0xe0, 0x0d, 0x5c, 0xaa, 0x0f, 0xc4, 0x05, 0x7d, 0x01, 0x5b, 0x91, 0x13, 0x53, 0x77, 0xee, 0x46, - 0x0e, 0xbb, 0xc0, 0xd4, 0xb8, 0x62, 0xc1, 0xed, 0x02, 0x6e, 0xb8, 0x20, 0x8e, 0x3e, 0x07, 0x2d, - 0xe1, 0xa3, 0xc5, 0xbe, 0x0f, 0xe3, 0xdb, 0x6b, 0x2f, 0xbc, 0x4f, 0xda, 0x75, 0xee, 0xff, 0x8e, - 0xe0, 0xbf, 0x49, 0xd9, 0xfa, 0xbf, 0x4a, 0x50, 0xbb, 0x14, 0x55, 0x76, 0x02, 0x15, 0x8e, 0x91, - 0xb8, 0xa4, 0x1c, 0xe6, 0x37, 0x13, 0x12, 0x1c, 0x20, 0x2e, 0x83, 0x7e, 0x00, 0x4d, 0xea, 0xfa, - 0x24, 0xa1, 0x8e, 0x1f, 0x71, 0x50, 0xcb, 0x78, 0xcd, 0xf8, 0xa6, 0x5a, 0x61, 0x37, 0x11, 0x36, - 0x03, 0x04, 0x4c, 0x8c, 0x44, 0x3f, 0x85, 0x26, 0xeb, 0x0d, 0x7e, 0x71, 0x6a, 0x57, 0x79, 0xb3, - 0xed, 0x6f, 0x74, 0x06, 0xdf, 0x16, 0x37, 0xe2, 0xb4, 0xdb, 0x7e, 0x09, 0x2a, 0xaf, 0x66, 0xa9, - 0x24, 0x86, 0xcf, 0x61, 0x71, 0xf8, 0xa4, 0x5d, 0x83, 0x61, 0x3d, 0xaf, 0xd1, 0x53, 0xa8, 0xde, - 0x71, 0x97, 0xea, 0xf2, 0x02, 0x97, 0x0f, 0x8e, 0xc3, 0x2f, 0xbe, 0xb3, 0xd3, 0xf1, 0xb7, 0xa2, - 0x9a, 0xf8, 0xd8, 0xd9, 0x38, 0x1d, 0x65, 0xa1, 0xe1, 0x54, 0x86, 0x47, 0xe5, 0x7b, 0x7c, 0xf2, - 0xb0, 0xa8, 0x7c, 0x0f, 0x7d, 0x0a, 0x5b, 0xf3, 0x65, 0x1c, 0xf3, 0x2b, 0xa3, 0xeb, 0x93, 0xf6, - 0x3e, 0x07, 0x47, 0x95, 0x3c, 0xcb, 0xf5, 0x89, 0xfe, 0xfb, 0x12, 0xb4, 0x2e, 0xc5, 0xa1, 0x9a, - 0x1e, 0xe4, 0x5f, 0xc2, 0x1e, 0xb9, 0xbe, 0x26, 0x73, 0xea, 0xde, 0x11, 0x7b, 0xee, 0x78, 0x1e, - 0x89, 0x6d, 0x59, 0xca, 0x6a, 0x77, 0xa7, 0x23, 0x2e, 0xd7, 0x03, 0xce, 0x1f, 0x0d, 0xf1, 0x6e, - 0x26, 0x2b, 0x59, 0x0b, 0x64, 0xc0, 0x9e, 0xeb, 0xfb, 0x64, 0xe1, 0x3a, 0x34, 0x6f, 0x40, 0xcc, - 0xb0, 0x03, 0x39, 0x10, 0x2e, 0xad, 0x73, 0x87, 0x92, 0xb5, 0x99, 0x4c, 0x23, 0x33, 0xf3, 0x84, - 0xd5, 0x7b, 0x7c, 0x93, 0xdd, 0x0d, 0xb6, 0xa5, 0xa6, 0xc5, 0x99, 0x58, 0x7e, 0x2c, 0xdc, 0x3b, - 0x2a, 0x1b, 0xf7, 0x8e, 0xf5, 0xd9, 0x50, 0xfd, 0xd0, 0xd9, 0xa0, 0x7f, 0x01, 0x3b, 0x19, 0x10, - 0xf2, 0x5e, 0x71, 0x02, 0x35, 0x9e, 0xdc, 0x74, 0x8a, 0xa0, 0xf7, 0xeb, 0x10, 0x4b, 0x09, 0xfd, - 0x77, 0x25, 0x40, 0xa9, 0x7e, 0x78, 0x9f, 0xfc, 0x8f, 0x82, 0xb9, 0x0f, 0x55, 0xce, 0x97, 0x48, - 0x8a, 0x05, 0xc3, 0xc1, 0x73, 0x12, 0x1a, 0xdd, 0x66, 0x30, 0x0a, 0xe5, 0x57, 0xec, 0x17, 0x93, - 0x64, 0xe9, 0x51, 0x2c, 0x25, 0xf4, 0xbf, 0x28, 0xb0, 0x57, 0xc0, 0x41, 0x62, 0xb9, 0x3e, 0x18, - 0x94, 0xff, 0x7c, 0x30, 0xa0, 0x63, 0x68, 0x44, 0xb7, 0xdf, 0x72, 0x80, 0x64, 0x5f, 0xbf, 0xb1, - 0xaf, 0x7f, 0x08, 0x95, 0x98, 0xcd, 0x97, 0x0a, 0xd7, 0xcc, 0x9f, 0x96, 0x9c, 0xcf, 0x8e, 0xdc, - 0x42, 0x1c, 0x85, 0x23, 0x57, 0xfa, 0xff, 0x77, 0x05, 0x0e, 0xd6, 0x75, 0xb0, 0xf4, 0xe8, 0xff, - 0x55, 0x2a, 0xf5, 0x18, 0x0e, 0x37, 0xa3, 0xfb, 0xa8, 0x04, 0x7d, 0x07, 0xd8, 0x4f, 0x7e, 0x05, - 0x6a, 0xee, 0x6e, 0xc5, 0x9e, 0x60, 0xa3, 0xf3, 0xc9, 0x14, 0x1b, 0xda, 0x23, 0xd4, 0x80, 0xca, - 0xcc, 0x9a, 0x5e, 0x68, 0x0a, 0xa3, 0x8c, 0xdf, 0x18, 0x03, 0xf1, 0xac, 0x63, 0x94, 0x2d, 0x85, - 0xca, 0x27, 0x7f, 0x53, 0x00, 0xd6, 0x53, 0x1f, 0xa9, 0x50, 0x7f, 0x3d, 0x79, 0x39, 0x99, 0xbe, - 0x99, 0x08, 0x03, 0xe7, 0xd6, 0x68, 0xa8, 0x29, 0xa8, 0x09, 0x55, 0xf1, 0x4e, 0x2c, 0xb1, 0x1d, - 0xe4, 0x23, 0xb1, 0xcc, 0x5e, 0x90, 0xd9, 0x0b, 0xb1, 0x82, 0xea, 0x50, 0xce, 0xde, 0x81, 0xf2, - 0xe1, 0x57, 0x63, 0x06, 0xb1, 0x71, 0x61, 0xf6, 0x06, 0x86, 0x56, 0x67, 0x1f, 0xb2, 0x27, 0x20, - 0x40, 0x2d, 0x7d, 0xff, 0x31, 0x4d, 0xf6, 0x6a, 0x04, 0xb6, 0xcf, 0xd4, 0x7a, 0x6e, 0x60, 0x4d, - 0x65, 0x3c, 0x3c, 0x7d, 0xa3, 0x6d, 0x31, 0xde, 0xd9, 0xc8, 0x30, 0x87, 0xda, 0x36, 0x7b, 0x36, - 0x3e, 0x37, 0x7a, 0xd8, 0xea, 0x1b, 0x3d, 0x4b, 0x6b, 0xb1, 0x2f, 0x97, 0xdc, 0xc1, 0x1d, 0xb6, - 0xcd, 0x8b, 0xe9, 0x6b, 0x3c, 0xe9, 0x99, 0x9a, 0x76, 0xf2, 0x14, 0xb6, 0x0b, 0x87, 0x3d, 0xdb, - 0xcb, 0xea, 0xf5, 0x4d, 0x63, 0xa6, 0x3d, 0x62, 0xf4, 0xec, 0x79, 0x0f, 0x0f, 0x67, 0x9a, 0xd2, - 0xff, 0xfc, 0xab, 0xa7, 0x77, 0x2e, 0x25, 0x49, 0xd2, 0x71, 0xc3, 0x53, 0x41, 0x9d, 0xde, 0x84, - 0xa7, 0x77, 0xf4, 0x94, 0xff, 0x85, 0x71, 0xba, 0x9e, 0x48, 0x57, 0x35, 0xce, 0xf9, 0xd9, 0xbf, - 0x03, 0x00, 0x00, 0xff, 0xff, 0x78, 0x33, 0x5b, 0xba, 0x1e, 0x11, 0x00, 0x00, + 0x11, 0x9e, 0x56, 0xeb, 0x99, 0x6d, 0xc9, 0xed, 0xf2, 0x63, 0xc5, 0x04, 0x4b, 0x78, 0x1b, 0x66, + 0xc7, 0xeb, 0x08, 0x64, 0x56, 0xc0, 0x70, 0x20, 0x96, 0x45, 0x8f, 0xb6, 0x47, 0x33, 0x2d, 0xc9, + 0x53, 0xea, 0xf1, 0x10, 0x7b, 0xe9, 0x68, 0x4b, 0x65, 0xbb, 0x71, 0x4b, 0xad, 0xe9, 0x2e, 0xd9, + 0xab, 0x1f, 0x40, 0x10, 0xc1, 0x95, 0x3f, 0xc0, 0x95, 0x33, 0x57, 0xb8, 0xf2, 0x2b, 0x08, 0x6e, + 0x9c, 0xf8, 0x13, 0x44, 0x3d, 0xfa, 0x65, 0xef, 0xce, 0x78, 0x36, 0x82, 0x03, 0x5c, 0x14, 0x55, + 0x59, 0x99, 0x59, 0x99, 0x5f, 0x3e, 0x3a, 0x4b, 0xa0, 0x9f, 0x7b, 0x0b, 0x3f, 0xb8, 0x9c, 0xb9, + 0xd4, 0x6d, 0x2d, 0xc3, 0x80, 0x06, 0x08, 0x52, 0xca, 0x63, 0xed, 0x86, 0x86, 0xcb, 0xa9, 0x38, + 0x78, 0xac, 0xbd, 0x5d, 0x91, 0x70, 0x2d, 0x37, 0x0d, 0x1a, 0x2c, 0x83, 0x54, 0xca, 0x18, 0x42, + 0xa5, 0x77, 0xe5, 0x86, 0x11, 0xa1, 0x68, 0x0f, 0xca, 0x53, 0xdf, 0x23, 0x0b, 0xda, 0x54, 0xf6, + 0x95, 0x83, 0x12, 0x96, 0x3b, 0x84, 0xa0, 0x38, 0x0d, 0x16, 0x8b, 0x66, 0x81, 0x53, 0xf9, 0x9a, + 0xf1, 0x46, 0x24, 0xbc, 0x21, 0x61, 0x53, 0x15, 0xbc, 0x62, 0x67, 0xfc, 0x4b, 0x85, 0xad, 0x2e, + 0xb7, 0xc3, 0x0e, 0xdd, 0x45, 0xe4, 0x4e, 0xa9, 0x17, 0x2c, 0xd0, 0x09, 0x40, 0x44, 0x5d, 0x4a, + 0xe6, 0x64, 0x41, 0xa3, 0xa6, 0xb2, 0xaf, 0x1e, 0x68, 0xed, 0xa7, 0xad, 0x8c, 0x07, 0xf7, 0x44, + 0x5a, 0x93, 0x98, 0x1f, 0x67, 0x44, 0x51, 0x1b, 0x34, 0x72, 0x43, 0x16, 0xd4, 0xa1, 0xc1, 0x35, + 0x59, 0x34, 0x8b, 0xfb, 0xca, 0x81, 0xd6, 0xde, 0x6a, 0x09, 0x07, 0x4d, 0x76, 0x62, 0xb3, 0x03, + 0x0c, 0x24, 0x59, 0x3f, 0xfe, 0x7b, 0x01, 0x6a, 0x89, 0x36, 0x64, 0x41, 0x75, 0xea, 0x52, 0x72, + 0x19, 0x84, 0x6b, 0xee, 0x66, 0xa3, 0xfd, 0x93, 0x07, 0x1a, 0xd2, 0xea, 0x49, 0x39, 0x9c, 0x68, + 0x40, 0x3f, 0x86, 0xca, 0x54, 0xa0, 0xc7, 0xd1, 0xd1, 0xda, 0xdb, 0x59, 0x65, 0x12, 0x58, 0x1c, + 0xf3, 0x20, 0x1d, 0xd4, 0xe8, 0xad, 0xcf, 0x21, 0xdb, 0xc0, 0x6c, 0x69, 0xfc, 0x59, 0x81, 0x6a, + 0xac, 0x17, 0x6d, 0xc3, 0x66, 0xd7, 0x72, 0x5e, 0x8f, 0xb0, 0xd9, 0x1b, 0x9f, 0x8c, 0x06, 0x5f, + 0x99, 0x7d, 0xfd, 0x11, 0xda, 0x80, 0x6a, 0xd7, 0x72, 0xba, 0xe6, 0xc9, 0x60, 0xa4, 0x2b, 0xa8, + 0x0e, 0xb5, 0xae, 0xe5, 0xf4, 0xc6, 0xc3, 0xe1, 0xc0, 0xd6, 0x0b, 0x68, 0x13, 0xb4, 0xae, 0xe5, + 0xe0, 0xb1, 0x65, 0x75, 0x3b, 0xbd, 0x97, 0xba, 0x8a, 0x76, 0x61, 0xab, 0x6b, 0x39, 0xfd, 0xa1, + 0xe5, 0xf4, 0xcd, 0x53, 0x6c, 0xf6, 0x3a, 0xb6, 0xd9, 0xd7, 0x8b, 0x08, 0xa0, 0xcc, 0xc8, 0x7d, + 0x4b, 0x2f, 0xc9, 0xf5, 0xc4, 0xb4, 0xf5, 0xb2, 0x54, 0x37, 0x18, 0x4d, 0x4c, 0x6c, 0xeb, 0x15, + 0xb9, 0x7d, 0x7d, 0xda, 0xef, 0xd8, 0xa6, 0x5e, 0x95, 0xdb, 0xbe, 0x69, 0x99, 0xb6, 0xa9, 0xd7, + 0x5e, 0x14, 0xab, 0x05, 0x5d, 0x7d, 0x51, 0xac, 0xaa, 0x7a, 0xd1, 0xf8, 0xa3, 0x02, 0xbb, 0x13, + 0x1a, 0x12, 0x77, 0xfe, 0x92, 0xac, 0xb1, 0xbb, 0xb8, 0x24, 0x98, 0xbc, 0x5d, 0x91, 0x88, 0xa2, + 0xc7, 0x50, 0x5d, 0x06, 0x91, 0xc7, 0xb0, 0xe3, 0x00, 0xd7, 0x70, 0xb2, 0x47, 0x47, 0x50, 0xbb, + 0x26, 0x6b, 0x27, 0x64, 0xfc, 0x12, 0x30, 0xd4, 0x4a, 0x12, 0x32, 0xd1, 0x54, 0xbd, 0x96, 0xab, + 0x2c, 0xbe, 0xea, 0xfb, 0xf1, 0x35, 0x2e, 0x60, 0xef, 0xae, 0x51, 0xd1, 0x32, 0x58, 0x44, 0x04, + 0x59, 0x80, 0x84, 0xa0, 0x43, 0xd3, 0xd8, 0x72, 0xfb, 0xb4, 0xf6, 0xc7, 0xef, 0x4c, 0x00, 0xbc, + 0x75, 0x7e, 0x97, 0x64, 0x7c, 0x0d, 0xdb, 0xe2, 0x1e, 0xdb, 0x3d, 0xf7, 0x49, 0xf4, 0x10, 0xd7, + 0xf7, 0xa0, 0x4c, 0x39, 0x73, 0xb3, 0xb0, 0xaf, 0x1e, 0xd4, 0xb0, 0xdc, 0x7d, 0xa8, 0x87, 0x33, + 0xd8, 0xc9, 0xdf, 0xfc, 0x5f, 0xf1, 0xef, 0x67, 0x50, 0xc4, 0x2b, 0x9f, 0xa0, 0x1d, 0x28, 0xcd, + 0x5d, 0x3a, 0xbd, 0x92, 0xde, 0x88, 0x0d, 0x73, 0xe5, 0xc2, 0xf3, 0x29, 0x09, 0x79, 0x08, 0x6b, + 0x58, 0xee, 0x8c, 0xbf, 0x28, 0x50, 0x3e, 0xe6, 0x4b, 0xf4, 0x29, 0x94, 0xc2, 0x15, 0x73, 0x56, + 0xd4, 0xba, 0x9e, 0xb5, 0x80, 0x69, 0xc6, 0xe2, 0x18, 0x0d, 0xa0, 0x71, 0xe1, 0x11, 0x7f, 0xc6, + 0x4b, 0x77, 0x18, 0xcc, 0x44, 0x56, 0x34, 0xda, 0x9f, 0x64, 0x05, 0x84, 0xce, 0xd6, 0x71, 0x8e, + 0x11, 0xdf, 0x11, 0x34, 0x9e, 0x41, 0x23, 0xcf, 0xc1, 0xca, 0xc9, 0xc4, 0xd8, 0x19, 0x8f, 0x9c, + 0xe1, 0x60, 0x32, 0xec, 0xd8, 0xbd, 0xe7, 0xfa, 0x23, 0x5e, 0x31, 0xe6, 0xc4, 0x76, 0xcc, 0xe3, + 0xe3, 0x31, 0xb6, 0x75, 0xc5, 0xf8, 0x77, 0x01, 0x36, 0x04, 0x28, 0x93, 0x60, 0x15, 0x4e, 0x09, + 0x8b, 0xe2, 0x35, 0x59, 0x47, 0x4b, 0x77, 0x4a, 0xe2, 0x28, 0xc6, 0x7b, 0x06, 0x48, 0x74, 0xe5, + 0x86, 0x33, 0xe9, 0xb9, 0xd8, 0xa0, 0x9f, 0x83, 0xc6, 0xa3, 0x49, 0x1d, 0xba, 0x5e, 0x12, 0x1e, + 0xc7, 0x46, 0x7b, 0x27, 0x4d, 0x6c, 0x1e, 0x2b, 0x6a, 0xaf, 0x97, 0x04, 0x03, 0x4d, 0xd6, 0xf9, + 0x6a, 0x28, 0x3e, 0xa0, 0x1a, 0xd2, 0x1c, 0x2a, 0xe5, 0x72, 0xe8, 0x30, 0x09, 0x48, 0x59, 0x6a, + 0xb9, 0x87, 0x5e, 0x1c, 0x24, 0xd4, 0x82, 0x72, 0xb0, 0x70, 0x66, 0x33, 0xbf, 0x59, 0xe1, 0x66, + 0x7e, 0x94, 0xe5, 0x1d, 0x2f, 0xfa, 0x7d, 0xab, 0x23, 0xd2, 0xa2, 0x14, 0x2c, 0xfa, 0x33, 0x1f, + 0x3d, 0x81, 0x06, 0xf9, 0x9a, 0x92, 0x70, 0xe1, 0xfa, 0xce, 0x7c, 0xcd, 0xba, 0x57, 0x95, 0xbb, + 0x5e, 0x8f, 0xa9, 0x43, 0x46, 0x44, 0x9f, 0xc2, 0x66, 0x44, 0x83, 0xa5, 0xe3, 0x5e, 0x50, 0x12, + 0x3a, 0xd3, 0x60, 0xb9, 0x6e, 0xd6, 0xf6, 0x95, 0x83, 0x2a, 0xae, 0x33, 0x72, 0x87, 0x51, 0x7b, + 0xc1, 0x72, 0x6d, 0xbc, 0x82, 0x1a, 0x0e, 0x6e, 0x7b, 0x57, 0xdc, 0x1f, 0x03, 0xca, 0xe7, 0xe4, + 0x22, 0x08, 0x89, 0x4c, 0x54, 0x90, 0x8d, 0x1c, 0x07, 0xb7, 0x58, 0x9e, 0xa0, 0x7d, 0x28, 0x71, + 0x9d, 0xb2, 0x5d, 0x64, 0x59, 0xc4, 0x81, 0xe1, 0x42, 0x15, 0x07, 0xb7, 0x3c, 0xec, 0xe8, 0x63, + 0x10, 0x00, 0x3b, 0x0b, 0x77, 0x1e, 0x47, 0xaf, 0xc6, 0x29, 0x23, 0x77, 0x4e, 0xd0, 0x33, 0xd0, + 0xc2, 0xe0, 0xd6, 0x99, 0xf2, 0xeb, 0x45, 0x25, 0x6a, 0xed, 0xdd, 0x5c, 0x72, 0xc6, 0xc6, 0x61, + 0x08, 0xe3, 0x65, 0x64, 0xbc, 0x02, 0x48, 0x73, 0xeb, 0x7d, 0x97, 0xfc, 0x88, 0x45, 0x83, 0xf8, + 0xb3, 0x58, 0xff, 0x86, 0x34, 0x99, 0x6b, 0xc0, 0xf2, 0x8c, 0x01, 0x31, 0x61, 0xc9, 0x73, 0x42, + 0xbd, 0xd9, 0x77, 0x48, 0x39, 0x04, 0xc5, 0x4b, 0xea, 0xcd, 0x78, 0xae, 0xd5, 0x30, 0x5f, 0x1b, + 0x5f, 0x42, 0xe9, 0x8c, 0xab, 0x7b, 0x06, 0x1a, 0xe7, 0x72, 0x18, 0x39, 0xae, 0xc1, 0x9c, 0x9b, + 0xc9, 0xd5, 0x18, 0xa2, 0x78, 0x19, 0x19, 0x1d, 0xa8, 0xbf, 0x94, 0xd7, 0x72, 0x86, 0x0f, 0xb7, + 0xcb, 0xf8, 0x6b, 0x01, 0x2a, 0x2f, 0x82, 0x15, 0x4b, 0x0c, 0xd4, 0x80, 0x82, 0x37, 0xe3, 0x72, + 0x2a, 0x2e, 0x78, 0x33, 0xf4, 0x6b, 0x68, 0xcc, 0xbd, 0xcb, 0xd0, 0x65, 0xe9, 0x25, 0x2a, 0x45, + 0x14, 0xfb, 0xf7, 0xb2, 0x96, 0x0d, 0x63, 0x0e, 0x5e, 0x2e, 0xf5, 0x79, 0x76, 0x9b, 0x29, 0x00, + 0x35, 0x57, 0x00, 0x4f, 0xa0, 0xe1, 0x07, 0x53, 0xd7, 0x77, 0x92, 0xf6, 0x5b, 0x14, 0x49, 0xca, + 0xa9, 0xa7, 0x71, 0x0f, 0xbe, 0x83, 0x4b, 0xe9, 0x81, 0xb8, 0xa0, 0x2f, 0x60, 0x63, 0xe9, 0x86, + 0xd4, 0x9b, 0x7a, 0x4b, 0x97, 0x0d, 0x30, 0x65, 0x2e, 0x98, 0x33, 0x3b, 0x87, 0x1b, 0xce, 0xb1, + 0xa3, 0xcf, 0x40, 0x8f, 0x78, 0x6b, 0x71, 0x6e, 0x83, 0xf0, 0xfa, 0xc2, 0x0f, 0x6e, 0xa3, 0x66, + 0x85, 0xdb, 0xbf, 0x29, 0xe8, 0x6f, 0x62, 0xb2, 0xf1, 0x27, 0x15, 0xca, 0x67, 0x22, 0xcb, 0x0e, + 0xa1, 0xc8, 0x31, 0x12, 0x43, 0xca, 0x5e, 0xf6, 0x32, 0xc1, 0xc1, 0x01, 0xe2, 0x3c, 0xe8, 0xfb, + 0x50, 0xa3, 0xde, 0x9c, 0x44, 0xd4, 0x9d, 0x2f, 0x39, 0xa8, 0x2a, 0x4e, 0x09, 0xdf, 0x94, 0x2b, + 0x6c, 0x12, 0x61, 0x3d, 0x40, 0xc0, 0xc4, 0x96, 0xe8, 0x73, 0xa8, 0xb1, 0xda, 0xe0, 0x83, 0x53, + 0xb3, 0xc4, 0x8b, 0x6d, 0xe7, 0x4e, 0x65, 0xf0, 0x6b, 0x71, 0x35, 0x8c, 0xab, 0xed, 0x17, 0xa0, + 0xf1, 0x6c, 0x96, 0x42, 0xa2, 0xf9, 0xec, 0xe5, 0x9b, 0x4f, 0x5c, 0x35, 0x18, 0xd2, 0x7e, 0x8d, + 0x9e, 0x42, 0xe9, 0x86, 0x9b, 0x54, 0x91, 0x03, 0x5c, 0xd6, 0x39, 0x0e, 0xbf, 0x38, 0x67, 0x5f, + 0xc7, 0xdf, 0x8a, 0x6c, 0xe2, 0x6d, 0xe7, 0xce, 0xd7, 0x51, 0x26, 0x1a, 0x8e, 0x79, 0xb8, 0x57, + 0x73, 0x9f, 0x77, 0x1e, 0xe6, 0xd5, 0xdc, 0x67, 0x0a, 0x6e, 0x48, 0x18, 0xb1, 0x94, 0x80, 0xfb, + 0x0a, 0xce, 0xc4, 0x11, 0x8e, 0x79, 0xd0, 0x27, 0xb0, 0x31, 0x5d, 0x85, 0x21, 0x9f, 0x30, 0xbd, + 0x39, 0x69, 0xee, 0x70, 0x2c, 0x35, 0x49, 0xb3, 0xbd, 0x39, 0x31, 0x2e, 0xa1, 0xce, 0xfb, 0xf9, + 0x90, 0x50, 0xb7, 0xef, 0x52, 0x97, 0xc1, 0x9b, 0x69, 0x04, 0x7c, 0xfd, 0xb0, 0x1e, 0xc0, 0xc2, + 0xb6, 0xbc, 0x9e, 0x06, 0xfe, 0x6a, 0xbe, 0x10, 0x19, 0xad, 0xe2, 0x94, 0x60, 0x58, 0xf0, 0x51, + 0xee, 0xa2, 0x5e, 0xe0, 0xfb, 0x44, 0xcc, 0xd3, 0x9f, 0x27, 0x75, 0xa0, 0xdc, 0x4f, 0xc5, 0x9c, + 0x50, 0x5c, 0x22, 0xc6, 0x1f, 0x14, 0xa8, 0x48, 0x77, 0xf3, 0xe9, 0xa2, 0x7c, 0x5b, 0xba, 0x14, + 0xee, 0xa7, 0x8b, 0x9a, 0xa6, 0xcb, 0x2f, 0x13, 0x13, 0xc4, 0x97, 0xeb, 0x87, 0xdf, 0x6a, 0x42, + 0x6a, 0x77, 0x62, 0xcc, 0xef, 0x0b, 0xd0, 0x38, 0x13, 0x73, 0x4c, 0x3c, 0x3b, 0x7d, 0x09, 0xdb, + 0xe4, 0xe2, 0x82, 0xf1, 0xdd, 0x10, 0x67, 0xea, 0xfa, 0x3e, 0x09, 0x1d, 0xd9, 0x3d, 0xb4, 0xf6, + 0x66, 0x4b, 0xbc, 0x67, 0x7a, 0x9c, 0x3e, 0xe8, 0xe3, 0xad, 0x84, 0x57, 0x92, 0x66, 0xc8, 0x84, + 0x6d, 0x6f, 0x3e, 0x27, 0x33, 0xcf, 0xa5, 0x59, 0x05, 0xe2, 0xb3, 0xb1, 0x2b, 0xf1, 0x3f, 0xb3, + 0x4f, 0x5c, 0x4a, 0x52, 0x35, 0x89, 0x44, 0xa2, 0xe6, 0x09, 0xf3, 0x2b, 0xbc, 0x4c, 0xc6, 0xb1, + 0xba, 0x94, 0xb4, 0x39, 0x11, 0xcb, 0xc3, 0xdc, 0xa8, 0x57, 0xbc, 0x33, 0xea, 0xa5, 0x9f, 0xe3, + 0xd2, 0xfb, 0x3e, 0xc7, 0xc6, 0x17, 0xb0, 0x99, 0x00, 0x21, 0x47, 0xb9, 0x43, 0x28, 0xf3, 0x7a, + 0x8a, 0x83, 0x8b, 0xee, 0x97, 0x3e, 0x96, 0x1c, 0xc6, 0xef, 0x0a, 0x80, 0x62, 0xf9, 0xe0, 0x36, + 0xfa, 0x1f, 0x05, 0x73, 0x07, 0x4a, 0x9c, 0x2e, 0x91, 0x14, 0x1b, 0x86, 0x83, 0xef, 0x46, 0x74, + 0x79, 0x9d, 0xc0, 0x28, 0x84, 0x5f, 0xb1, 0x5f, 0x4c, 0xa2, 0x95, 0x4f, 0xb1, 0xe4, 0x30, 0xfe, + 0xa6, 0xc0, 0x76, 0x0e, 0x07, 0x89, 0x65, 0x5a, 0x87, 0xca, 0x3b, 0xea, 0xf0, 0x00, 0xaa, 0xcb, + 0xeb, 0x77, 0xd4, 0x6b, 0x72, 0xfa, 0x8d, 0xad, 0xf4, 0x07, 0x50, 0x0c, 0x59, 0x4b, 0x2f, 0x72, + 0xc9, 0xec, 0x80, 0xc2, 0xe9, 0x6c, 0xca, 0xc9, 0xf9, 0x91, 0x9b, 0x72, 0xa4, 0xfd, 0xff, 0x50, + 0x60, 0x37, 0xcd, 0x83, 0x95, 0x4f, 0xff, 0xaf, 0x42, 0x69, 0x84, 0xb0, 0x77, 0xd7, 0xbb, 0x0f, + 0x0a, 0xd0, 0x77, 0x80, 0xfd, 0xf0, 0x57, 0xa0, 0x65, 0xc6, 0x59, 0xf6, 0xea, 0x1d, 0x9c, 0x8c, + 0xc6, 0xd8, 0xd4, 0x1f, 0xa1, 0x2a, 0x14, 0x27, 0xf6, 0xf8, 0x54, 0x57, 0xd8, 0xca, 0xfc, 0x8d, + 0xd9, 0x13, 0x2f, 0x69, 0xb6, 0x72, 0x24, 0x93, 0x7a, 0xf8, 0x4f, 0x05, 0x20, 0xfd, 0xd0, 0x22, + 0x0d, 0x2a, 0xaf, 0x47, 0x2f, 0x47, 0xe3, 0x37, 0x23, 0xa1, 0xe0, 0xc4, 0x1e, 0xf4, 0x75, 0x05, + 0xd5, 0xa0, 0x24, 0x9e, 0xe6, 0x05, 0x76, 0x83, 0x7c, 0x97, 0xab, 0xec, 0xd1, 0x9e, 0x3c, 0xca, + 0x8b, 0xa8, 0x02, 0x6a, 0xf2, 0xf4, 0x96, 0x6f, 0xed, 0x32, 0x53, 0x88, 0xcd, 0x53, 0xab, 0xd3, + 0x33, 0xf5, 0x0a, 0x3b, 0x48, 0x5e, 0xdd, 0x00, 0xe5, 0xf8, 0xc9, 0xcd, 0x24, 0xd9, 0x43, 0x1d, + 0xd8, 0x3d, 0x63, 0xfb, 0xb9, 0x89, 0x75, 0x8d, 0xd1, 0xf0, 0xf8, 0x8d, 0xbe, 0xc1, 0x68, 0xc7, + 0x03, 0xd3, 0xea, 0xeb, 0x75, 0xf6, 0x52, 0x7f, 0x6e, 0x76, 0xb0, 0xdd, 0x35, 0x3b, 0xb6, 0xde, + 0x60, 0x27, 0x67, 0xdc, 0xc0, 0x4d, 0x76, 0xcd, 0x8b, 0xf1, 0x6b, 0x3c, 0xea, 0x58, 0xba, 0xce, + 0x36, 0x67, 0x26, 0x9e, 0x0c, 0xc6, 0x23, 0x7d, 0xeb, 0xf0, 0x29, 0xd4, 0x73, 0xc3, 0x16, 0xbb, + 0xd8, 0xee, 0x74, 0x2d, 0x73, 0xa2, 0x3f, 0x62, 0xeb, 0xc9, 0xf3, 0x0e, 0xee, 0x4f, 0x74, 0xa5, + 0xfb, 0xd9, 0x57, 0x4f, 0x6f, 0x3c, 0x4a, 0xa2, 0xa8, 0xe5, 0x05, 0x47, 0x62, 0x75, 0x74, 0x19, + 0x1c, 0xdd, 0xd0, 0x23, 0xfe, 0x17, 0xd2, 0x51, 0xda, 0x9e, 0xce, 0xcb, 0x9c, 0xf2, 0xd3, 0xff, + 0x04, 0x00, 0x00, 0xff, 0xff, 0xb2, 0xf5, 0xaa, 0x3a, 0x9e, 0x12, 0x00, 0x00, } diff --git a/go/vt/vttablet/tabletserver/replication_watcher.go b/go/vt/vttablet/tabletserver/replication_watcher.go index 19afcb7cfa8..a1578d94ab8 100644 --- a/go/vt/vttablet/tabletserver/replication_watcher.go +++ b/go/vt/vttablet/tabletserver/replication_watcher.go @@ -20,6 +20,7 @@ import ( "time" "golang.org/x/net/context" + "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -33,11 +34,6 @@ type VStreamer interface { Stream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error } -//SchemaSubscriber will get notified when the schema has been updated -type SchemaSubscriber interface { - SchemaUpdated(gtid string, ddl string, timestamp int64) -} - // ReplicationWatcher is a tabletserver service that watches the // replication stream. It will trigger schema reloads if a DDL // is encountered. @@ -45,17 +41,18 @@ type ReplicationWatcher struct { env tabletenv.Env watchReplication bool vs VStreamer - subscriber SchemaSubscriber + subscriber schema.SchemaSubscriber cancel context.CancelFunc } // NewReplicationWatcher creates a new ReplicationWatcher. -func NewReplicationWatcher(env tabletenv.Env, vs VStreamer, config *tabletenv.TabletConfig) *ReplicationWatcher { +func NewReplicationWatcher(env tabletenv.Env, vs VStreamer, config *tabletenv.TabletConfig, schemaTracker schema.SchemaSubscriber) *ReplicationWatcher { return &ReplicationWatcher{ env: env, vs: vs, watchReplication: config.WatchReplication, + subscriber: schemaTracker, } } diff --git a/go/vt/vttablet/tabletserver/replication_watcher_test.go b/go/vt/vttablet/tabletserver/replication_watcher_test.go index 726e4669941..48f14da83fe 100644 --- a/go/vt/vttablet/tabletserver/replication_watcher_test.go +++ b/go/vt/vttablet/tabletserver/replication_watcher_test.go @@ -20,6 +20,8 @@ import ( "testing" "time" + "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" + "github.com/stretchr/testify/require" "golang.org/x/net/context" "vitess.io/vitess/go/test/utils" @@ -29,13 +31,13 @@ import ( "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) -func (t *testableSubscriber) SchemaUpdated(gtid string, ddl string, timestamp int64) { +func (t *mockSubscriber) SchemaUpdated(gtid string, ddl string, timestamp int64) { t.gtids = append(t.gtids, gtid) t.ddls = append(t.ddls, ddl) t.timestamps = append(t.timestamps, timestamp) } -var _ SchemaSubscriber = (*testableSubscriber)(nil) +var _ schema.SchemaSubscriber = (*mockSubscriber)(nil) var _ VStreamer = (*fakeVstreamer)(nil) var _ tabletenv.Env = (*fakeEnv)(nil) @@ -88,7 +90,7 @@ func TestReplicationWatcher(t *testing.T) { for _, testCase := range testCases { t.Run(testCase.name, func(t *testing.T) { - subscriber := &testableSubscriber{} + subscriber := &mockSubscriber{} streamer := &fakeVstreamer{ events: testCase.input, } @@ -111,7 +113,7 @@ func TestReplicationWatcher(t *testing.T) { } } -type testableSubscriber struct { +type mockSubscriber struct { gtids []string ddls []string timestamps []int64 diff --git a/go/vt/vttablet/tabletserver/schema/engine.go b/go/vt/vttablet/tabletserver/schema/engine.go index 0e9ee129b65..f419714d017 100644 --- a/go/vt/vttablet/tabletserver/schema/engine.go +++ b/go/vt/vttablet/tabletserver/schema/engine.go @@ -63,6 +63,7 @@ type Engine struct { // and do not require locking mu. conns *connpool.Pool ticks *timer.Timer + Sh HistorianInterface } // NewEngine creates a new Engine. @@ -137,6 +138,11 @@ func (se *Engine) IsOpen() bool { return se.isOpen } +// GetConnection returns a connection from the pool +func (se *Engine) GetConnection(ctx context.Context) (*connpool.DBConn, error) { + return se.conns.Get(ctx) +} + // Close shuts down Engine and is idempotent. // It can be re-opened after Close. func (se *Engine) Close() { @@ -339,6 +345,11 @@ func (se *Engine) broadcast(created, altered, dropped []string) { } } +// GetTableForPos returns the info for a table. +func (se *Engine) GetTableForPos(tableName sqlparser.TableIdent, pos string) *Table { + return se.Sh.GetTableForPos(tableName, pos) +} + // GetTable returns the info for a table. func (se *Engine) GetTable(tableName sqlparser.TableIdent) *Table { se.mu.Lock() @@ -405,3 +416,7 @@ func (se *Engine) SetTableForTests(table *Table) { defer se.mu.Unlock() se.tables[table.Name.String()] = table } + +func (se *Engine) SetHistorian(sh *Historian) { + se.Sh = sh +} diff --git a/go/vt/vttablet/tabletserver/schema/historian.go b/go/vt/vttablet/tabletserver/schema/historian.go new file mode 100644 index 00000000000..dcc960f4280 --- /dev/null +++ b/go/vt/vttablet/tabletserver/schema/historian.go @@ -0,0 +1,53 @@ +/* +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 schema + +import ( + "context" + + "vitess.io/vitess/go/vt/proto/binlogdata" + "vitess.io/vitess/go/vt/sqlparser" +) + +type HistorianInterface interface { + RegisterVersionEvent(version *binlogdata.Version) + GetTableForPos(tableName sqlparser.TableIdent, pos string) *Table +} + +type HistoryEngine interface { + Reload(ctx context.Context) error + GetTable(ident sqlparser.TableIdent) *Table +} + +type Historian struct { + se HistoryEngine +} + +//TODO first time called warm cache, drop cache on last +func NewSchemaHistorian(se HistoryEngine) *Historian { + return &Historian{se: se} +} + +//Placeholder TODO, currently returns Table not TableMetaData +func (h *Historian) GetTableForPos(tableName sqlparser.TableIdent, pos string) *Table { + h.se.Reload(context.Background()) + _ = pos + return h.se.GetTable(tableName) +} + +func (h *Historian) RegisterVersionEvent(version *binlogdata.Version) { +} diff --git a/go/vt/vttablet/tabletserver/schema/historian_test.go b/go/vt/vttablet/tabletserver/schema/historian_test.go new file mode 100644 index 00000000000..43b1e49ec28 --- /dev/null +++ b/go/vt/vttablet/tabletserver/schema/historian_test.go @@ -0,0 +1,17 @@ +/* +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 schema diff --git a/go/vt/vttablet/tabletserver/schema/tracker.go b/go/vt/vttablet/tabletserver/schema/tracker.go new file mode 100644 index 00000000000..a422ed3722b --- /dev/null +++ b/go/vt/vttablet/tabletserver/schema/tracker.go @@ -0,0 +1,112 @@ +/* +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 schema + +import ( + "bytes" + "context" + "fmt" + + "github.com/gogo/protobuf/proto" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/log" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + querypb "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" +) + +const createSchemaTrackingTable = `CREATE TABLE IF NOT EXISTS _vt.schema_tracking ( + id INT AUTO_INCREMENT, + pos VARBINARY(10000) NOT NULL, + time_updated BIGINT(20) NOT NULL, + ddl VARBINARY(1000) DEFAULT NULL, + schema longblob NOT NULL, + PRIMARY KEY (id) + ) ENGINE=InnoDB` + +type trackerEngine interface { + GetConnection(ctx context.Context) (*connpool.DBConn, error) + Reload(ctx context.Context) error + GetSchema() map[string]*Table +} + +//SchemaSubscriber will get notified when the schema has been updated +type SchemaSubscriber interface { + SchemaUpdated(gtid string, ddl string, timestamp int64) +} + +var _ SchemaSubscriber = (*Tracker)(nil) + +type Tracker struct { + engine trackerEngine +} + +type TableMetaData struct { + Name string + Fields []*querypb.Field + PKColumns []int +} + +func NewTracker(engine trackerEngine) *Tracker { + return &Tracker{engine: engine} +} + +func (st *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) { + ctx := context.Background() + + st.engine.Reload(ctx) + newSchema := st.engine.GetSchema() + dbSchema := &binlogdatapb.TableMetaDataCollection{ + Tables: []*binlogdatapb.TableMetaData{}, + } + blob, err := proto.Marshal(dbSchema) + for name, table := range newSchema { + t := &binlogdatapb.TableMetaData{ + Name: name, + Fields: table.Fields, + //Pkcolumns: table.PKColumns, + } + dbSchema.Tables = append(dbSchema.Tables, t) + } + query := fmt.Sprintf("insert into _vt.schema_version "+ + "(pos, ddl, schema, time_updated) "+ + "values (%v, %v, %v, %d)", gtid, ddl, encodeString(string(blob)), timestamp) + + conn, err := st.engine.GetConnection(ctx) + if err != nil { + return + } + defer conn.Recycle() + log.Infof("Creating schema tracking table") + _, err = conn.Exec(ctx, createSchemaTrackingTable, 1, false) + if err != nil { + log.Errorf("Error creating schema_tracking table %v", err) + return + } + log.Infof("Inserting version for position %s", gtid) + _, err = conn.Exec(ctx, query, 1, false) + if err != nil { + log.Errorf("Error inserting version for position %s %v", gtid, err) + return + } +} + +func encodeString(in string) string { + buf := bytes.NewBuffer(nil) + sqltypes.NewVarChar(in).EncodeSQL(buf) + return buf.String() +} diff --git a/go/vt/vttablet/tabletserver/schema_historian_test.go b/go/vt/vttablet/tabletserver/schema/tracker_test.go similarity index 90% rename from go/vt/vttablet/tabletserver/schema_historian_test.go rename to go/vt/vttablet/tabletserver/schema/tracker_test.go index c9c700b5158..0cebd1735f4 100644 --- a/go/vt/vttablet/tabletserver/schema_historian_test.go +++ b/go/vt/vttablet/tabletserver/schema/tracker_test.go @@ -1,5 +1,5 @@ -package tabletserver +package schema import ( "fmt" @@ -24,7 +24,7 @@ func TestSerialization(t *testing.T) { }, } - b := ToGOB64(m) - newMap := FromGOB64(b) + b := ToGOB(m) + newMap := FromGOB(b) fmt.Printf("%v", newMap) } \ No newline at end of file diff --git a/go/vt/vttablet/tabletserver/schema_historian.go b/go/vt/vttablet/tabletserver/schema_historian.go deleted file mode 100644 index 01e7aaf6c5c..00000000000 --- a/go/vt/vttablet/tabletserver/schema_historian.go +++ /dev/null @@ -1,82 +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 tabletserver - -import ( - "bytes" - "context" - "encoding/gob" - "fmt" - "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" -) - -type schemaEngine interface { - Reload(ctx context.Context) error - GetSchema() map[string]*schema.Table -} - -type schemaTracker struct { - engine schemaEngine - conn *connpool.Pool -} - -func (s *schemaTracker) SchemaUpdated(gtid string, ddl string, timestamp int64) { - ctx := context.Background() - - s.engine.Reload(ctx) - //newSchema := s.engine.GetSchema() - //conn, err := s.conn.Get(ctx) - //if err != nil { - // panic(err) - //} - - //for name, table := range newSchema { - // - // //td := tabletmanagerdatapb.TableDefinition{ - // // Name: name, - // // Fields: table.Fields, - // //} - // - //} -} - -var _ SchemaSubscriber = (*schemaTracker)(nil) - -// go binary encoder -func ToGOB64(m map[string]*schema.Table) []byte { - b := bytes.Buffer{} - e := gob.NewEncoder(&b) - err := e.Encode(m) - if err != nil { - fmt.Println(`failed gob Encode`, err) - } - return b.Bytes() -} - -// go binary decoder -func FromGOB64(bites []byte) map[string]*schema.Table { - m := map[string]*schema.Table{} - b := bytes.Buffer{} - b.Write(bites) - d := gob.NewDecoder(&b) - err := d.Decode(&m) - if err != nil { - fmt.Println(`failed gob Decode`, err) - } - return m -} diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 6a7c9ea5d90..3f47fb9928f 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -228,6 +228,8 @@ func NewTabletServer(name string, config *tabletenv.TabletConfig, topoServer *to alias: alias, } tsv.se = schema.NewEngine(tsv) + sh := schema.NewSchemaHistorian(tsv.se) + tsv.se.SetHistorian(sh) tsv.qe = NewQueryEngine(tsv, tsv.se) tsv.te = NewTxEngine(tsv) tsv.txController = tsv.te @@ -236,7 +238,8 @@ func NewTabletServer(name string, config *tabletenv.TabletConfig, topoServer *to tsv.txThrottler = txthrottler.NewTxThrottler(tsv.config, topoServer) tsOnce.Do(func() { srvTopoServer = srvtopo.NewResilientServer(topoServer, "TabletSrvTopo") }) tsv.vstreamer = vstreamer.NewEngine(tsv, srvTopoServer, tsv.se) - tsv.watcher = NewReplicationWatcher(tsv, tsv.vstreamer, tsv.config) + schemaTracker := schema.NewTracker(tsv.se) + tsv.watcher = NewReplicationWatcher(tsv, tsv.vstreamer, tsv.config, schemaTracker) tsv.messager = messager.NewEngine(tsv, tsv.se, tsv.vstreamer) tsv.exporter.NewGaugeFunc("TabletState", "Tablet server state", func() int64 { @@ -1686,9 +1689,9 @@ func (tsv *TabletServer) BroadcastHealth(terTimestamp int64, stats *querypb.Real target := tsv.target tsv.mu.Unlock() shr := &querypb.StreamHealthResponse{ - Target: &target, - TabletAlias: &tsv.alias, - Serving: tsv.IsServing(), + Target: &target, + TabletAlias: &tsv.alias, + Serving: tsv.IsServing(), TabletExternallyReparentedTimestamp: terTimestamp, RealtimeStats: stats, } diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index 3e75fed8d6e..e8a0c502c15 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -21,6 +21,7 @@ import ( "flag" "fmt" "io" + "strconv" "time" "github.com/golang/protobuf/proto" @@ -69,6 +70,7 @@ type vstreamer struct { vschema *localVSchema plans map[uint64]*streamerPlan journalTableID uint64 + versionTableID uint64 // format and pos are updated by parseEvent. format mysql.BinlogFormat @@ -88,8 +90,8 @@ type streamerPlan struct { // se: the schema engine. The vstreamer uses it to convert the TableMap into field info. // startPos: a flavor compliant position to stream from. This can also contain the special // value "current", which means start from the current position. -// filter: the list of filtering rules. If a rule has a select expressinon for its filter, -// the select list can only reference direct columns. No other experssions are allowed. +// filter: the list of filtering rules. If a rule has a select expression for its filter, +// the select list can only reference direct columns. No other expressions are allowed. // The select expression is allowed to contain the special 'keyspace_id()' function which // will return the keyspace id of the row. Examples: // "select * from t", same as an empty Filter, @@ -101,7 +103,8 @@ type streamerPlan struct { // Other constructs like joins, group by, etc. are not supported. // vschema: the current vschema. This value can later be changed through the SetVSchema method. // send: callback function to send events. -func newVStreamer(ctx context.Context, cp dbconfigs.Connector, se *schema.Engine, startPos string, filter *binlogdatapb.Filter, vschema *localVSchema, send func([]*binlogdatapb.VEvent) error) *vstreamer { +func newVStreamer(ctx context.Context, cp dbconfigs.Connector, se *schema.Engine, startPos string, + filter *binlogdatapb.Filter, vschema *localVSchema, send func([]*binlogdatapb.VEvent) error) *vstreamer { ctx, cancel := context.WithCancel(ctx) return &vstreamer{ ctx: ctx, @@ -214,12 +217,14 @@ func (vs *vstreamer) parseEvents(ctx context.Context, events <-chan mysql.Binlog // If a single row exceeds the packet size, it will be in its own packet. bufferAndTransmit := func(vevent *binlogdatapb.VEvent) error { switch vevent.Type { - case binlogdatapb.VEventType_GTID, binlogdatapb.VEventType_BEGIN, binlogdatapb.VEventType_FIELD, binlogdatapb.VEventType_JOURNAL: + case binlogdatapb.VEventType_GTID, binlogdatapb.VEventType_BEGIN, binlogdatapb.VEventType_FIELD, + binlogdatapb.VEventType_JOURNAL, binlogdatapb.VEventType_VERSION: // We never have to send GTID, BEGIN, FIELD events on their own. // A JOURNAL event is always preceded by a BEGIN and followed by a COMMIT. // So, we don't have to send it right away. bufferedEvents = append(bufferedEvents, vevent) - case binlogdatapb.VEventType_COMMIT, binlogdatapb.VEventType_DDL, binlogdatapb.VEventType_OTHER, binlogdatapb.VEventType_HEARTBEAT: + case binlogdatapb.VEventType_COMMIT, binlogdatapb.VEventType_DDL, binlogdatapb.VEventType_OTHER, + binlogdatapb.VEventType_HEARTBEAT: // COMMIT, DDL, OTHER and HEARTBEAT must be immediately sent. // Although unlikely, it's possible to get a HEARTBEAT in the middle // of a transaction. If so, we still send the partial transaction along @@ -449,10 +454,6 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e Type: binlogdatapb.VEventType_OTHER, }) } - // Proactively reload schema. - // If the DDL adds a column, comparing with an older snapshot of the - // schema will make us think that a column was dropped and error out. - vs.se.Reload(vs.ctx) case sqlparser.StmtOther, sqlparser.StmtPriv: // These are either: // 1) DBA statements like REPAIR that can be ignored. @@ -485,6 +486,9 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e if tm.Database == "_vt" && tm.Name == "resharding_journal" { // A journal is a special case that generates a JOURNAL event. return nil, vs.buildJournalPlan(id, tm) + } else if tm.Database == "_vt" && tm.Name == "schema_version" { + // Generates a Version event when it detects that a schema is stored in the schema_version table. + return nil, vs.buildVersionPlan(id, tm) } if tm.Database != "" && tm.Database != params.DbName { vs.plans[id] = nil @@ -499,7 +503,7 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e } case ev.IsWriteRows() || ev.IsDeleteRows() || ev.IsUpdateRows(): // The existence of before and after images can be used to - // identify statememt types. It's also possible that the + // identify statement types. It's also possible that the // before and after images end up going to different shards. // If so, an update will be treated as delete on one shard // and insert on the other. @@ -513,7 +517,9 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e return nil, err } if id == vs.journalTableID { - vevents, err = vs.processJounalEvent(vevents, plan, rows) + vevents, err = vs.processJournalEvent(vevents, plan, rows) + } else if id == vs.versionTableID { + vevents, err = vs.processVersionEvent(vevents, plan, rows) } else { vevents, err = vs.processRowEvent(vevents, plan, rows) } @@ -528,6 +534,42 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e return vevents, nil } +//TODO create common buildSpecialPlan for journal/version? +func (vs *vstreamer) buildVersionPlan(id uint64, tm *mysql.TableMap) error { + conn, err := vs.cp.Connect(vs.ctx) + if err != nil { + return err + } + defer conn.Close() + qr, err := conn.ExecuteFetch("select * from _vt.schema_version where 1 != 1", 1, true) + if err != nil { + return err + } + fields := qr.Fields + log.Infof(">>> buildVersionPlan fields are %v", fields) + if len(fields) < len(tm.Types) { + return fmt.Errorf("cannot determine table columns for %s: event has %v, schema as %v", tm.Name, tm.Types, fields) + } + table := &Table{ + Name: "_vt.schema_version", + Fields: fields[:len(tm.Types)], + } + // Build a normal table plan, which means, return all rows + // and columns as is. Special handling is done when we actually + // receive the row event. We'll build a VERSION event instead. + plan, err := buildREPlan(table, nil, "") + if err != nil { + return err + } + vs.plans[id] = &streamerPlan{ + Plan: plan, + TableMap: tm, + } + vs.versionTableID = id + return nil + +} + func (vs *vstreamer) buildJournalPlan(id uint64, tm *mysql.TableMap) error { conn, err := vs.cp.Connect(vs.ctx) if err != nil { @@ -605,7 +647,7 @@ func (vs *vstreamer) buildTableColumns(tm *mysql.TableMap) ([]*querypb.Field, er }) } - st := vs.se.GetTable(sqlparser.NewTableIdent(tm.Name)) + st := vs.se.GetTable(sqlparser.NewTableIdent(tm.Name)) //GetTableForPos(sqlparser.NewTableIdent(tm.Name), vs.pos.String()) if st == nil { if vs.filter.FieldEventMode == binlogdatapb.Filter_ERR_ON_MISMATCH { return nil, fmt.Errorf("unknown table %v in schema", tm.Name) @@ -632,7 +674,7 @@ func (vs *vstreamer) buildTableColumns(tm *mysql.TableMap) ([]*querypb.Field, er return fields, nil } -func (vs *vstreamer) processJounalEvent(vevents []*binlogdatapb.VEvent, plan *streamerPlan, rows mysql.Rows) ([]*binlogdatapb.VEvent, error) { +func (vs *vstreamer) processJournalEvent(vevents []*binlogdatapb.VEvent, plan *streamerPlan, rows mysql.Rows) ([]*binlogdatapb.VEvent, error) { // Get DbName params, err := vs.cp.MysqlParams() if err != nil { @@ -671,6 +713,57 @@ nextrow: return vevents, nil } +func (vs *vstreamer) processVersionEvent(vevents []*binlogdatapb.VEvent, plan *streamerPlan, rows mysql.Rows) ([]*binlogdatapb.VEvent, error) { + // Get DbName + params, err := vs.cp.MysqlParams() + if err != nil { + return nil, err + } +nextrow: + for _, row := range rows.Rows { + afterOK, afterValues, err := vs.extractRowAndFilter(plan, row.Data, rows.DataColumns, row.NullColumns) + if err != nil { + return nil, err + } + if !afterOK { + // This can happen if someone manually deleted rows. + continue + } + // Exclude events that don't match the db_name. + for i, fld := range plan.fields() { + if fld.Name == "db_name" && afterValues[i].ToString() != params.DbName { + continue nextrow + } + } + version := &binlogdatapb.Version{} + for i, fld := range plan.fields() { + val := afterValues[i].ToString() + switch fld.Name { + case "pos": + version.Gtid = val + case "ddl": + version.Ddl = val + case "time_updated": + val, _ := strconv.Atoi(val) + version.Timestamp = int64(val) + case "schemax": + tables := &binlogdatapb.TableMetaDataCollection{} + log.Infof("SchemaX tables are %v", len(afterValues[i].ToBytes())) + if err := proto.Unmarshal(afterValues[i].ToBytes(), tables); err != nil { + return nil, err + } + version.Tables = tables + } + } + vs.se.Sh.RegisterVersionEvent(version) + vevents = append(vevents, &binlogdatapb.VEvent{ + Type: binlogdatapb.VEventType_VERSION, + Version: version, + }) + } + return vevents, nil +} + func (vs *vstreamer) processRowEvent(vevents []*binlogdatapb.VEvent, plan *streamerPlan, rows mysql.Rows) ([]*binlogdatapb.VEvent, error) { rowChanges := make([]*binlogdatapb.RowChange, 0, len(rows.Rows)) for _, row := range rows.Rows { diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index 49cdbc535ef..8fe1121aaf7 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -17,15 +17,21 @@ limitations under the License. package vstreamer import ( + "bytes" "fmt" "strconv" "strings" "testing" "time" + "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/net/context" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/mysql" @@ -37,6 +43,89 @@ type testcase struct { output [][]string } +type mockHistorian struct { + se schema.HistoryEngine + numVersionEventsReceived int + gtid string + ddl string + time_updated int64 + tables map[string]*binlogdatapb.TableMetaData +} + +func newMockHistorian(se schema.HistoryEngine) *mockHistorian { + return &mockHistorian{se: se} +} + +func (h *mockHistorian) GetTableForPos(tableName sqlparser.TableIdent, pos string) *schema.Table { + return nil +} + +func (h *mockHistorian) RegisterVersionEvent(version *binlogdatapb.Version) { + h.numVersionEventsReceived++ + h.gtid = version.Gtid + h.ddl = version.Ddl + h.time_updated = version.Timestamp + h.tables = make(map[string]*binlogdatapb.TableMetaData) + for _, table := range version.Tables.Tables { + h.tables[table.Name] = table + } + +} + +var _ schema.HistorianInterface = (*mockHistorian)(nil) + +func TestVersion(t *testing.T) { + if testing.Short() { + t.Skip() + } + historian := newMockHistorian(engine.se) + engine.se.Sh = historian + + 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))", + }) + defer execStatements(t, []string{ + "drop table _vt.schema_version", + }) + engine.se.Reload(context.Background()) + tbl := &binlogdatapb.TableMetaData{ + Name: "t1", + Fields: []*query.Field{ + {Name: "id", Type: query.Type_INT64, Table: "t1", Database: "test"}, + {Name: "i1", Type: query.Type_INT64, Table: "t1", Database: "test"}, + }, + Pkcolumns: []int64{0}, + } + tbls := &binlogdatapb.TableMetaDataCollection{ + Tables: []*binlogdatapb.TableMetaData{tbl}, + } + blob, err := proto.Marshal(tbls) + if err != nil { + t.Fatalf("Error marshalling tables %v", err) + } + testcases := []testcase{{ + input: []string{ + fmt.Sprintf("insert into _vt.schema_version values(1, 'gtid1', 123, 'create table t1', %s)", encodeString(string(blob))), + }, + // External table events don't get sent. + output: [][]string{{ + `begin`, + `type:VERSION version: fields: pkcolumns:0 > > > `, + `gtid`, + `commit`, + }}, + }} + runCases(t, nil, testcases, "") + assert.Equal(t, 1, historian.numVersionEventsReceived) + assert.Equal(t, 1, len(historian.tables)) +} + +func encodeString(in string) string { + buf := bytes.NewBuffer(nil) + sqltypes.NewVarChar(in).EncodeSQL(buf) + return buf.String() +} + func TestFilteredVarBinary(t *testing.T) { if testing.Short() { t.Skip() @@ -1346,7 +1435,7 @@ func expectLog(ctx context.Context, t *testing.T, input interface{}, ch <-chan [ } } if len(wantset) != len(evs) { - t.Fatalf("%v: evs\n%v, want\n%v", input, evs, wantset) + t.Fatalf("%v: evs\n%v, want\n%v, >> got length %d, wanted length %d", input, evs, wantset, len(evs), len(wantset)) } for i, want := range wantset { // CurrentTime is not testable. diff --git a/proto/binlogdata.proto b/proto/binlogdata.proto index 7268f3342d2..50a3a684f3c 100644 --- a/proto/binlogdata.proto +++ b/proto/binlogdata.proto @@ -230,6 +230,7 @@ enum VEventType { // GTIDs. VGTID = 15; JOURNAL = 16; + VERSION = 17; } // RowChange represents one row change. @@ -337,11 +338,35 @@ message VEvent { Journal journal = 8; // Dml is set if the event type is INSERT, REPLACE, UPDATE or DELETE. string dml = 9; + // Version is set if the event type is VERSION. + Version version = 10; // CurrentTime specifies the current time when the message was sent. // This can be used to compenssate for clock skew. int64 current_time = 20; } +message TableMetaData { + string name = 1; + repeated query.Field fields = 2; + repeated int64 p_k_columns = 3; +} + +message TableMetaDataCollection { + repeated TableMetaData tables = 1; +} + +message Version { + // Timestamp is the binlog timestamp in seconds. + // The value should be ignored if 0. + int64 timestamp = 1; + // Gtid position where version was set + string gtid = 2; + // Ddl which caused this version event + string ddl = 3; + // + TableMetaDataCollection tables = 4; +} + // VStreamRequest is the payload for VStreamer message VStreamRequest { vtrpc.CallerID effective_caller_id = 1; @@ -378,6 +403,7 @@ message VStreamRowsResponse { // VStreamResultsRequest is the payload for VStreamResults // The ids match VStreamRows, in case we decide to merge the two. +// The ids match VStreamRows, in case we decide to merge the two. message VStreamResultsRequest { vtrpc.CallerID effective_caller_id = 1; query.VTGateCallerID immediate_caller_id = 2; From 7b54e2ad921411af3fc254fc14070f1723526c3d Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Sat, 2 May 2020 23:53:45 +0200 Subject: [PATCH 04/17] Basic functionality implemented. More tests needed Signed-off-by: Rohit Nayak --- go/vt/proto/binlogdata/binlogdata.pb.go | 324 ++++++--------- .../vreplication/external_connector.go | 3 +- .../tabletserver/replication_watcher.go | 2 +- go/vt/vttablet/tabletserver/schema/engine.go | 12 +- .../vttablet/tabletserver/schema/historian.go | 235 ++++++++++- go/vt/vttablet/tabletserver/schema/tracker.go | 44 ++- .../tabletserver/schema/tracker_test.go | 29 -- .../vttablet/tabletserver/tabletenv/config.go | 2 + go/vt/vttablet/tabletserver/tabletserver.go | 7 +- .../vttablet/tabletserver/vstreamer/engine.go | 10 +- .../tabletserver/vstreamer/main_test.go | 20 +- .../vstreamer/resultstreamer_test.go | 2 +- .../tabletserver/vstreamer/rowstreamer.go | 28 +- .../vstreamer/rowstreamer_test.go | 12 +- .../tabletserver/vstreamer/vstreamer.go | 96 ++--- .../tabletserver/vstreamer/vstreamer_test.go | 369 ++++++++++++++---- proto/binlogdata.proto | 14 - 17 files changed, 737 insertions(+), 472 deletions(-) diff --git a/go/vt/proto/binlogdata/binlogdata.pb.go b/go/vt/proto/binlogdata/binlogdata.pb.go index eb808fdb43b..ec1a62d3f16 100644 --- a/go/vt/proto/binlogdata/binlogdata.pb.go +++ b/go/vt/proto/binlogdata/binlogdata.pb.go @@ -1276,8 +1276,6 @@ type VEvent struct { Journal *Journal `protobuf:"bytes,8,opt,name=journal,proto3" json:"journal,omitempty"` // Dml is set if the event type is INSERT, REPLACE, UPDATE or DELETE. Dml string `protobuf:"bytes,9,opt,name=dml,proto3" json:"dml,omitempty"` - // Version is set if the event type is VERSION. - Version *Version `protobuf:"bytes,10,opt,name=version,proto3" json:"version,omitempty"` // CurrentTime specifies the current time when the message was sent. // This can be used to compenssate for clock skew. CurrentTime int64 `protobuf:"varint,20,opt,name=current_time,json=currentTime,proto3" json:"current_time,omitempty"` @@ -1374,13 +1372,6 @@ func (m *VEvent) GetDml() string { return "" } -func (m *VEvent) GetVersion() *Version { - if m != nil { - return m.Version - } - return nil -} - func (m *VEvent) GetCurrentTime() int64 { if m != nil { return m.CurrentTime @@ -1391,7 +1382,7 @@ func (m *VEvent) GetCurrentTime() int64 { type TableMetaData struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Fields []*query.Field `protobuf:"bytes,2,rep,name=fields,proto3" json:"fields,omitempty"` - Pkcolumns []int64 `protobuf:"varint,3,rep,packed,name=pkcolumns,proto3" json:"pkcolumns,omitempty"` + PKColumns []int64 `protobuf:"varint,3,rep,packed,name=p_k_columns,json=pKColumns,proto3" json:"p_k_columns,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1436,9 +1427,9 @@ func (m *TableMetaData) GetFields() []*query.Field { return nil } -func (m *TableMetaData) GetPkcolumns() []int64 { +func (m *TableMetaData) GetPKColumns() []int64 { if m != nil { - return m.Pkcolumns + return m.PKColumns } return nil } @@ -1482,74 +1473,6 @@ func (m *TableMetaDataCollection) GetTables() []*TableMetaData { return nil } -type Version struct { - // Timestamp is the binlog timestamp in seconds. - // The value should be ignored if 0. - Timestamp int64 `protobuf:"varint,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - // Gtid position where version was set - Gtid string `protobuf:"bytes,2,opt,name=gtid,proto3" json:"gtid,omitempty"` - // Ddl which caused this version event - Ddl string `protobuf:"bytes,3,opt,name=ddl,proto3" json:"ddl,omitempty"` - // - Tables *TableMetaDataCollection `protobuf:"bytes,4,opt,name=tables,proto3" json:"tables,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Version) Reset() { *m = Version{} } -func (m *Version) String() string { return proto.CompactTextString(m) } -func (*Version) ProtoMessage() {} -func (*Version) Descriptor() ([]byte, []int) { - return fileDescriptor_5fd02bcb2e350dad, []int{19} -} - -func (m *Version) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Version.Unmarshal(m, b) -} -func (m *Version) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Version.Marshal(b, m, deterministic) -} -func (m *Version) XXX_Merge(src proto.Message) { - xxx_messageInfo_Version.Merge(m, src) -} -func (m *Version) XXX_Size() int { - return xxx_messageInfo_Version.Size(m) -} -func (m *Version) XXX_DiscardUnknown() { - xxx_messageInfo_Version.DiscardUnknown(m) -} - -var xxx_messageInfo_Version proto.InternalMessageInfo - -func (m *Version) GetTimestamp() int64 { - if m != nil { - return m.Timestamp - } - return 0 -} - -func (m *Version) GetGtid() string { - if m != nil { - return m.Gtid - } - return "" -} - -func (m *Version) GetDdl() string { - if m != nil { - return m.Ddl - } - return "" -} - -func (m *Version) GetTables() *TableMetaDataCollection { - if m != nil { - return m.Tables - } - return nil -} - // VStreamRequest is the payload for VStreamer type VStreamRequest struct { EffectiveCallerId *vtrpc.CallerID `protobuf:"bytes,1,opt,name=effective_caller_id,json=effectiveCallerId,proto3" json:"effective_caller_id,omitempty"` @@ -1566,7 +1489,7 @@ func (m *VStreamRequest) Reset() { *m = VStreamRequest{} } func (m *VStreamRequest) String() string { return proto.CompactTextString(m) } func (*VStreamRequest) ProtoMessage() {} func (*VStreamRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5fd02bcb2e350dad, []int{20} + return fileDescriptor_5fd02bcb2e350dad, []int{19} } func (m *VStreamRequest) XXX_Unmarshal(b []byte) error { @@ -1634,7 +1557,7 @@ func (m *VStreamResponse) Reset() { *m = VStreamResponse{} } func (m *VStreamResponse) String() string { return proto.CompactTextString(m) } func (*VStreamResponse) ProtoMessage() {} func (*VStreamResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5fd02bcb2e350dad, []int{21} + return fileDescriptor_5fd02bcb2e350dad, []int{20} } func (m *VStreamResponse) XXX_Unmarshal(b []byte) error { @@ -1678,7 +1601,7 @@ func (m *VStreamRowsRequest) Reset() { *m = VStreamRowsRequest{} } func (m *VStreamRowsRequest) String() string { return proto.CompactTextString(m) } func (*VStreamRowsRequest) ProtoMessage() {} func (*VStreamRowsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5fd02bcb2e350dad, []int{22} + return fileDescriptor_5fd02bcb2e350dad, []int{21} } func (m *VStreamRowsRequest) XXX_Unmarshal(b []byte) error { @@ -1750,7 +1673,7 @@ func (m *VStreamRowsResponse) Reset() { *m = VStreamRowsResponse{} } func (m *VStreamRowsResponse) String() string { return proto.CompactTextString(m) } func (*VStreamRowsResponse) ProtoMessage() {} func (*VStreamRowsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5fd02bcb2e350dad, []int{23} + return fileDescriptor_5fd02bcb2e350dad, []int{22} } func (m *VStreamRowsResponse) XXX_Unmarshal(b []byte) error { @@ -1823,7 +1746,7 @@ func (m *VStreamResultsRequest) Reset() { *m = VStreamResultsRequest{} } func (m *VStreamResultsRequest) String() string { return proto.CompactTextString(m) } func (*VStreamResultsRequest) ProtoMessage() {} func (*VStreamResultsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_5fd02bcb2e350dad, []int{24} + return fileDescriptor_5fd02bcb2e350dad, []int{23} } func (m *VStreamResultsRequest) XXX_Unmarshal(b []byte) error { @@ -1887,7 +1810,7 @@ func (m *VStreamResultsResponse) Reset() { *m = VStreamResultsResponse{} func (m *VStreamResultsResponse) String() string { return proto.CompactTextString(m) } func (*VStreamResultsResponse) ProtoMessage() {} func (*VStreamResultsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_5fd02bcb2e350dad, []int{25} + return fileDescriptor_5fd02bcb2e350dad, []int{24} } func (m *VStreamResultsResponse) XXX_Unmarshal(b []byte) error { @@ -1955,7 +1878,6 @@ func init() { proto.RegisterType((*VEvent)(nil), "binlogdata.VEvent") proto.RegisterType((*TableMetaData)(nil), "binlogdata.TableMetaData") proto.RegisterType((*TableMetaDataCollection)(nil), "binlogdata.TableMetaDataCollection") - proto.RegisterType((*Version)(nil), "binlogdata.Version") proto.RegisterType((*VStreamRequest)(nil), "binlogdata.VStreamRequest") proto.RegisterType((*VStreamResponse)(nil), "binlogdata.VStreamResponse") proto.RegisterType((*VStreamRowsRequest)(nil), "binlogdata.VStreamRowsRequest") @@ -1967,119 +1889,117 @@ func init() { func init() { proto.RegisterFile("binlogdata.proto", fileDescriptor_5fd02bcb2e350dad) } var fileDescriptor_5fd02bcb2e350dad = []byte{ - // 1821 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x58, 0x4b, 0x73, 0x23, 0x49, - 0x11, 0x9e, 0x56, 0xeb, 0x99, 0x6d, 0xc9, 0xed, 0xf2, 0x63, 0xc5, 0x04, 0x4b, 0x78, 0x1b, 0x66, - 0xc7, 0xeb, 0x08, 0x64, 0x56, 0xc0, 0x70, 0x20, 0x96, 0x45, 0x8f, 0xb6, 0x47, 0x33, 0x2d, 0xc9, - 0x53, 0xea, 0xf1, 0x10, 0x7b, 0xe9, 0x68, 0x4b, 0x65, 0xbb, 0x71, 0x4b, 0xad, 0xe9, 0x2e, 0xd9, - 0xab, 0x1f, 0x40, 0x10, 0xc1, 0x95, 0x3f, 0xc0, 0x95, 0x33, 0x57, 0xb8, 0xf2, 0x2b, 0x08, 0x6e, - 0x9c, 0xf8, 0x13, 0x44, 0x3d, 0xfa, 0x65, 0xef, 0xce, 0x78, 0x36, 0x82, 0x03, 0x5c, 0x14, 0x55, - 0x59, 0x99, 0x59, 0x99, 0x5f, 0x3e, 0x3a, 0x4b, 0xa0, 0x9f, 0x7b, 0x0b, 0x3f, 0xb8, 0x9c, 0xb9, - 0xd4, 0x6d, 0x2d, 0xc3, 0x80, 0x06, 0x08, 0x52, 0xca, 0x63, 0xed, 0x86, 0x86, 0xcb, 0xa9, 0x38, - 0x78, 0xac, 0xbd, 0x5d, 0x91, 0x70, 0x2d, 0x37, 0x0d, 0x1a, 0x2c, 0x83, 0x54, 0xca, 0x18, 0x42, - 0xa5, 0x77, 0xe5, 0x86, 0x11, 0xa1, 0x68, 0x0f, 0xca, 0x53, 0xdf, 0x23, 0x0b, 0xda, 0x54, 0xf6, - 0x95, 0x83, 0x12, 0x96, 0x3b, 0x84, 0xa0, 0x38, 0x0d, 0x16, 0x8b, 0x66, 0x81, 0x53, 0xf9, 0x9a, - 0xf1, 0x46, 0x24, 0xbc, 0x21, 0x61, 0x53, 0x15, 0xbc, 0x62, 0x67, 0xfc, 0x4b, 0x85, 0xad, 0x2e, - 0xb7, 0xc3, 0x0e, 0xdd, 0x45, 0xe4, 0x4e, 0xa9, 0x17, 0x2c, 0xd0, 0x09, 0x40, 0x44, 0x5d, 0x4a, - 0xe6, 0x64, 0x41, 0xa3, 0xa6, 0xb2, 0xaf, 0x1e, 0x68, 0xed, 0xa7, 0xad, 0x8c, 0x07, 0xf7, 0x44, - 0x5a, 0x93, 0x98, 0x1f, 0x67, 0x44, 0x51, 0x1b, 0x34, 0x72, 0x43, 0x16, 0xd4, 0xa1, 0xc1, 0x35, - 0x59, 0x34, 0x8b, 0xfb, 0xca, 0x81, 0xd6, 0xde, 0x6a, 0x09, 0x07, 0x4d, 0x76, 0x62, 0xb3, 0x03, - 0x0c, 0x24, 0x59, 0x3f, 0xfe, 0x7b, 0x01, 0x6a, 0x89, 0x36, 0x64, 0x41, 0x75, 0xea, 0x52, 0x72, - 0x19, 0x84, 0x6b, 0xee, 0x66, 0xa3, 0xfd, 0x93, 0x07, 0x1a, 0xd2, 0xea, 0x49, 0x39, 0x9c, 0x68, - 0x40, 0x3f, 0x86, 0xca, 0x54, 0xa0, 0xc7, 0xd1, 0xd1, 0xda, 0xdb, 0x59, 0x65, 0x12, 0x58, 0x1c, - 0xf3, 0x20, 0x1d, 0xd4, 0xe8, 0xad, 0xcf, 0x21, 0xdb, 0xc0, 0x6c, 0x69, 0xfc, 0x59, 0x81, 0x6a, - 0xac, 0x17, 0x6d, 0xc3, 0x66, 0xd7, 0x72, 0x5e, 0x8f, 0xb0, 0xd9, 0x1b, 0x9f, 0x8c, 0x06, 0x5f, - 0x99, 0x7d, 0xfd, 0x11, 0xda, 0x80, 0x6a, 0xd7, 0x72, 0xba, 0xe6, 0xc9, 0x60, 0xa4, 0x2b, 0xa8, - 0x0e, 0xb5, 0xae, 0xe5, 0xf4, 0xc6, 0xc3, 0xe1, 0xc0, 0xd6, 0x0b, 0x68, 0x13, 0xb4, 0xae, 0xe5, - 0xe0, 0xb1, 0x65, 0x75, 0x3b, 0xbd, 0x97, 0xba, 0x8a, 0x76, 0x61, 0xab, 0x6b, 0x39, 0xfd, 0xa1, - 0xe5, 0xf4, 0xcd, 0x53, 0x6c, 0xf6, 0x3a, 0xb6, 0xd9, 0xd7, 0x8b, 0x08, 0xa0, 0xcc, 0xc8, 0x7d, - 0x4b, 0x2f, 0xc9, 0xf5, 0xc4, 0xb4, 0xf5, 0xb2, 0x54, 0x37, 0x18, 0x4d, 0x4c, 0x6c, 0xeb, 0x15, - 0xb9, 0x7d, 0x7d, 0xda, 0xef, 0xd8, 0xa6, 0x5e, 0x95, 0xdb, 0xbe, 0x69, 0x99, 0xb6, 0xa9, 0xd7, - 0x5e, 0x14, 0xab, 0x05, 0x5d, 0x7d, 0x51, 0xac, 0xaa, 0x7a, 0xd1, 0xf8, 0xa3, 0x02, 0xbb, 0x13, - 0x1a, 0x12, 0x77, 0xfe, 0x92, 0xac, 0xb1, 0xbb, 0xb8, 0x24, 0x98, 0xbc, 0x5d, 0x91, 0x88, 0xa2, - 0xc7, 0x50, 0x5d, 0x06, 0x91, 0xc7, 0xb0, 0xe3, 0x00, 0xd7, 0x70, 0xb2, 0x47, 0x47, 0x50, 0xbb, - 0x26, 0x6b, 0x27, 0x64, 0xfc, 0x12, 0x30, 0xd4, 0x4a, 0x12, 0x32, 0xd1, 0x54, 0xbd, 0x96, 0xab, - 0x2c, 0xbe, 0xea, 0xfb, 0xf1, 0x35, 0x2e, 0x60, 0xef, 0xae, 0x51, 0xd1, 0x32, 0x58, 0x44, 0x04, - 0x59, 0x80, 0x84, 0xa0, 0x43, 0xd3, 0xd8, 0x72, 0xfb, 0xb4, 0xf6, 0xc7, 0xef, 0x4c, 0x00, 0xbc, - 0x75, 0x7e, 0x97, 0x64, 0x7c, 0x0d, 0xdb, 0xe2, 0x1e, 0xdb, 0x3d, 0xf7, 0x49, 0xf4, 0x10, 0xd7, - 0xf7, 0xa0, 0x4c, 0x39, 0x73, 0xb3, 0xb0, 0xaf, 0x1e, 0xd4, 0xb0, 0xdc, 0x7d, 0xa8, 0x87, 0x33, - 0xd8, 0xc9, 0xdf, 0xfc, 0x5f, 0xf1, 0xef, 0x67, 0x50, 0xc4, 0x2b, 0x9f, 0xa0, 0x1d, 0x28, 0xcd, - 0x5d, 0x3a, 0xbd, 0x92, 0xde, 0x88, 0x0d, 0x73, 0xe5, 0xc2, 0xf3, 0x29, 0x09, 0x79, 0x08, 0x6b, - 0x58, 0xee, 0x8c, 0xbf, 0x28, 0x50, 0x3e, 0xe6, 0x4b, 0xf4, 0x29, 0x94, 0xc2, 0x15, 0x73, 0x56, - 0xd4, 0xba, 0x9e, 0xb5, 0x80, 0x69, 0xc6, 0xe2, 0x18, 0x0d, 0xa0, 0x71, 0xe1, 0x11, 0x7f, 0xc6, - 0x4b, 0x77, 0x18, 0xcc, 0x44, 0x56, 0x34, 0xda, 0x9f, 0x64, 0x05, 0x84, 0xce, 0xd6, 0x71, 0x8e, - 0x11, 0xdf, 0x11, 0x34, 0x9e, 0x41, 0x23, 0xcf, 0xc1, 0xca, 0xc9, 0xc4, 0xd8, 0x19, 0x8f, 0x9c, - 0xe1, 0x60, 0x32, 0xec, 0xd8, 0xbd, 0xe7, 0xfa, 0x23, 0x5e, 0x31, 0xe6, 0xc4, 0x76, 0xcc, 0xe3, - 0xe3, 0x31, 0xb6, 0x75, 0xc5, 0xf8, 0x77, 0x01, 0x36, 0x04, 0x28, 0x93, 0x60, 0x15, 0x4e, 0x09, - 0x8b, 0xe2, 0x35, 0x59, 0x47, 0x4b, 0x77, 0x4a, 0xe2, 0x28, 0xc6, 0x7b, 0x06, 0x48, 0x74, 0xe5, - 0x86, 0x33, 0xe9, 0xb9, 0xd8, 0xa0, 0x9f, 0x83, 0xc6, 0xa3, 0x49, 0x1d, 0xba, 0x5e, 0x12, 0x1e, - 0xc7, 0x46, 0x7b, 0x27, 0x4d, 0x6c, 0x1e, 0x2b, 0x6a, 0xaf, 0x97, 0x04, 0x03, 0x4d, 0xd6, 0xf9, - 0x6a, 0x28, 0x3e, 0xa0, 0x1a, 0xd2, 0x1c, 0x2a, 0xe5, 0x72, 0xe8, 0x30, 0x09, 0x48, 0x59, 0x6a, - 0xb9, 0x87, 0x5e, 0x1c, 0x24, 0xd4, 0x82, 0x72, 0xb0, 0x70, 0x66, 0x33, 0xbf, 0x59, 0xe1, 0x66, - 0x7e, 0x94, 0xe5, 0x1d, 0x2f, 0xfa, 0x7d, 0xab, 0x23, 0xd2, 0xa2, 0x14, 0x2c, 0xfa, 0x33, 0x1f, - 0x3d, 0x81, 0x06, 0xf9, 0x9a, 0x92, 0x70, 0xe1, 0xfa, 0xce, 0x7c, 0xcd, 0xba, 0x57, 0x95, 0xbb, - 0x5e, 0x8f, 0xa9, 0x43, 0x46, 0x44, 0x9f, 0xc2, 0x66, 0x44, 0x83, 0xa5, 0xe3, 0x5e, 0x50, 0x12, - 0x3a, 0xd3, 0x60, 0xb9, 0x6e, 0xd6, 0xf6, 0x95, 0x83, 0x2a, 0xae, 0x33, 0x72, 0x87, 0x51, 0x7b, - 0xc1, 0x72, 0x6d, 0xbc, 0x82, 0x1a, 0x0e, 0x6e, 0x7b, 0x57, 0xdc, 0x1f, 0x03, 0xca, 0xe7, 0xe4, - 0x22, 0x08, 0x89, 0x4c, 0x54, 0x90, 0x8d, 0x1c, 0x07, 0xb7, 0x58, 0x9e, 0xa0, 0x7d, 0x28, 0x71, - 0x9d, 0xb2, 0x5d, 0x64, 0x59, 0xc4, 0x81, 0xe1, 0x42, 0x15, 0x07, 0xb7, 0x3c, 0xec, 0xe8, 0x63, - 0x10, 0x00, 0x3b, 0x0b, 0x77, 0x1e, 0x47, 0xaf, 0xc6, 0x29, 0x23, 0x77, 0x4e, 0xd0, 0x33, 0xd0, - 0xc2, 0xe0, 0xd6, 0x99, 0xf2, 0xeb, 0x45, 0x25, 0x6a, 0xed, 0xdd, 0x5c, 0x72, 0xc6, 0xc6, 0x61, - 0x08, 0xe3, 0x65, 0x64, 0xbc, 0x02, 0x48, 0x73, 0xeb, 0x7d, 0x97, 0xfc, 0x88, 0x45, 0x83, 0xf8, - 0xb3, 0x58, 0xff, 0x86, 0x34, 0x99, 0x6b, 0xc0, 0xf2, 0x8c, 0x01, 0x31, 0x61, 0xc9, 0x73, 0x42, - 0xbd, 0xd9, 0x77, 0x48, 0x39, 0x04, 0xc5, 0x4b, 0xea, 0xcd, 0x78, 0xae, 0xd5, 0x30, 0x5f, 0x1b, - 0x5f, 0x42, 0xe9, 0x8c, 0xab, 0x7b, 0x06, 0x1a, 0xe7, 0x72, 0x18, 0x39, 0xae, 0xc1, 0x9c, 0x9b, - 0xc9, 0xd5, 0x18, 0xa2, 0x78, 0x19, 0x19, 0x1d, 0xa8, 0xbf, 0x94, 0xd7, 0x72, 0x86, 0x0f, 0xb7, - 0xcb, 0xf8, 0x6b, 0x01, 0x2a, 0x2f, 0x82, 0x15, 0x4b, 0x0c, 0xd4, 0x80, 0x82, 0x37, 0xe3, 0x72, - 0x2a, 0x2e, 0x78, 0x33, 0xf4, 0x6b, 0x68, 0xcc, 0xbd, 0xcb, 0xd0, 0x65, 0xe9, 0x25, 0x2a, 0x45, - 0x14, 0xfb, 0xf7, 0xb2, 0x96, 0x0d, 0x63, 0x0e, 0x5e, 0x2e, 0xf5, 0x79, 0x76, 0x9b, 0x29, 0x00, - 0x35, 0x57, 0x00, 0x4f, 0xa0, 0xe1, 0x07, 0x53, 0xd7, 0x77, 0x92, 0xf6, 0x5b, 0x14, 0x49, 0xca, - 0xa9, 0xa7, 0x71, 0x0f, 0xbe, 0x83, 0x4b, 0xe9, 0x81, 0xb8, 0xa0, 0x2f, 0x60, 0x63, 0xe9, 0x86, - 0xd4, 0x9b, 0x7a, 0x4b, 0x97, 0x0d, 0x30, 0x65, 0x2e, 0x98, 0x33, 0x3b, 0x87, 0x1b, 0xce, 0xb1, - 0xa3, 0xcf, 0x40, 0x8f, 0x78, 0x6b, 0x71, 0x6e, 0x83, 0xf0, 0xfa, 0xc2, 0x0f, 0x6e, 0xa3, 0x66, - 0x85, 0xdb, 0xbf, 0x29, 0xe8, 0x6f, 0x62, 0xb2, 0xf1, 0x27, 0x15, 0xca, 0x67, 0x22, 0xcb, 0x0e, - 0xa1, 0xc8, 0x31, 0x12, 0x43, 0xca, 0x5e, 0xf6, 0x32, 0xc1, 0xc1, 0x01, 0xe2, 0x3c, 0xe8, 0xfb, - 0x50, 0xa3, 0xde, 0x9c, 0x44, 0xd4, 0x9d, 0x2f, 0x39, 0xa8, 0x2a, 0x4e, 0x09, 0xdf, 0x94, 0x2b, - 0x6c, 0x12, 0x61, 0x3d, 0x40, 0xc0, 0xc4, 0x96, 0xe8, 0x73, 0xa8, 0xb1, 0xda, 0xe0, 0x83, 0x53, - 0xb3, 0xc4, 0x8b, 0x6d, 0xe7, 0x4e, 0x65, 0xf0, 0x6b, 0x71, 0x35, 0x8c, 0xab, 0xed, 0x17, 0xa0, - 0xf1, 0x6c, 0x96, 0x42, 0xa2, 0xf9, 0xec, 0xe5, 0x9b, 0x4f, 0x5c, 0x35, 0x18, 0xd2, 0x7e, 0x8d, - 0x9e, 0x42, 0xe9, 0x86, 0x9b, 0x54, 0x91, 0x03, 0x5c, 0xd6, 0x39, 0x0e, 0xbf, 0x38, 0x67, 0x5f, - 0xc7, 0xdf, 0x8a, 0x6c, 0xe2, 0x6d, 0xe7, 0xce, 0xd7, 0x51, 0x26, 0x1a, 0x8e, 0x79, 0xb8, 0x57, - 0x73, 0x9f, 0x77, 0x1e, 0xe6, 0xd5, 0xdc, 0x67, 0x0a, 0x6e, 0x48, 0x18, 0xb1, 0x94, 0x80, 0xfb, - 0x0a, 0xce, 0xc4, 0x11, 0x8e, 0x79, 0xd0, 0x27, 0xb0, 0x31, 0x5d, 0x85, 0x21, 0x9f, 0x30, 0xbd, - 0x39, 0x69, 0xee, 0x70, 0x2c, 0x35, 0x49, 0xb3, 0xbd, 0x39, 0x31, 0x2e, 0xa1, 0xce, 0xfb, 0xf9, - 0x90, 0x50, 0xb7, 0xef, 0x52, 0x97, 0xc1, 0x9b, 0x69, 0x04, 0x7c, 0xfd, 0xb0, 0x1e, 0xc0, 0xc2, - 0xb6, 0xbc, 0x9e, 0x06, 0xfe, 0x6a, 0xbe, 0x10, 0x19, 0xad, 0xe2, 0x94, 0x60, 0x58, 0xf0, 0x51, - 0xee, 0xa2, 0x5e, 0xe0, 0xfb, 0x44, 0xcc, 0xd3, 0x9f, 0x27, 0x75, 0xa0, 0xdc, 0x4f, 0xc5, 0x9c, - 0x50, 0x5c, 0x22, 0xc6, 0x1f, 0x14, 0xa8, 0x48, 0x77, 0xf3, 0xe9, 0xa2, 0x7c, 0x5b, 0xba, 0x14, - 0xee, 0xa7, 0x8b, 0x9a, 0xa6, 0xcb, 0x2f, 0x13, 0x13, 0xc4, 0x97, 0xeb, 0x87, 0xdf, 0x6a, 0x42, - 0x6a, 0x77, 0x62, 0xcc, 0xef, 0x0b, 0xd0, 0x38, 0x13, 0x73, 0x4c, 0x3c, 0x3b, 0x7d, 0x09, 0xdb, - 0xe4, 0xe2, 0x82, 0xf1, 0xdd, 0x10, 0x67, 0xea, 0xfa, 0x3e, 0x09, 0x1d, 0xd9, 0x3d, 0xb4, 0xf6, - 0x66, 0x4b, 0xbc, 0x67, 0x7a, 0x9c, 0x3e, 0xe8, 0xe3, 0xad, 0x84, 0x57, 0x92, 0x66, 0xc8, 0x84, - 0x6d, 0x6f, 0x3e, 0x27, 0x33, 0xcf, 0xa5, 0x59, 0x05, 0xe2, 0xb3, 0xb1, 0x2b, 0xf1, 0x3f, 0xb3, - 0x4f, 0x5c, 0x4a, 0x52, 0x35, 0x89, 0x44, 0xa2, 0xe6, 0x09, 0xf3, 0x2b, 0xbc, 0x4c, 0xc6, 0xb1, - 0xba, 0x94, 0xb4, 0x39, 0x11, 0xcb, 0xc3, 0xdc, 0xa8, 0x57, 0xbc, 0x33, 0xea, 0xa5, 0x9f, 0xe3, - 0xd2, 0xfb, 0x3e, 0xc7, 0xc6, 0x17, 0xb0, 0x99, 0x00, 0x21, 0x47, 0xb9, 0x43, 0x28, 0xf3, 0x7a, - 0x8a, 0x83, 0x8b, 0xee, 0x97, 0x3e, 0x96, 0x1c, 0xc6, 0xef, 0x0a, 0x80, 0x62, 0xf9, 0xe0, 0x36, - 0xfa, 0x1f, 0x05, 0x73, 0x07, 0x4a, 0x9c, 0x2e, 0x91, 0x14, 0x1b, 0x86, 0x83, 0xef, 0x46, 0x74, - 0x79, 0x9d, 0xc0, 0x28, 0x84, 0x5f, 0xb1, 0x5f, 0x4c, 0xa2, 0x95, 0x4f, 0xb1, 0xe4, 0x30, 0xfe, - 0xa6, 0xc0, 0x76, 0x0e, 0x07, 0x89, 0x65, 0x5a, 0x87, 0xca, 0x3b, 0xea, 0xf0, 0x00, 0xaa, 0xcb, - 0xeb, 0x77, 0xd4, 0x6b, 0x72, 0xfa, 0x8d, 0xad, 0xf4, 0x07, 0x50, 0x0c, 0x59, 0x4b, 0x2f, 0x72, - 0xc9, 0xec, 0x80, 0xc2, 0xe9, 0x6c, 0xca, 0xc9, 0xf9, 0x91, 0x9b, 0x72, 0xa4, 0xfd, 0xff, 0x50, - 0x60, 0x37, 0xcd, 0x83, 0x95, 0x4f, 0xff, 0xaf, 0x42, 0x69, 0x84, 0xb0, 0x77, 0xd7, 0xbb, 0x0f, - 0x0a, 0xd0, 0x77, 0x80, 0xfd, 0xf0, 0x57, 0xa0, 0x65, 0xc6, 0x59, 0xf6, 0xea, 0x1d, 0x9c, 0x8c, - 0xc6, 0xd8, 0xd4, 0x1f, 0xa1, 0x2a, 0x14, 0x27, 0xf6, 0xf8, 0x54, 0x57, 0xd8, 0xca, 0xfc, 0x8d, - 0xd9, 0x13, 0x2f, 0x69, 0xb6, 0x72, 0x24, 0x93, 0x7a, 0xf8, 0x4f, 0x05, 0x20, 0xfd, 0xd0, 0x22, - 0x0d, 0x2a, 0xaf, 0x47, 0x2f, 0x47, 0xe3, 0x37, 0x23, 0xa1, 0xe0, 0xc4, 0x1e, 0xf4, 0x75, 0x05, - 0xd5, 0xa0, 0x24, 0x9e, 0xe6, 0x05, 0x76, 0x83, 0x7c, 0x97, 0xab, 0xec, 0xd1, 0x9e, 0x3c, 0xca, - 0x8b, 0xa8, 0x02, 0x6a, 0xf2, 0xf4, 0x96, 0x6f, 0xed, 0x32, 0x53, 0x88, 0xcd, 0x53, 0xab, 0xd3, - 0x33, 0xf5, 0x0a, 0x3b, 0x48, 0x5e, 0xdd, 0x00, 0xe5, 0xf8, 0xc9, 0xcd, 0x24, 0xd9, 0x43, 0x1d, - 0xd8, 0x3d, 0x63, 0xfb, 0xb9, 0x89, 0x75, 0x8d, 0xd1, 0xf0, 0xf8, 0x8d, 0xbe, 0xc1, 0x68, 0xc7, - 0x03, 0xd3, 0xea, 0xeb, 0x75, 0xf6, 0x52, 0x7f, 0x6e, 0x76, 0xb0, 0xdd, 0x35, 0x3b, 0xb6, 0xde, - 0x60, 0x27, 0x67, 0xdc, 0xc0, 0x4d, 0x76, 0xcd, 0x8b, 0xf1, 0x6b, 0x3c, 0xea, 0x58, 0xba, 0xce, - 0x36, 0x67, 0x26, 0x9e, 0x0c, 0xc6, 0x23, 0x7d, 0xeb, 0xf0, 0x29, 0xd4, 0x73, 0xc3, 0x16, 0xbb, - 0xd8, 0xee, 0x74, 0x2d, 0x73, 0xa2, 0x3f, 0x62, 0xeb, 0xc9, 0xf3, 0x0e, 0xee, 0x4f, 0x74, 0xa5, - 0xfb, 0xd9, 0x57, 0x4f, 0x6f, 0x3c, 0x4a, 0xa2, 0xa8, 0xe5, 0x05, 0x47, 0x62, 0x75, 0x74, 0x19, - 0x1c, 0xdd, 0xd0, 0x23, 0xfe, 0x17, 0xd2, 0x51, 0xda, 0x9e, 0xce, 0xcb, 0x9c, 0xf2, 0xd3, 0xff, - 0x04, 0x00, 0x00, 0xff, 0xff, 0xb2, 0xf5, 0xaa, 0x3a, 0x9e, 0x12, 0x00, 0x00, + // 1778 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x58, 0xcd, 0x72, 0xdb, 0xc8, + 0x11, 0x36, 0xf8, 0xcf, 0x86, 0x44, 0x41, 0xa3, 0x9f, 0x65, 0x5c, 0xd9, 0x2d, 0x2d, 0x2a, 0x5e, + 0x6b, 0x55, 0x15, 0x2a, 0xcb, 0x24, 0xce, 0x69, 0xb3, 0xe1, 0x0f, 0x24, 0xd3, 0x02, 0x49, 0x79, + 0x08, 0xcb, 0xa9, 0xbd, 0xa0, 0x20, 0x72, 0x24, 0x23, 0xc2, 0x9f, 0x81, 0xa1, 0xb4, 0x7c, 0x80, + 0x54, 0x1e, 0x20, 0x4f, 0x91, 0x73, 0xae, 0xc9, 0x35, 0x4f, 0x91, 0xca, 0x2d, 0xa7, 0x9c, 0xf2, + 0x06, 0xa9, 0xf9, 0x01, 0x08, 0xc8, 0x5b, 0xb6, 0xbc, 0x55, 0x39, 0x24, 0x17, 0x56, 0x4f, 0x4f, + 0x77, 0x4f, 0xf7, 0x37, 0xdd, 0x8d, 0x1e, 0x82, 0x76, 0xe9, 0x06, 0x5e, 0x78, 0xbd, 0x70, 0xa8, + 0xd3, 0x89, 0xe2, 0x90, 0x86, 0x08, 0xd6, 0x9c, 0xc7, 0xea, 0x2d, 0x8d, 0xa3, 0xb9, 0xd8, 0x78, + 0xac, 0xbe, 0x5d, 0x92, 0x78, 0x25, 0x17, 0x2d, 0x1a, 0x46, 0xe1, 0x5a, 0x4b, 0x1f, 0x43, 0x7d, + 0xf0, 0xc6, 0x89, 0x13, 0x42, 0xd1, 0x3e, 0xd4, 0xe6, 0x9e, 0x4b, 0x02, 0xda, 0x56, 0x0e, 0x94, + 0xc3, 0x2a, 0x96, 0x2b, 0x84, 0xa0, 0x32, 0x0f, 0x83, 0xa0, 0x5d, 0xe2, 0x5c, 0x4e, 0x33, 0xd9, + 0x84, 0xc4, 0xb7, 0x24, 0x6e, 0x97, 0x85, 0xac, 0x58, 0xe9, 0xff, 0x2c, 0xc3, 0x76, 0x9f, 0xfb, + 0x61, 0xc5, 0x4e, 0x90, 0x38, 0x73, 0xea, 0x86, 0x01, 0x3a, 0x05, 0x48, 0xa8, 0x43, 0x89, 0x4f, + 0x02, 0x9a, 0xb4, 0x95, 0x83, 0xf2, 0xa1, 0xda, 0x7d, 0xda, 0xc9, 0x45, 0xf0, 0x8e, 0x4a, 0x67, + 0x96, 0xca, 0xe3, 0x9c, 0x2a, 0xea, 0x82, 0x4a, 0x6e, 0x49, 0x40, 0x6d, 0x1a, 0xde, 0x90, 0xa0, + 0x5d, 0x39, 0x50, 0x0e, 0xd5, 0xee, 0x76, 0x47, 0x04, 0x68, 0xb0, 0x1d, 0x8b, 0x6d, 0x60, 0x20, + 0x19, 0xfd, 0xf8, 0x6f, 0x25, 0x68, 0x66, 0xd6, 0x90, 0x09, 0x8d, 0xb9, 0x43, 0xc9, 0x75, 0x18, + 0xaf, 0x78, 0x98, 0xad, 0xee, 0xcf, 0x1e, 0xe8, 0x48, 0x67, 0x20, 0xf5, 0x70, 0x66, 0x01, 0xfd, + 0x14, 0xea, 0x73, 0x81, 0x1e, 0x47, 0x47, 0xed, 0xee, 0xe4, 0x8d, 0x49, 0x60, 0x71, 0x2a, 0x83, + 0x34, 0x28, 0x27, 0x6f, 0x3d, 0x0e, 0xd9, 0x06, 0x66, 0xa4, 0xfe, 0x27, 0x05, 0x1a, 0xa9, 0x5d, + 0xb4, 0x03, 0x5b, 0x7d, 0xd3, 0x7e, 0x35, 0xc1, 0xc6, 0x60, 0x7a, 0x3a, 0x19, 0x7d, 0x6b, 0x0c, + 0xb5, 0x47, 0x68, 0x03, 0x1a, 0x7d, 0xd3, 0xee, 0x1b, 0xa7, 0xa3, 0x89, 0xa6, 0xa0, 0x4d, 0x68, + 0xf6, 0x4d, 0x7b, 0x30, 0x1d, 0x8f, 0x47, 0x96, 0x56, 0x42, 0x5b, 0xa0, 0xf6, 0x4d, 0x1b, 0x4f, + 0x4d, 0xb3, 0xdf, 0x1b, 0x9c, 0x69, 0x65, 0xb4, 0x07, 0xdb, 0x7d, 0xd3, 0x1e, 0x8e, 0x4d, 0x7b, + 0x68, 0x9c, 0x63, 0x63, 0xd0, 0xb3, 0x8c, 0xa1, 0x56, 0x41, 0x00, 0x35, 0xc6, 0x1e, 0x9a, 0x5a, + 0x55, 0xd2, 0x33, 0xc3, 0xd2, 0x6a, 0xd2, 0xdc, 0x68, 0x32, 0x33, 0xb0, 0xa5, 0xd5, 0xe5, 0xf2, + 0xd5, 0xf9, 0xb0, 0x67, 0x19, 0x5a, 0x43, 0x2e, 0x87, 0x86, 0x69, 0x58, 0x86, 0xd6, 0x7c, 0x51, + 0x69, 0x94, 0xb4, 0xf2, 0x8b, 0x4a, 0xa3, 0xac, 0x55, 0xf4, 0x3f, 0x2a, 0xb0, 0x37, 0xa3, 0x31, + 0x71, 0xfc, 0x33, 0xb2, 0xc2, 0x4e, 0x70, 0x4d, 0x30, 0x79, 0xbb, 0x24, 0x09, 0x45, 0x8f, 0xa1, + 0x11, 0x85, 0x89, 0xcb, 0xb0, 0xe3, 0x00, 0x37, 0x71, 0xb6, 0x46, 0xc7, 0xd0, 0xbc, 0x21, 0x2b, + 0x3b, 0x66, 0xf2, 0x12, 0x30, 0xd4, 0xc9, 0x12, 0x32, 0xb3, 0xd4, 0xb8, 0x91, 0x54, 0x1e, 0xdf, + 0xf2, 0x87, 0xf1, 0xd5, 0xaf, 0x60, 0xff, 0xbe, 0x53, 0x49, 0x14, 0x06, 0x09, 0x41, 0x26, 0x20, + 0xa1, 0x68, 0xd3, 0xf5, 0xdd, 0x72, 0xff, 0xd4, 0xee, 0xa7, 0xef, 0x4d, 0x00, 0xbc, 0x7d, 0x79, + 0x9f, 0xa5, 0x7f, 0x07, 0x3b, 0xe2, 0x1c, 0xcb, 0xb9, 0xf4, 0x48, 0xf2, 0x90, 0xd0, 0xf7, 0xa1, + 0x46, 0xb9, 0x70, 0xbb, 0x74, 0x50, 0x3e, 0x6c, 0x62, 0xb9, 0xfa, 0xd8, 0x08, 0x17, 0xb0, 0x5b, + 0x3c, 0xf9, 0xbf, 0x12, 0xdf, 0x2f, 0xa0, 0x82, 0x97, 0x1e, 0x41, 0xbb, 0x50, 0xf5, 0x1d, 0x3a, + 0x7f, 0x23, 0xa3, 0x11, 0x0b, 0x16, 0xca, 0x95, 0xeb, 0x51, 0x12, 0xf3, 0x2b, 0x6c, 0x62, 0xb9, + 0xd2, 0xff, 0xac, 0x40, 0xed, 0x84, 0x93, 0xe8, 0x0b, 0xa8, 0xc6, 0x4b, 0x16, 0xac, 0xa8, 0x75, + 0x2d, 0xef, 0x01, 0xb3, 0x8c, 0xc5, 0x36, 0x1a, 0x41, 0xeb, 0xca, 0x25, 0xde, 0x82, 0x97, 0xee, + 0x38, 0x5c, 0x88, 0xac, 0x68, 0x75, 0x3f, 0xcf, 0x2b, 0x08, 0x9b, 0x9d, 0x93, 0x82, 0x20, 0xbe, + 0xa7, 0xa8, 0x3f, 0x83, 0x56, 0x51, 0x82, 0x95, 0x93, 0x81, 0xb1, 0x3d, 0x9d, 0xd8, 0xe3, 0xd1, + 0x6c, 0xdc, 0xb3, 0x06, 0xcf, 0xb5, 0x47, 0xbc, 0x62, 0x8c, 0x99, 0x65, 0x1b, 0x27, 0x27, 0x53, + 0x6c, 0x69, 0x8a, 0xfe, 0xaf, 0x12, 0x6c, 0x08, 0x50, 0x66, 0xe1, 0x32, 0x9e, 0x13, 0x76, 0x8b, + 0x37, 0x64, 0x95, 0x44, 0xce, 0x9c, 0xa4, 0xb7, 0x98, 0xae, 0x19, 0x20, 0xc9, 0x1b, 0x27, 0x5e, + 0xc8, 0xc8, 0xc5, 0x02, 0xfd, 0x12, 0x54, 0x7e, 0x9b, 0xd4, 0xa6, 0xab, 0x88, 0xf0, 0x7b, 0x6c, + 0x75, 0x77, 0xd7, 0x89, 0xcd, 0xef, 0x8a, 0x5a, 0xab, 0x88, 0x60, 0xa0, 0x19, 0x5d, 0xac, 0x86, + 0xca, 0x03, 0xaa, 0x61, 0x9d, 0x43, 0xd5, 0x42, 0x0e, 0x1d, 0x65, 0x17, 0x52, 0x93, 0x56, 0xde, + 0x41, 0x2f, 0xbd, 0x24, 0xd4, 0x81, 0x5a, 0x18, 0xd8, 0x8b, 0x85, 0xd7, 0xae, 0x73, 0x37, 0x3f, + 0xc9, 0xcb, 0x4e, 0x83, 0xe1, 0xd0, 0xec, 0x89, 0xb4, 0xa8, 0x86, 0xc1, 0x70, 0xe1, 0xa1, 0x27, + 0xd0, 0x22, 0xdf, 0x51, 0x12, 0x07, 0x8e, 0x67, 0xfb, 0x2b, 0xd6, 0xbd, 0x1a, 0x3c, 0xf4, 0xcd, + 0x94, 0x3b, 0x66, 0x4c, 0xf4, 0x05, 0x6c, 0x25, 0x34, 0x8c, 0x6c, 0xe7, 0x8a, 0x92, 0xd8, 0x9e, + 0x87, 0xd1, 0xaa, 0xdd, 0x3c, 0x50, 0x0e, 0x1b, 0x78, 0x93, 0xb1, 0x7b, 0x8c, 0x3b, 0x08, 0xa3, + 0x95, 0xfe, 0x12, 0x9a, 0x38, 0xbc, 0x1b, 0xbc, 0xe1, 0xf1, 0xe8, 0x50, 0xbb, 0x24, 0x57, 0x61, + 0x4c, 0x64, 0xa2, 0x82, 0x6c, 0xe4, 0x38, 0xbc, 0xc3, 0x72, 0x07, 0x1d, 0x40, 0x95, 0xdb, 0x94, + 0xed, 0x22, 0x2f, 0x22, 0x36, 0x74, 0x07, 0x1a, 0x38, 0xbc, 0xe3, 0xd7, 0x8e, 0x3e, 0x05, 0x01, + 0xb0, 0x1d, 0x38, 0x7e, 0x7a, 0x7b, 0x4d, 0xce, 0x99, 0x38, 0x3e, 0x41, 0xcf, 0x40, 0x8d, 0xc3, + 0x3b, 0x7b, 0xce, 0x8f, 0x17, 0x95, 0xa8, 0x76, 0xf7, 0x0a, 0xc9, 0x99, 0x3a, 0x87, 0x21, 0x4e, + 0xc9, 0x44, 0x7f, 0x09, 0xb0, 0xce, 0xad, 0x0f, 0x1d, 0xf2, 0x13, 0x76, 0x1b, 0xc4, 0x5b, 0xa4, + 0xf6, 0x37, 0xa4, 0xcb, 0xdc, 0x02, 0x96, 0x7b, 0x0c, 0x88, 0x19, 0x4b, 0x9e, 0x53, 0xea, 0x2e, + 0x7e, 0x40, 0xca, 0x21, 0xa8, 0x5c, 0x53, 0x77, 0xc1, 0x73, 0xad, 0x89, 0x39, 0xad, 0x7f, 0x03, + 0xd5, 0x0b, 0x6e, 0xee, 0x19, 0xa8, 0x5c, 0xca, 0x66, 0xec, 0xb4, 0x06, 0x0b, 0x61, 0x66, 0x47, + 0x63, 0x48, 0x52, 0x32, 0xd1, 0x7b, 0xb0, 0x79, 0x26, 0x8f, 0xe5, 0x02, 0x1f, 0xef, 0x97, 0xfe, + 0x97, 0x12, 0xd4, 0x5f, 0x84, 0x4b, 0x96, 0x18, 0xa8, 0x05, 0x25, 0x77, 0xc1, 0xf5, 0xca, 0xb8, + 0xe4, 0x2e, 0xd0, 0x6f, 0xa0, 0xe5, 0xbb, 0xd7, 0xb1, 0xc3, 0xd2, 0x4b, 0x54, 0x8a, 0x28, 0xf6, + 0x1f, 0xe5, 0x3d, 0x1b, 0xa7, 0x12, 0xbc, 0x5c, 0x36, 0xfd, 0xfc, 0x32, 0x57, 0x00, 0xe5, 0x42, + 0x01, 0x3c, 0x81, 0x96, 0x17, 0xce, 0x1d, 0xcf, 0xce, 0xda, 0x6f, 0x45, 0x24, 0x29, 0xe7, 0x9e, + 0xa7, 0x3d, 0xf8, 0x1e, 0x2e, 0xd5, 0x07, 0xe2, 0x82, 0xbe, 0x86, 0x8d, 0xc8, 0x89, 0xa9, 0x3b, + 0x77, 0x23, 0x87, 0x0d, 0x30, 0x35, 0xae, 0x58, 0x70, 0xbb, 0x80, 0x1b, 0x2e, 0x88, 0xa3, 0x2f, + 0x41, 0x4b, 0x78, 0x6b, 0xb1, 0xef, 0xc2, 0xf8, 0xe6, 0xca, 0x0b, 0xef, 0x92, 0x76, 0x9d, 0xfb, + 0xbf, 0x25, 0xf8, 0xaf, 0x53, 0xb6, 0xfe, 0xef, 0x12, 0xd4, 0x2e, 0x44, 0x96, 0x1d, 0x41, 0x85, + 0x63, 0x24, 0x86, 0x94, 0xfd, 0xfc, 0x61, 0x42, 0x82, 0x03, 0xc4, 0x65, 0xd0, 0x8f, 0xa1, 0x49, + 0x5d, 0x9f, 0x24, 0xd4, 0xf1, 0x23, 0x0e, 0x6a, 0x19, 0xaf, 0x19, 0xdf, 0x97, 0x2b, 0x6c, 0x12, + 0x61, 0x3d, 0x40, 0xc0, 0xc4, 0x48, 0xf4, 0x15, 0x34, 0x59, 0x6d, 0xf0, 0xc1, 0xa9, 0x5d, 0xe5, + 0xc5, 0xb6, 0x7b, 0xaf, 0x32, 0xf8, 0xb1, 0xb8, 0x11, 0xa7, 0xd5, 0xf6, 0x2b, 0x50, 0x79, 0x36, + 0x4b, 0x25, 0xd1, 0x7c, 0xf6, 0x8b, 0xcd, 0x27, 0xad, 0x1a, 0x0c, 0xeb, 0x7e, 0x8d, 0x9e, 0x42, + 0xf5, 0x96, 0xbb, 0x54, 0x97, 0x03, 0x5c, 0x3e, 0x38, 0x0e, 0xbf, 0xd8, 0x67, 0x5f, 0xc7, 0xdf, + 0x89, 0x6c, 0xe2, 0x6d, 0xe7, 0xde, 0xd7, 0x51, 0x26, 0x1a, 0x4e, 0x65, 0x78, 0x54, 0xbe, 0xc7, + 0x3b, 0x0f, 0x8b, 0xca, 0xf7, 0xd0, 0xe7, 0xb0, 0x31, 0x5f, 0xc6, 0x31, 0x1f, 0x19, 0x5d, 0x9f, + 0xb4, 0x77, 0x39, 0x38, 0xaa, 0xe4, 0x59, 0xae, 0x4f, 0x74, 0x17, 0x36, 0x79, 0x83, 0x1e, 0x13, + 0xea, 0x0c, 0x1d, 0xea, 0x30, 0xbc, 0x72, 0x95, 0xcd, 0xe9, 0x87, 0x15, 0x35, 0xfa, 0x0c, 0xd4, + 0xc8, 0xbe, 0xb1, 0xe7, 0xa1, 0xb7, 0xf4, 0x03, 0x91, 0xa4, 0x65, 0xdc, 0x8c, 0xce, 0x06, 0x82, + 0xa1, 0x9b, 0xf0, 0x49, 0xe1, 0xa8, 0x41, 0xe8, 0x79, 0x44, 0x8c, 0xc8, 0x5f, 0x65, 0xa9, 0xad, + 0xbc, 0x9b, 0x5d, 0x05, 0xa5, 0x34, 0xeb, 0xf5, 0x3f, 0x94, 0xa0, 0x75, 0x21, 0xa6, 0x81, 0x74, + 0x02, 0xf9, 0x06, 0x76, 0xc8, 0xd5, 0x15, 0x33, 0x79, 0x4b, 0xec, 0xb9, 0xe3, 0x79, 0x24, 0xb6, + 0x65, 0x0d, 0xaa, 0xdd, 0xad, 0x8e, 0x78, 0x15, 0x0c, 0x38, 0x7f, 0x34, 0xc4, 0xdb, 0x99, 0xac, + 0x64, 0x2d, 0x90, 0x01, 0x3b, 0xae, 0xef, 0x93, 0x85, 0xeb, 0xd0, 0xbc, 0x01, 0xd1, 0x7c, 0xf7, + 0x64, 0xd0, 0x17, 0xd6, 0xa9, 0x43, 0xc9, 0xda, 0x4c, 0xa6, 0x91, 0x99, 0x79, 0xc2, 0xa2, 0x89, + 0xaf, 0xb3, 0xa1, 0x66, 0x53, 0x6a, 0x5a, 0x9c, 0x89, 0xe5, 0x66, 0x61, 0x60, 0xaa, 0xdc, 0x1b, + 0x98, 0xd6, 0x1f, 0xb5, 0xea, 0x87, 0x3e, 0x6a, 0xfa, 0xd7, 0xb0, 0x95, 0x01, 0x21, 0x07, 0xa2, + 0x23, 0xa8, 0xf1, 0xac, 0x4c, 0xf1, 0x44, 0xef, 0x16, 0x10, 0x96, 0x12, 0xfa, 0xef, 0x4b, 0x80, + 0x52, 0xfd, 0xf0, 0x2e, 0xf9, 0x1f, 0x05, 0x73, 0x17, 0xaa, 0x9c, 0x2f, 0x91, 0x14, 0x0b, 0x86, + 0x83, 0xe7, 0x24, 0x34, 0xba, 0xc9, 0x60, 0x14, 0xca, 0x2f, 0xd9, 0x2f, 0x26, 0xc9, 0xd2, 0xa3, + 0x58, 0x4a, 0xe8, 0x7f, 0x55, 0x60, 0xa7, 0x80, 0x83, 0xc4, 0x72, 0x9d, 0xfc, 0xca, 0x7b, 0x92, + 0xff, 0x10, 0x1a, 0xd1, 0xcd, 0x7b, 0x8a, 0x24, 0xdb, 0xfd, 0xde, 0x86, 0xf4, 0x19, 0x54, 0x62, + 0xd6, 0x18, 0x2b, 0x5c, 0x33, 0xff, 0x99, 0xe7, 0x7c, 0x36, 0x2b, 0x14, 0xe2, 0x28, 0xcc, 0x0a, + 0xd2, 0xff, 0xbf, 0x2b, 0xb0, 0xb7, 0xce, 0x83, 0xa5, 0x47, 0xff, 0xaf, 0xae, 0x52, 0x8f, 0x61, + 0xff, 0x7e, 0x74, 0x1f, 0x75, 0x41, 0x3f, 0x00, 0xf6, 0xa3, 0x5f, 0x83, 0x9a, 0x1b, 0x0a, 0xd9, + 0xdb, 0x71, 0x74, 0x3a, 0x99, 0x62, 0x43, 0x7b, 0x84, 0x1a, 0x50, 0x99, 0x59, 0xd3, 0x73, 0x4d, + 0x61, 0x94, 0xf1, 0x5b, 0x63, 0x20, 0xde, 0xa3, 0x8c, 0xb2, 0xa5, 0x50, 0xf9, 0xe8, 0x1f, 0x0a, + 0xc0, 0xfa, 0x73, 0x85, 0x54, 0xa8, 0xbf, 0x9a, 0x9c, 0x4d, 0xa6, 0xaf, 0x27, 0xc2, 0xc0, 0xa9, + 0x35, 0x1a, 0x6a, 0x0a, 0x6a, 0x42, 0x55, 0x3c, 0x70, 0x4b, 0xec, 0x04, 0xf9, 0xba, 0x2d, 0xb3, + 0xa7, 0x6f, 0xf6, 0xb4, 0xad, 0xa0, 0x3a, 0x94, 0xb3, 0x07, 0xac, 0x7c, 0xb1, 0xd6, 0x98, 0x41, + 0x6c, 0x9c, 0x9b, 0xbd, 0x81, 0xa1, 0xd5, 0xd9, 0x46, 0xf6, 0x76, 0x05, 0xa8, 0xa5, 0x0f, 0x57, + 0xa6, 0xc9, 0x9e, 0xbb, 0xc0, 0xce, 0x99, 0x5a, 0xcf, 0x0d, 0xac, 0xa9, 0x8c, 0x87, 0xa7, 0xaf, + 0xb5, 0x0d, 0xc6, 0x3b, 0x19, 0x19, 0xe6, 0x50, 0xdb, 0x64, 0xef, 0xdd, 0xe7, 0x46, 0x0f, 0x5b, + 0x7d, 0xa3, 0x67, 0x69, 0x2d, 0xb6, 0x73, 0xc1, 0x1d, 0xdc, 0x62, 0xc7, 0xbc, 0x98, 0xbe, 0xc2, + 0x93, 0x9e, 0xa9, 0x69, 0x6c, 0x71, 0x61, 0xe0, 0xd9, 0x68, 0x3a, 0xd1, 0xb6, 0x8f, 0x9e, 0xc2, + 0x66, 0x61, 0x64, 0x61, 0x07, 0x5b, 0xbd, 0xbe, 0x69, 0xcc, 0xb4, 0x47, 0x8c, 0x9e, 0x3d, 0xef, + 0xe1, 0xe1, 0x4c, 0x53, 0xfa, 0x5f, 0x7e, 0xfb, 0xf4, 0xd6, 0xa5, 0x24, 0x49, 0x3a, 0x6e, 0x78, + 0x2c, 0xa8, 0xe3, 0xeb, 0xf0, 0xf8, 0x96, 0x1e, 0xf3, 0x3f, 0x62, 0x8e, 0xd7, 0xed, 0xe9, 0xb2, + 0xc6, 0x39, 0x3f, 0xff, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x15, 0xca, 0xd6, 0xe6, 0xe4, 0x11, + 0x00, 0x00, } diff --git a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go index 6204c124a8f..8597254b05b 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go +++ b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go @@ -88,7 +88,8 @@ func (ec *externalConnector) Get(name string) (*mysqlConnector, error) { c := &mysqlConnector{} c.env = tabletenv.NewEnv(config, name) c.se = schema.NewEngine(c.env) - c.vstreamer = vstreamer.NewEngine(c.env, nil, c.se) + sh := schema.NewHistorian(c.se) + c.vstreamer = vstreamer.NewEngine(c.env, nil, sh) c.se.InitDBConfig(c.env.Config().DB.DbaWithDB()) // Open diff --git a/go/vt/vttablet/tabletserver/replication_watcher.go b/go/vt/vttablet/tabletserver/replication_watcher.go index a1578d94ab8..61412243860 100644 --- a/go/vt/vttablet/tabletserver/replication_watcher.go +++ b/go/vt/vttablet/tabletserver/replication_watcher.go @@ -87,7 +87,7 @@ func (rpw *ReplicationWatcher) Process(ctx context.Context) { } for { - // VStreamer will reload the schema when it encounters a DDL. + // The tracker will reload the schema and save it into _vt.schema_tracking when the vstream encounters a DDL. err := rpw.vs.Stream(ctx, "current", filter, func(events []*binlogdatapb.VEvent) error { for _, event := range events { if event.Type == binlogdatapb.VEventType_DDL { diff --git a/go/vt/vttablet/tabletserver/schema/engine.go b/go/vt/vttablet/tabletserver/schema/engine.go index f419714d017..d592ea1a90e 100644 --- a/go/vt/vttablet/tabletserver/schema/engine.go +++ b/go/vt/vttablet/tabletserver/schema/engine.go @@ -63,7 +63,6 @@ type Engine struct { // and do not require locking mu. conns *connpool.Pool ticks *timer.Timer - Sh HistorianInterface } // NewEngine creates a new Engine. @@ -181,6 +180,7 @@ func (se *Engine) Reload(ctx context.Context) error { se.mu.Lock() defer se.mu.Unlock() if !se.isOpen { + log.Warning("Schema reload called for an engine that is not yet open") return nil } return se.reload(ctx) @@ -217,6 +217,7 @@ func (se *Engine) reload(ctx context.Context) error { changedTables := make(map[string]*Table) // created and altered contain the names of created and altered tables for broadcast. var created, altered []string + log.Infof("In se Reload with %d tables found", len(tableData.Rows)) for _, row := range tableData.Rows { tableName := row[0].ToString() curTables[tableName] = true @@ -345,11 +346,6 @@ func (se *Engine) broadcast(created, altered, dropped []string) { } } -// GetTableForPos returns the info for a table. -func (se *Engine) GetTableForPos(tableName sqlparser.TableIdent, pos string) *Table { - return se.Sh.GetTableForPos(tableName, pos) -} - // GetTable returns the info for a table. func (se *Engine) GetTable(tableName sqlparser.TableIdent) *Table { se.mu.Lock() @@ -416,7 +412,3 @@ func (se *Engine) SetTableForTests(table *Table) { defer se.mu.Unlock() se.tables[table.Name.String()] = table } - -func (se *Engine) SetHistorian(sh *Historian) { - se.Sh = sh -} diff --git a/go/vt/vttablet/tabletserver/schema/historian.go b/go/vt/vttablet/tabletserver/schema/historian.go index dcc960f4280..9a16d0e86ff 100644 --- a/go/vt/vttablet/tabletserver/schema/historian.go +++ b/go/vt/vttablet/tabletserver/schema/historian.go @@ -18,36 +18,241 @@ package schema import ( "context" + "encoding/json" + "fmt" + "sort" + "sync" + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/proto/binlogdata" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/vtgate/evalengine" + "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" + "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" + "vitess.io/vitess/go/vt/sqlparser" ) -type HistorianInterface interface { - RegisterVersionEvent(version *binlogdata.Version) - GetTableForPos(tableName sqlparser.TableIdent, pos string) *Table +const GetSchemaVersions = "select id, pos, ddl, time_updated, schemax from _vt.schema_version where id >= %d order by id desc" + +// Historian defines the interface to reload a db schema or get the schema of a table for a given position +type Historian interface { + RegisterVersionEvent() + Reload(ctx context.Context) error + GetTableForPos(tableName sqlparser.TableIdent, pos string) *binlogdata.TableMetaData + Open() error + Init(tabletType topodatapb.TabletType) error } +// HistoryEngine exposes only those schema.Engine APIs that are needed by the HistorianSvc +// We define an interface to facilitate a mock for testing +var _ HistoryEngine = (*Engine)(nil) + +// HistoryEngine defines the interface which is implemented by schema.Engine for the historian to get current schema +// information from the database type HistoryEngine interface { + Open() error Reload(ctx context.Context) error - GetTable(ident sqlparser.TableIdent) *Table + GetConnection(ctx context.Context) (*connpool.DBConn, error) + GetSchema() map[string]*Table +} + +// TODO: rename TableMetaData and TableMetaDataCollection: to TableLite and SchemaLite? + +// TrackedSchema has the snapshot of the table at a given pos (reached by ddl) +type TrackedSchema struct { + schema map[string]*binlogdata.TableMetaData + pos mysql.Position + ddl string +} + +var _ Historian = (*HistorianSvc)(nil) + +// HistorianSvc implements the Historian interface by calling schema.Engine for the underlying schema +// and supplying a schema for a specific version by loading the cached values from the schema_version table +// The schema version table is populated by the Tracker +type HistorianSvc struct { + he HistoryEngine + lastId int + schemas []*TrackedSchema + isMaster bool + mu sync.Mutex + trackSchemaVersions bool + lastSchema map[string]*binlogdata.TableMetaData +} + +// Reload gets the latest schema from the database +func (h *HistorianSvc) Reload(ctx context.Context) error { + return h.reload(ctx) +} + +// NewHistorian creates a new historian. It expects a schema.Engine instance +func NewHistorian(he HistoryEngine) *HistorianSvc { + sh := HistorianSvc{he: he} + sh.trackSchemaVersions = true //TODO use flag + + return &sh +} + +// readRow converts a row from the schema_version table to a TrackedSchema +func (h *HistorianSvc) readRow(row []sqltypes.Value) (*TrackedSchema, error) { + id, _ := evalengine.ToInt64(row[0]) + pos, err := mysql.DecodePosition(string(row[1].ToBytes())) + if err != nil { + return nil, err + } + ddl := string(row[2].ToBytes()) + timeUpdated, _ := evalengine.ToInt64(row[3]) + sch := &binlogdata.TableMetaDataCollection{} + if err := json.Unmarshal(row[4].ToBytes(), sch); err != nil { + return nil, err + } + log.Infof("Read tracked schema from db: id %d, pos %v, ddl %s, schema len %d, time_updated %d \n", id, pos, ddl, len(sch.Tables), timeUpdated) + + tables := map[string]*binlogdata.TableMetaData{} + for _, t := range sch.Tables { + tables[t.Name] = t + } + trackedSchema := &TrackedSchema{ + schema: tables, + pos: pos, + ddl: ddl, + } + return trackedSchema, nil +} + +// loadFromDB loads all rows from the schema_version table that the historian does not have as yet +func (h *HistorianSvc) loadFromDB(ctx context.Context, lastId int) error { + conn, err := h.he.GetConnection(ctx) + if err != nil { + return err + } + defer conn.Recycle() + log.Infof("In loadFromDb for lastId %d", lastId) + tableData, err := conn.Exec(ctx, fmt.Sprintf(GetSchemaVersions, lastId), 1, true) + if err != nil { + log.Errorf("Error reading schema_tracking table %v", err) + return err + } + if len(tableData.Rows) > 0 { + log.Infof("Received rows from schema_version: %v", tableData.Rows) + trackedSchema, err := h.readRow(tableData.Rows[0]) + if err != nil { + return err + } + h.mu.Lock() + defer h.mu.Unlock() + h.schemas = append(h.schemas, trackedSchema) + h.sortSchemas() + } + return nil +} + +func (h *HistorianSvc) sortSchemas() { + sort.Slice(h.schemas, func(i int, j int) bool { + return h.schemas[j].pos.AtLeast(h.schemas[i].pos) + }) +} + +// Init needs to be called once the tablet's type is known. It also warms the cache from the db +func (h *HistorianSvc) Init(tabletType topodatapb.TabletType) error { + h.Open() + ctx := tabletenv.LocalContext() + + if !h.trackSchemaVersions { + return nil + } + + h.isMaster = tabletType == topodatapb.TabletType_MASTER + if h.isMaster { + h.Reload(ctx) + if err := h.loadFromDB(ctx, h.lastId); err != nil { + return err + } + } + return nil +} + +// Open opens the underlying schema Engine. Called directly by any user of the historian other than tabletserver which calls Init +func (h *HistorianSvc) Open() error { + return h.he.Open() } -type Historian struct { - se HistoryEngine +// reload gets the latest schema and replaces the latest copy of the schema maintained by the historian +func (h *HistorianSvc) reload(ctx context.Context) error { + log.Info("In historian reload") + if err := h.he.Reload(ctx); err != nil { + return err + } + tables := map[string]*binlogdata.TableMetaData{} + log.Infof("Schema returned by engine is %v", h.he.GetSchema()) + for _, t := range h.he.GetSchema() { + table := &binlogdata.TableMetaData{ + Name: t.Name.String(), + Fields: t.Fields, + } + pkc := make([]int64, 0) + for _, pk := range t.PKColumns { + pkc = append(pkc, int64(pk)) + } + table.PKColumns = pkc + tables[table.Name] = table + log.Infof("Historian, found table %s", table.Name) + } + h.lastSchema = tables + return nil } -//TODO first time called warm cache, drop cache on last -func NewSchemaHistorian(se HistoryEngine) *Historian { - return &Historian{se: se} +// GetTableForPos returns a best-effort schema for a specific gtid +func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid string) *binlogdata.TableMetaData { + log.Infof("GetTableForPos called for %s with pos %s", tableName, gtid) + if gtid != "" { + pos, err := mysql.DecodePosition(gtid) + if err != nil { + log.Errorf("Error decoding position for %s: %v", gtid, err) + return nil + } + var t *binlogdata.TableMetaData + if h.trackSchemaVersions && len(h.schemas) > 0 { + t = h.getTableFromHistoryForPos(tableName, pos) + } + if t != nil { + log.Infof("Returning table %s from history for pos %s, schema %s", tableName, gtid, t) + return t + } + } + if h.lastSchema == nil { + return nil + } + log.Infof("Returning table %s from latest schema for pos %s, schema %s", tableName, gtid, h.lastSchema[tableName.String()]) + return h.lastSchema[tableName.String()] } -//Placeholder TODO, currently returns Table not TableMetaData -func (h *Historian) GetTableForPos(tableName sqlparser.TableIdent, pos string) *Table { - h.se.Reload(context.Background()) - _ = pos - return h.se.GetTable(tableName) +// getTableFromHistoryForPos looks in the cache for a schema for a specific gtid +func (h *HistorianSvc) getTableFromHistoryForPos(tableName sqlparser.TableIdent, pos mysql.Position) *binlogdata.TableMetaData { + idx := sort.Search(len(h.schemas), func(i int) bool { + return !pos.AtLeast(h.schemas[i].pos) + }) + log.Infof("Found index of schema to be %d", idx) + if idx >= len(h.schemas) || idx == 0 && !h.schemas[idx].pos.Equal(pos) { + log.Infof("Not found schema in cache for %s with pos %s", tableName, pos) + return nil + } else { + log.Infof("Found tracked schema. Looking for %s, found %s", pos, h.schemas[idx]) + return h.schemas[idx].schema[tableName.String()] + } } -func (h *Historian) RegisterVersionEvent(version *binlogdata.Version) { +// RegisterVersionEvent is called by the vstream when it encounters a version event (an insert into _vt.schema_tracking) +// It triggers the historian to load the newer rows from the database to update its cache +func (h *HistorianSvc) RegisterVersionEvent() { + if !h.trackSchemaVersions { + return + } + ctx := tabletenv.LocalContext() + if err := h.loadFromDB(ctx, h.lastId); err != nil { + log.Error("Error loading schema version information from database in RegisterVersionEvent(): %v", err) + } } diff --git a/go/vt/vttablet/tabletserver/schema/tracker.go b/go/vt/vttablet/tabletserver/schema/tracker.go index a422ed3722b..314a9ee73df 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker.go +++ b/go/vt/vttablet/tabletserver/schema/tracker.go @@ -25,16 +25,16 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/log" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - querypb "vitess.io/vitess/go/vt/proto/query" "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" ) -const createSchemaTrackingTable = `CREATE TABLE IF NOT EXISTS _vt.schema_tracking ( +//todo: pos as primary key, add timestamp and use instead of id and use default AND on update current_timestamp ... +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, - schema longblob NOT NULL, + schemax BLOB NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB` @@ -51,40 +51,43 @@ type SchemaSubscriber interface { var _ SchemaSubscriber = (*Tracker)(nil) +// Tracker implements SchemaSubscriber and persists versions into the ddb type Tracker struct { engine trackerEngine } -type TableMetaData struct { - Name string - Fields []*querypb.Field - PKColumns []int -} - +// NewTracker creates a Tracker, needs a SchemaEngine (which implements the trackerEngine interface) func NewTracker(engine trackerEngine) *Tracker { return &Tracker{engine: engine} } +// SchemaUpdated is called by a vstream when it encounters a DDL //TODO multiple might come for same pos, ok? func (st *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) { + + if gtid == "" || ddl == "" { + log.Errorf("Got invalid gtid or ddl in SchemaUpdated") + return + } ctx := context.Background() st.engine.Reload(ctx) - newSchema := st.engine.GetSchema() + tables := st.engine.GetSchema() dbSchema := &binlogdatapb.TableMetaDataCollection{ Tables: []*binlogdatapb.TableMetaData{}, } - blob, err := proto.Marshal(dbSchema) - for name, table := range newSchema { + for name, table := range tables { t := &binlogdatapb.TableMetaData{ Name: name, Fields: table.Fields, - //Pkcolumns: table.PKColumns, } + pks := make([]int64, 0) + for _, pk := range table.PKColumns { + pks = append(pks, int64(pk)) + } + t.PKColumns = pks dbSchema.Tables = append(dbSchema.Tables, t) } - query := fmt.Sprintf("insert into _vt.schema_version "+ - "(pos, ddl, schema, time_updated) "+ - "values (%v, %v, %v, %d)", gtid, ddl, encodeString(string(blob)), timestamp) + blob, err := proto.Marshal(dbSchema) conn, err := st.engine.GetConnection(ctx) if err != nil { @@ -97,10 +100,15 @@ func (st *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) { log.Errorf("Error creating schema_tracking table %v", err) return } - log.Infof("Inserting version for position %s", gtid) + + log.Infof("Inserting version for position %s: %+v", gtid, dbSchema) + query := fmt.Sprintf("insert ignore into _vt.schema_version "+ + "(pos, ddl, schemax, time_updated) "+ + "values (%v, %v, %v, %d)", encodeString(gtid), encodeString(ddl), encodeString(string(blob)), timestamp) + _, err = conn.Exec(ctx, query, 1, false) if err != nil { - log.Errorf("Error inserting version for position %s %v", gtid, err) + log.Errorf("Error inserting version for position %s, ddl %s, %v", gtid, ddl, err) return } } diff --git a/go/vt/vttablet/tabletserver/schema/tracker_test.go b/go/vt/vttablet/tabletserver/schema/tracker_test.go index 0cebd1735f4..b9e149c53eb 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker_test.go +++ b/go/vt/vttablet/tabletserver/schema/tracker_test.go @@ -1,30 +1 @@ - package schema - -import ( - "fmt" - "testing" - querypb "vitess.io/vitess/go/vt/proto/query" - "vitess.io/vitess/go/vt/sqlparser" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" -) - -func TestSerialization(t *testing.T) { - m := map[string]*schema.Table{ - "foo":&schema.Table{ - Name: sqlparser.NewTableIdent("oh noes"), - Fields: []*querypb.Field{{ - Name: "column", - Type: querypb.Type_VARCHAR, - }}, - PKColumns: nil, - Type: 0, - SequenceInfo: nil, - MessageInfo: nil, - }, - } - - b := ToGOB(m) - newMap := FromGOB(b) - fmt.Printf("%v", newMap) -} \ No newline at end of file diff --git a/go/vt/vttablet/tabletserver/tabletenv/config.go b/go/vt/vttablet/tabletserver/tabletenv/config.go index d8611ff1746..1c3eb81c5a6 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/config.go +++ b/go/vt/vttablet/tabletserver/tabletenv/config.go @@ -109,6 +109,7 @@ func init() { flag.BoolVar(¤tConfig.TerseErrors, "queryserver-config-terse-errors", defaultConfig.TerseErrors, "prevent bind vars from escaping in returned errors") flag.StringVar(&deprecatedPoolNamePrefix, "pool-name-prefix", "", "Deprecated") flag.BoolVar(¤tConfig.WatchReplication, "watch_replication_stream", false, "When enabled, vttablet will stream the MySQL replication stream from the local server, and use it to support the include_event_token ExecuteOptions.") + flag.BoolVar(¤tConfig.TrackSchemaVersions, "track_schema_versions", false, "When enabled, vttablet will store versions of schemas at each position that a DDL is applied and allow retrieval of the schema corresponding to a position") flag.BoolVar(&deprecatedAutocommit, "enable-autocommit", true, "This flag is deprecated. Autocommit is always allowed.") flag.BoolVar(¤tConfig.TwoPCEnable, "twopc_enable", defaultConfig.TwoPCEnable, "if the flag is on, 2pc is enabled. Other 2pc flags must be supplied.") flag.StringVar(¤tConfig.TwoPCCoordinatorAddress, "twopc_coordinator_address", defaultConfig.TwoPCCoordinatorAddress, "address of the (VTGate) process(es) that will be used to notify of abandoned transactions.") @@ -205,6 +206,7 @@ type TabletConfig struct { QueryCacheSize int `json:"queryCacheSize,omitempty"` SchemaReloadIntervalSeconds int `json:"schemaReloadIntervalSeconds,omitempty"` WatchReplication bool `json:"watchReplication,omitempty"` + TrackSchemaVersions bool `json:"trackSchemaVersions,omitempty"` TerseErrors bool `json:"terseErrors,omitempty"` MessagePostponeParallelism int `json:"messagePostponeParallelism,omitempty"` CacheResultFields bool `json:"cacheResultFields,omitempty"` diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 3f47fb9928f..06b64209ab3 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -193,6 +193,7 @@ type TabletServer struct { // alias is used for identifying this tabletserver in healthcheck responses. alias topodatapb.TabletAlias + sh schema.Historian } // RegisterFunctions is a list of all the @@ -228,8 +229,7 @@ func NewTabletServer(name string, config *tabletenv.TabletConfig, topoServer *to alias: alias, } tsv.se = schema.NewEngine(tsv) - sh := schema.NewSchemaHistorian(tsv.se) - tsv.se.SetHistorian(sh) + tsv.sh = schema.NewHistorian(tsv.se) tsv.qe = NewQueryEngine(tsv, tsv.se) tsv.te = NewTxEngine(tsv) tsv.txController = tsv.te @@ -237,7 +237,7 @@ func NewTabletServer(name string, config *tabletenv.TabletConfig, topoServer *to tsv.hr = heartbeat.NewReader(tsv) tsv.txThrottler = txthrottler.NewTxThrottler(tsv.config, topoServer) tsOnce.Do(func() { srvTopoServer = srvtopo.NewResilientServer(topoServer, "TabletSrvTopo") }) - tsv.vstreamer = vstreamer.NewEngine(tsv, srvTopoServer, tsv.se) + tsv.vstreamer = vstreamer.NewEngine(tsv, srvTopoServer, tsv.sh) schemaTracker := schema.NewTracker(tsv.se) tsv.watcher = NewReplicationWatcher(tsv, tsv.vstreamer, tsv.config, schemaTracker) tsv.messager = messager.NewEngine(tsv, tsv.se, tsv.vstreamer) @@ -412,6 +412,7 @@ func (tsv *TabletServer) StartService(target querypb.Target, dbcfgs *dbconfigs.D return err } _ /* state changed */, err = tsv.SetServingType(tabletType, true, nil) + tsv.sh.Init(tabletType) return err } diff --git a/go/vt/vttablet/tabletserver/vstreamer/engine.go b/go/vt/vttablet/tabletserver/vstreamer/engine.go index 834f5c5d878..9f107aa8970 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/engine.go +++ b/go/vt/vttablet/tabletserver/vstreamer/engine.go @@ -63,7 +63,7 @@ type Engine struct { // The following members are initialized once at the beginning. ts srvtopo.Server - se *schema.Engine + sh schema.Historian keyspace string cell string @@ -74,7 +74,7 @@ type Engine struct { // NewEngine creates a new Engine. // Initialization sequence is: NewEngine->InitDBConfig->Open. // Open and Close can be called multiple times and are idempotent. -func NewEngine(env tabletenv.Env, ts srvtopo.Server, se *schema.Engine) *Engine { +func NewEngine(env tabletenv.Env, ts srvtopo.Server, sh schema.Historian) *Engine { vse := &Engine{ env: env, streamers: make(map[int]*vstreamer), @@ -82,7 +82,7 @@ func NewEngine(env tabletenv.Env, ts srvtopo.Server, se *schema.Engine) *Engine resultStreamers: make(map[int]*resultStreamer), lvschema: &localVSchema{vschema: &vindexes.VSchema{}}, ts: ts, - se: se, + sh: sh, vschemaErrors: env.Exporter().NewCounter("VSchemaErrors", "Count of VSchema errors"), vschemaUpdates: env.Exporter().NewCounter("VSchemaUpdates", "Count of VSchema updates. Does not include errors"), } @@ -156,7 +156,7 @@ func (vse *Engine) Stream(ctx context.Context, startPos string, filter *binlogda if !vse.isOpen { return nil, 0, errors.New("VStreamer is not open") } - streamer := newVStreamer(ctx, vse.env.Config().DB.AppWithDB(), vse.se, startPos, filter, vse.lvschema, send) + streamer := newVStreamer(ctx, vse.env.Config().DB.AppWithDB(), vse.sh, startPos, filter, vse.lvschema, send) idx := vse.streamIdx vse.streamers[idx] = streamer vse.streamIdx++ @@ -196,7 +196,7 @@ func (vse *Engine) StreamRows(ctx context.Context, query string, lastpk []sqltyp if !vse.isOpen { return nil, 0, errors.New("VStreamer is not open") } - rowStreamer := newRowStreamer(ctx, vse.env.Config().DB.AppWithDB(), vse.se, query, lastpk, vse.lvschema, send) + rowStreamer := newRowStreamer(ctx, vse.env.Config().DB.AppWithDB(), vse.sh, query, lastpk, vse.lvschema, send) idx := vse.streamIdx vse.rowStreamers[idx] = rowStreamer vse.streamIdx++ diff --git a/go/vt/vttablet/tabletserver/vstreamer/main_test.go b/go/vt/vttablet/tabletserver/vstreamer/main_test.go index 080817d0803..192ac3835a0 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/main_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/main_test.go @@ -25,13 +25,17 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/dbconfigs" + tabletpb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer/testenv" ) var ( - engine *Engine - env *testenv.Env + engine *Engine + env *testenv.Env + historian schema.Historian + config *tabletenv.TabletConfig ) func TestMain(m *testing.M) { @@ -51,8 +55,10 @@ func TestMain(m *testing.M) { defer env.Close() // engine cannot be initialized in testenv because it introduces - // circular dependencies. - engine = NewEngine(env.TabletEnv, env.SrvTopo, env.SchemaEngine) + // circular dependencies + historian = schema.NewHistorian(env.SchemaEngine) //newMockHistorian + historian.Init(tabletpb.TabletType_MASTER) + engine = NewEngine(env.TabletEnv, env.SrvTopo, historian) engine.Open(env.KeyspaceName, env.Cells[0]) defer engine.Close() @@ -66,8 +72,12 @@ func customEngine(t *testing.T, modifier func(mysql.ConnParams) mysql.ConnParams require.NoError(t, err) modified := modifier(*original) config := env.TabletEnv.Config().Clone() + config.WatchReplication = true config.DB = dbconfigs.NewTestDBConfigs(modified, modified, modified.DbName) - engine := NewEngine(tabletenv.NewEnv(config, "VStreamerTest"), env.SrvTopo, env.SchemaEngine) + historian = schema.NewHistorian(env.SchemaEngine) //newMockHistorian + historian.Init(tabletpb.TabletType_MASTER) + + engine := NewEngine(tabletenv.NewEnv(config, "VStreamerTest"), env.SrvTopo, historian) engine.Open(env.KeyspaceName, env.Cells[0]) return engine } diff --git a/go/vt/vttablet/tabletserver/vstreamer/resultstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/resultstreamer_test.go index b84915a08f6..92a1a277b34 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/resultstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/resultstreamer_test.go @@ -36,7 +36,7 @@ func TestStreamResults(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) query := "select id, val from t1 order by id" wantStream := []string{ diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go index 35001615384..3b5e6ae3b07 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go @@ -38,8 +38,8 @@ type RowStreamer interface { } // NewRowStreamer returns a RowStreamer -func NewRowStreamer(ctx context.Context, cp dbconfigs.Connector, se *schema.Engine, query string, lastpk []sqltypes.Value, send func(*binlogdatapb.VStreamRowsResponse) error) RowStreamer { - return newRowStreamer(ctx, cp, se, query, lastpk, &localVSchema{vschema: &vindexes.VSchema{}}, send) +func NewRowStreamer(ctx context.Context, cp dbconfigs.Connector, sh *schema.HistorianSvc, query string, lastpk []sqltypes.Value, send func(*binlogdatapb.VStreamRowsResponse) error) RowStreamer { + return newRowStreamer(ctx, cp, sh, query, lastpk, &localVSchema{vschema: &vindexes.VSchema{}}, send) } // rowStreamer is used for copying the existing rows of a table @@ -55,7 +55,7 @@ type rowStreamer struct { cancel func() cp dbconfigs.Connector - se *schema.Engine + sh schema.Historian query string lastpk []sqltypes.Value send func(*binlogdatapb.VStreamRowsResponse) error @@ -66,13 +66,13 @@ type rowStreamer struct { sendQuery string } -func newRowStreamer(ctx context.Context, cp dbconfigs.Connector, se *schema.Engine, query string, lastpk []sqltypes.Value, vschema *localVSchema, send func(*binlogdatapb.VStreamRowsResponse) error) *rowStreamer { +func newRowStreamer(ctx context.Context, cp dbconfigs.Connector, sh schema.Historian, query string, lastpk []sqltypes.Value, vschema *localVSchema, send func(*binlogdatapb.VStreamRowsResponse) error) *rowStreamer { ctx, cancel := context.WithCancel(ctx) return &rowStreamer{ ctx: ctx, cancel: cancel, cp: cp, - se: se, + sh: sh, query: query, lastpk: lastpk, send: send, @@ -85,9 +85,9 @@ func (rs *rowStreamer) Cancel() { } func (rs *rowStreamer) Stream() error { - // Ensure se is Open. If vttablet came up in a non_serving role, + // Ensure sh is Open. If vttablet came up in a non_serving role, // the schema engine may not have been initialized. - if err := rs.se.Open(); err != nil { + if err := rs.sh.Open(); err != nil { return err } @@ -113,12 +113,12 @@ func (rs *rowStreamer) buildPlan() error { if err != nil { return err } - st := rs.se.GetTable(fromTable) + st := rs.sh.GetTableForPos(fromTable, "") //TODO if st == nil { return fmt.Errorf("unknown table %v in schema", fromTable) } ti := &Table{ - Name: st.Name.String(), + Name: st.Name, Fields: st.Fields, } // The plan we build is identical to the one for vstreamer. @@ -140,20 +140,22 @@ func (rs *rowStreamer) buildPlan() error { return err } -func buildPKColumns(st *schema.Table) ([]int, error) { +func buildPKColumns(st *binlogdatapb.TableMetaData) ([]int, error) { + var pkColumns = make([]int, 0) if len(st.PKColumns) == 0 { - pkColumns := make([]int, len(st.Fields)) + pkColumns = make([]int, len(st.Fields)) for i := range st.Fields { pkColumns[i] = i } return pkColumns, nil } for _, pk := range st.PKColumns { - if pk >= len(st.Fields) { + if pk >= int64(len(st.Fields)) { return nil, fmt.Errorf("primary key %d refers to non-existent column", pk) } + pkColumns = append(pkColumns, int(pk)) } - return st.PKColumns, nil + return pkColumns, nil } func (rs *rowStreamer) buildSelect() (string, error) { diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go index 36e7dfcbf11..4ae7c55ac88 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go @@ -54,7 +54,7 @@ func TestStreamRowsScan(t *testing.T) { "drop table t3", "drop table t4", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) // t1: all rows wantStream := []string{ @@ -198,7 +198,7 @@ func TestStreamRowsKeyRange(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) time.Sleep(1 * time.Second) @@ -228,7 +228,7 @@ func TestStreamRowsFilterInt(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) time.Sleep(1 * time.Second) @@ -257,7 +257,7 @@ func TestStreamRowsFilterVarBinary(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) time.Sleep(1 * time.Second) @@ -285,7 +285,7 @@ func TestStreamRowsMultiPacket(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) wantStream := []string{ `fields: fields: pkfields: `, @@ -313,7 +313,7 @@ func TestStreamRowsCancel(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index e8a0c502c15..5c8d41120da 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -21,7 +21,6 @@ import ( "flag" "fmt" "io" - "strconv" "time" "github.com/golang/protobuf/proto" @@ -61,7 +60,7 @@ type vstreamer struct { cancel func() cp dbconfigs.Connector - se *schema.Engine + sh schema.Historian startPos string filter *binlogdatapb.Filter send func([]*binlogdatapb.VEvent) error @@ -87,7 +86,7 @@ type streamerPlan struct { // newVStreamer creates a new vstreamer. // cp: the mysql conn params. -// se: the schema engine. The vstreamer uses it to convert the TableMap into field info. +// sh: the schema engine. The vstreamer uses it to convert the TableMap into field info. // startPos: a flavor compliant position to stream from. This can also contain the special // value "current", which means start from the current position. // filter: the list of filtering rules. If a rule has a select expression for its filter, @@ -103,14 +102,14 @@ type streamerPlan struct { // Other constructs like joins, group by, etc. are not supported. // vschema: the current vschema. This value can later be changed through the SetVSchema method. // send: callback function to send events. -func newVStreamer(ctx context.Context, cp dbconfigs.Connector, se *schema.Engine, startPos string, +func newVStreamer(ctx context.Context, cp dbconfigs.Connector, sh schema.Historian, startPos string, filter *binlogdatapb.Filter, vschema *localVSchema, send func([]*binlogdatapb.VEvent) error) *vstreamer { ctx, cancel := context.WithCancel(ctx) return &vstreamer{ ctx: ctx, cancel: cancel, cp: cp, - se: se, + sh: sh, startPos: startPos, filter: filter, send: send, @@ -166,9 +165,9 @@ func (vs *vstreamer) Stream() error { vs.pos = pos } - // Ensure se is Open. If vttablet came up in a non_serving role, + // Ensure sh is Open. If vttablet came up in a non_serving role, // the schema engine may not have been initialized. - if err := vs.se.Open(); err != nil { + if err := vs.sh.Open(); err != nil { return wrapError(err, vs.pos) } @@ -447,6 +446,7 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e }) } else { // If the DDL need not be sent, send a dummy OTHER event. + log.Infof("Not sending DDL for %s", q.SQL) vevents = append(vevents, &binlogdatapb.VEvent{ Type: binlogdatapb.VEventType_GTID, Gtid: mysql.EncodePosition(vs.pos), @@ -519,7 +519,13 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e if id == vs.journalTableID { vevents, err = vs.processJournalEvent(vevents, plan, rows) } else if id == vs.versionTableID { - vevents, err = vs.processVersionEvent(vevents, plan, rows) + log.Infof("In vstreamer registering version event") + vs.sh.RegisterVersionEvent() + vevent := &binlogdatapb.VEvent{ + Type: binlogdatapb.VEventType_VERSION, + } + vevents = append(vevents, vevent) + } else { vevents, err = vs.processRowEvent(vevents, plan, rows) } @@ -534,29 +540,27 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e return vevents, nil } -//TODO create common buildSpecialPlan for journal/version? -func (vs *vstreamer) buildVersionPlan(id uint64, tm *mysql.TableMap) error { +func (vs *vstreamer) buildJournalPlan(id uint64, tm *mysql.TableMap) error { conn, err := vs.cp.Connect(vs.ctx) if err != nil { return err } defer conn.Close() - qr, err := conn.ExecuteFetch("select * from _vt.schema_version where 1 != 1", 1, true) + qr, err := conn.ExecuteFetch("select * from _vt.resharding_journal where 1 != 1", 1, true) if err != nil { return err } fields := qr.Fields - log.Infof(">>> buildVersionPlan fields are %v", fields) if len(fields) < len(tm.Types) { return fmt.Errorf("cannot determine table columns for %s: event has %v, schema as %v", tm.Name, tm.Types, fields) } table := &Table{ - Name: "_vt.schema_version", + Name: "_vt.resharding_journal", Fields: fields[:len(tm.Types)], } // Build a normal table plan, which means, return all rows // and columns as is. Special handling is done when we actually - // receive the row event. We'll build a VERSION event instead. + // receive the row event. We'll build a JOURNAL event instead. plan, err := buildREPlan(table, nil, "") if err != nil { return err @@ -565,18 +569,17 @@ func (vs *vstreamer) buildVersionPlan(id uint64, tm *mysql.TableMap) error { Plan: plan, TableMap: tm, } - vs.versionTableID = id + vs.journalTableID = id return nil - } -func (vs *vstreamer) buildJournalPlan(id uint64, tm *mysql.TableMap) error { +func (vs *vstreamer) buildVersionPlan(id uint64, tm *mysql.TableMap) error { conn, err := vs.cp.Connect(vs.ctx) if err != nil { return err } defer conn.Close() - qr, err := conn.ExecuteFetch("select * from _vt.resharding_journal where 1 != 1", 1, true) + qr, err := conn.ExecuteFetch("select * from _vt.schema_version where 1 != 1", 1, true) if err != nil { return err } @@ -585,7 +588,7 @@ func (vs *vstreamer) buildJournalPlan(id uint64, tm *mysql.TableMap) error { return fmt.Errorf("cannot determine table columns for %s: event has %v, schema as %v", tm.Name, tm.Types, fields) } table := &Table{ - Name: "_vt.resharding_journal", + Name: "_vt.schema_version", Fields: fields[:len(tm.Types)], } // Build a normal table plan, which means, return all rows @@ -599,7 +602,7 @@ func (vs *vstreamer) buildJournalPlan(id uint64, tm *mysql.TableMap) error { Plan: plan, TableMap: tm, } - vs.journalTableID = id + vs.versionTableID = id return nil } @@ -647,7 +650,7 @@ func (vs *vstreamer) buildTableColumns(tm *mysql.TableMap) ([]*querypb.Field, er }) } - st := vs.se.GetTable(sqlparser.NewTableIdent(tm.Name)) //GetTableForPos(sqlparser.NewTableIdent(tm.Name), vs.pos.String()) + st := vs.sh.GetTableForPos(sqlparser.NewTableIdent(tm.Name), mysql.EncodePosition(vs.pos)) if st == nil { if vs.filter.FieldEventMode == binlogdatapb.Filter_ERR_ON_MISMATCH { return nil, fmt.Errorf("unknown table %v in schema", tm.Name) @@ -713,57 +716,6 @@ nextrow: return vevents, nil } -func (vs *vstreamer) processVersionEvent(vevents []*binlogdatapb.VEvent, plan *streamerPlan, rows mysql.Rows) ([]*binlogdatapb.VEvent, error) { - // Get DbName - params, err := vs.cp.MysqlParams() - if err != nil { - return nil, err - } -nextrow: - for _, row := range rows.Rows { - afterOK, afterValues, err := vs.extractRowAndFilter(plan, row.Data, rows.DataColumns, row.NullColumns) - if err != nil { - return nil, err - } - if !afterOK { - // This can happen if someone manually deleted rows. - continue - } - // Exclude events that don't match the db_name. - for i, fld := range plan.fields() { - if fld.Name == "db_name" && afterValues[i].ToString() != params.DbName { - continue nextrow - } - } - version := &binlogdatapb.Version{} - for i, fld := range plan.fields() { - val := afterValues[i].ToString() - switch fld.Name { - case "pos": - version.Gtid = val - case "ddl": - version.Ddl = val - case "time_updated": - val, _ := strconv.Atoi(val) - version.Timestamp = int64(val) - case "schemax": - tables := &binlogdatapb.TableMetaDataCollection{} - log.Infof("SchemaX tables are %v", len(afterValues[i].ToBytes())) - if err := proto.Unmarshal(afterValues[i].ToBytes(), tables); err != nil { - return nil, err - } - version.Tables = tables - } - } - vs.se.Sh.RegisterVersionEvent(version) - vevents = append(vevents, &binlogdatapb.VEvent{ - Type: binlogdatapb.VEventType_VERSION, - Version: version, - }) - } - return vevents, nil -} - func (vs *vstreamer) processRowEvent(vevents []*binlogdatapb.VEvent, plan *streamerPlan, rows mysql.Rows) ([]*binlogdatapb.VEvent, error) { rowChanges := make([]*binlogdatapb.RowChange, 0, len(rows.Rows)) for _, row := range rows.Rows { diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index 8fe1121aaf7..7d1efd51cf1 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -17,24 +17,21 @@ limitations under the License. package vstreamer import ( - "bytes" "fmt" "strconv" "strings" "testing" "time" - "github.com/gogo/protobuf/proto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "golang.org/x/net/context" - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/proto/query" + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/vt/log" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" - "vitess.io/vitess/go/mysql" - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" ) @@ -43,43 +40,57 @@ type testcase struct { output [][]string } +var numVersionEventsReceived int + type mockHistorian struct { - se schema.HistoryEngine - numVersionEventsReceived int - gtid string - ddl string - time_updated int64 - tables map[string]*binlogdatapb.TableMetaData + he schema.HistoryEngine } -func newMockHistorian(se schema.HistoryEngine) *mockHistorian { - return &mockHistorian{se: se} +func (h *mockHistorian) Init(tabletType topodatapb.TabletType) error { + return nil } -func (h *mockHistorian) GetTableForPos(tableName sqlparser.TableIdent, pos string) *schema.Table { +func (h *mockHistorian) Open() error { return nil } -func (h *mockHistorian) RegisterVersionEvent(version *binlogdatapb.Version) { - h.numVersionEventsReceived++ - h.gtid = version.Gtid - h.ddl = version.Ddl - h.time_updated = version.Timestamp - h.tables = make(map[string]*binlogdatapb.TableMetaData) - for _, table := range version.Tables.Tables { - h.tables[table.Name] = table - } +func (h *mockHistorian) Reload(ctx context.Context) error { + return nil +} +func newMockHistorian(he schema.HistoryEngine) *mockHistorian { + sh := mockHistorian{he: he} + return &sh } -var _ schema.HistorianInterface = (*mockHistorian)(nil) +func (h *mockHistorian) GetTableForPos(tableName sqlparser.TableIdent, pos string) *binlogdatapb.TableMetaData { + return nil +} + +func (h *mockHistorian) RegisterVersionEvent() { + numVersionEventsReceived++ +} + +var _ schema.Historian = (*mockHistorian)(nil) func TestVersion(t *testing.T) { if testing.Short() { t.Skip() } - historian := newMockHistorian(engine.se) - engine.se.Sh = historian + + oldEngine := engine + defer func() { + engine = oldEngine + }() + + log.Infof("Old engine is %v", engine.sh) + mh := newMockHistorian(env.SchemaEngine) + log.Infof("Mock historian is %v", mh) + + engine = NewEngine(engine.env, env.SrvTopo, mh) + engine.Open(env.KeyspaceName, env.Cells[0]) + defer engine.Close() + log.Infof("New engine is %v:%v", engine.sh, mh) 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))", @@ -87,43 +98,223 @@ func TestVersion(t *testing.T) { defer execStatements(t, []string{ "drop table _vt.schema_version", }) - engine.se.Reload(context.Background()) - tbl := &binlogdatapb.TableMetaData{ - Name: "t1", - Fields: []*query.Field{ - {Name: "id", Type: query.Type_INT64, Table: "t1", Database: "test"}, - {Name: "i1", Type: query.Type_INT64, Table: "t1", Database: "test"}, - }, - Pkcolumns: []int64{0}, - } - tbls := &binlogdatapb.TableMetaDataCollection{ - Tables: []*binlogdatapb.TableMetaData{tbl}, - } - blob, err := proto.Marshal(tbls) - if err != nil { - t.Fatalf("Error marshalling tables %v", err) - } + engine.sh.Reload(context.Background()) testcases := []testcase{{ input: []string{ - fmt.Sprintf("insert into _vt.schema_version values(1, 'gtid1', 123, 'create table t1', %s)", encodeString(string(blob))), + fmt.Sprintf("insert into _vt.schema_version values(1, 'MariaDB/0-41983-20', 123, 'create table t1', 'abc')"), + fmt.Sprintf("insert into _vt.schema_version values(2, 'MariaDB/0-41983-22', 125, 'alter table t1', 'abc')"), }, // External table events don't get sent. output: [][]string{{ `begin`, - `type:VERSION version: fields: pkcolumns:0 > > > `, + `type:VERSION `, + `gtid`, + `commit`}, { + `begin`, + `type:VERSION `, `gtid`, `commit`, }}, }} runCases(t, nil, testcases, "") - assert.Equal(t, 1, historian.numVersionEventsReceived) - assert.Equal(t, 1, len(historian.tables)) + assert.Equal(t, 2, numVersionEventsReceived) } -func encodeString(in string) string { - buf := bytes.NewBuffer(nil) - sqltypes.NewVarChar(in).EncodeSQL(buf) - return buf.String() +func execAndExpect(t *testing.T, ctx context.Context, exec []string, expect [][]string) { + filter := &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "/.*/", + }}, + } + // Record position before the next few statements. + pos := masterPosition(t) + execStatements(t, exec) + + engine.sh.Reload(context.Background()) + + ch := make(chan []*binlogdatapb.VEvent) + go func() { + defer close(ch) + if err := vstream(ctx, t, pos, filter, ch); err != nil { + t.Error(err) + } + }() + expectLog(ctx, t, "versions", ch, expect) +} + +func TestVersionEndToEnd(t *testing.T) { + if testing.Short() { + t.Skip() + } + + execStatements(t, []string{ + "create table version_test1(id1 int, id2 int, primary key(id1))", + }) + defer execStatements(t, []string{ + "drop table version_test1", + }) + + // Record position before the next few statements. + pos := masterPosition(t) + execStatements(t, []string{ + "begin", + "insert into version_test1 values(1, 10)", + "commit", + "sleep", + "begin", + "alter table version_test1 add column id3 int", + "commit", + "sleep", + "begin", + "insert into version_test1 values(2, 20, 200)", + "commit", + "sleep", + "begin", + "alter table version_test1 modify column id3 varbinary(16)", + "commit", + "sleep", + "begin", + "insert into version_test1 values(3, 30, 'abc')", + "commit", + }) + engine.sh.Reload(context.Background()) + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + // Test RE as well as select-based filters. + filter := &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "/.*/", + }}, + } + + ch := make(chan []*binlogdatapb.VEvent) + go func() { + defer close(ch) + if err := vstream(ctx, t, pos, filter, ch); err != nil { + t.Error(err) + } + }() + expectLog(ctx, t, "present", ch, [][]string{{ + `begin`, + `type:FIELD field_event: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + `commit`, + }, { + `gtid`, + `type:DDL ddl:"alter table version_test1 add column id3 int" `, + }, { + `begin`, + `type:FIELD field_event: fields: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + `commit`, + }, { + `gtid`, + `type:DDL ddl:"alter table version_test1 modify column id3 varbinary(16)" `, + }, { + `begin`, + `type:FIELD field_event: fields: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + `commit`, + }, + }) + +} + +func XXTestVersionEndToEnd(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + _ = cancel + defer cancel() + if testing.Short() { + t.Skip() + } + + execStatements(t, []string{ + "create table version_test1(id1 int, id2 int, primary key(id1))", + }) + defer execStatements(t, []string{ + "drop table version_test1", + }) + + var exec = []string{ + "begin", + "insert into version_test1 values(1, 10)", + "commit", + "begin", + "alter table version_test1 add column id3 int", + "commit", + } + var expect = [][]string{{ + `begin`, + `type:FIELD field_event: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + `commit`, + }, { + `gtid`, + `type:DDL ddl:"alter table version_test1 add column id3 int" `, + }, { + `gtid`, + `type:OTHER `, + }, { + `begin`, + `type:VERSION `, + `gtid`, + `commit`, + }, + } + + execAndExpect(t, ctx, exec, expect) + + return + exec = []string{ + "begin", + "insert into version_test1 values(2, 20, 200)", + "commit", + } + expect = [][]string{{ + `begin`, + `type:FIELD field_event: fields: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + `commit`, + }, + } + execAndExpect(t, ctx, exec, expect) + time.Sleep(1 * time.Second) + + exec = []string{ + "begin", + "alter table version_test1 modify column id3 varbinary(16)", + "commit", + } + expect = [][]string{{ + `gtid`, + `type:DDL ddl:"alter table version_test1 modify column id3 varbinary(16)" `, + }, + } + execAndExpect(t, ctx, exec, expect) + time.Sleep(1 * time.Second) + + exec = []string{ + "begin", + "insert into version_test1 values(3, 30, 'abc')", + "commit", + } + expect = [][]string{{ + `begin`, + `type:FIELD field_event: fields: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + `commit`, + }, + } + execAndExpect(t, ctx, exec, expect) + time.Sleep(1 * time.Second) } func TestFilteredVarBinary(t *testing.T) { @@ -137,7 +328,7 @@ func TestFilteredVarBinary(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) filter := &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{{ @@ -190,7 +381,7 @@ func TestFilteredInt(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) filter := &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{{ @@ -245,7 +436,7 @@ func TestStatements(t *testing.T) { "drop table stream1", "drop table stream2", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) testcases := []testcase{{ input: []string{ @@ -339,7 +530,7 @@ func TestOther(t *testing.T) { "drop table stream1", "drop table stream2", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) testcases := []string{ "repair table stream2", @@ -406,7 +597,7 @@ func TestRegexp(t *testing.T) { "drop table yes_stream", "drop table no_stream", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) filter := &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{{ @@ -446,7 +637,7 @@ func TestREKeyRange(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) setVSchema(t, shardedVSchema) defer env.SetVSchema("{}") @@ -536,7 +727,7 @@ func TestInKeyRangeMultiColumn(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) setVSchema(t, multicolumnVSchema) defer env.SetVSchema("{}") @@ -591,7 +782,7 @@ func TestREMultiColumnVindex(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) setVSchema(t, multicolumnVSchema) defer env.SetVSchema("{}") @@ -645,7 +836,7 @@ func TestSelectFilter(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) filter := &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{{ @@ -701,7 +892,7 @@ func TestDDLAddColumn(t *testing.T) { "insert into ddl_test2 values(2, 'bbb', 'ccc')", "commit", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -767,7 +958,7 @@ func TestDDLDropColumn(t *testing.T) { "alter table ddl_test2 drop column val2", "insert into ddl_test2 values(2, 'bbb')", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -822,7 +1013,7 @@ func TestBuffering(t *testing.T) { execStatement(t, "create table packet_test(id int, val varbinary(128), primary key(id))") defer execStatement(t, "drop table packet_test") - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) testcases := []testcase{{ // All rows in one packet. @@ -934,7 +1125,7 @@ func TestBestEffortNameInFieldEvent(t *testing.T) { defer execStatements(t, []string{ "drop table vitess_test_new", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) testcases := []testcase{{ input: []string{ "insert into vitess_test_new values(2, 'abc')", @@ -981,7 +1172,7 @@ func TestTypes(t *testing.T) { "drop table vitess_misc", "drop table vitess_null", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) testcases := []testcase{{ input: []string{ @@ -1114,7 +1305,7 @@ func TestJSON(t *testing.T) { t.Fatal(err) } defer execStatement(t, "drop table vitess_json") - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) testcases := []testcase{{ input: []string{ @@ -1143,7 +1334,7 @@ func TestExternalTable(t *testing.T) { defer execStatements(t, []string{ "drop database external", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) testcases := []testcase{{ input: []string{ @@ -1172,7 +1363,7 @@ func TestJournal(t *testing.T) { defer execStatements(t, []string{ "drop table _vt.resharding_journal", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) journal1 := &binlogdatapb.Journal{ Id: 1, @@ -1212,7 +1403,7 @@ func TestMinimalMode(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) // Record position before the next few statements. pos := masterPosition(t) @@ -1248,7 +1439,7 @@ func TestStatementMode(t *testing.T) { "create table stream2(id int, val varbinary(128), primary key(id))", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) defer execStatements(t, []string{ "drop table stream1", @@ -1303,7 +1494,7 @@ func TestNoFutureGTID(t *testing.T) { defer execStatements(t, []string{ "drop table stream1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) pos := masterPosition(t) t.Logf("current position: %v", pos) @@ -1342,7 +1533,7 @@ func TestFilteredMultipleWhere(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.se.Reload(context.Background()) + engine.sh.Reload(context.Background()) setVSchema(t, shardedVSchema) defer env.SetVSchema("{}") @@ -1386,6 +1577,7 @@ func runCases(t *testing.T, filter *binlogdatapb.Filter, testcases []testcase, p // If position is 'current', we wait for a heartbeat to be // sure the vstreamer has started. if position == "current" { + log.Infof("Starting stream with current position") expectLog(ctx, t, "current pos", ch, [][]string{{`gtid`, `type:OTHER `}}) } @@ -1416,7 +1608,7 @@ func expectLog(ctx context.Context, t *testing.T, input interface{}, ch <-chan [ select { case allevs, ok := <-ch: if !ok { - t.Fatal("stream ended early") + t.Fatal("expectLog: not ok, stream ended early") } for _, ev := range allevs { // Ignore spurious heartbeats that can happen on slow machines. @@ -1426,9 +1618,9 @@ func expectLog(ctx context.Context, t *testing.T, input interface{}, ch <-chan [ evs = append(evs, ev) } case <-ctx.Done(): - t.Fatal("stream ended early") + t.Fatalf("expectLog: Done(), stream ended early") case <-timer.C: - t.Fatalf("timed out waiting for events: %v", wantset) + t.Fatalf("expectLog: timed out waiting for events: %v", wantset) } if len(evs) != 0 { break @@ -1476,6 +1668,8 @@ func startStream(ctx context.Context, t *testing.T, filter *binlogdatapb.Filter, return ch } +var lastPos string + func vstream(ctx context.Context, t *testing.T, pos string, filter *binlogdatapb.Filter, ch chan []*binlogdatapb.VEvent) error { if filter == nil { filter = &binlogdatapb.Filter{ @@ -1485,12 +1679,25 @@ func vstream(ctx context.Context, t *testing.T, pos string, filter *binlogdatapb } } return engine.Stream(ctx, pos, filter, func(evs []*binlogdatapb.VEvent) error { - t.Logf("Received events: %v", evs) + log.Infof("Received events: %v", evs) + for _, ev := range evs { + log.Infof("Original stream: %s event found %v", ev.Type, ev) + if ev.Type == binlogdatapb.VEventType_GTID { + lastPos = ev.Gtid + } + if ev.Type == binlogdatapb.VEventType_DDL { + schemaTracker := schema.NewTracker(env.SchemaEngine) + schemaTracker.SchemaUpdated(lastPos, ev.Ddl, ev.Timestamp) + } + } + select { case ch <- evs: case <-ctx.Done(): - return fmt.Errorf("stream ended early") + return fmt.Errorf("engine.Stream Done() stream ended early") } + log.Infof("Waiting for a second after sending events %v", evs) + time.Sleep(1 * time.Second) return nil }) } @@ -1504,6 +1711,14 @@ func execStatement(t *testing.T, query string) { func execStatements(t *testing.T, queries []string) { t.Helper() + for _, query := range queries { + if query == "sleep" { + //time.Sleep(5000 * time.Millisecond) + continue + } + execStatement(t, query) + } + return if err := env.Mysqld.ExecuteSuperQueryList(context.Background(), queries); err != nil { t.Fatal(err) } diff --git a/proto/binlogdata.proto b/proto/binlogdata.proto index 50a3a684f3c..41ba235a019 100644 --- a/proto/binlogdata.proto +++ b/proto/binlogdata.proto @@ -338,8 +338,6 @@ message VEvent { Journal journal = 8; // Dml is set if the event type is INSERT, REPLACE, UPDATE or DELETE. string dml = 9; - // Version is set if the event type is VERSION. - Version version = 10; // CurrentTime specifies the current time when the message was sent. // This can be used to compenssate for clock skew. int64 current_time = 20; @@ -355,18 +353,6 @@ message TableMetaDataCollection { repeated TableMetaData tables = 1; } -message Version { - // Timestamp is the binlog timestamp in seconds. - // The value should be ignored if 0. - int64 timestamp = 1; - // Gtid position where version was set - string gtid = 2; - // Ddl which caused this version event - string ddl = 3; - // - TableMetaDataCollection tables = 4; -} - // VStreamRequest is the payload for VStreamer message VStreamRequest { vtrpc.CallerID effective_caller_id = 1; From 9138edeff4864afc7b85620ca0cf5f4fcf79aa14 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Mon, 4 May 2020 14:45:08 +0200 Subject: [PATCH 05/17] Working version with e2e for 'current' stream Signed-off-by: Rohit Nayak --- go/vt/vttablet/endtoend/framework/server.go | 7 +- go/vt/vttablet/endtoend/main_test.go | 14 + go/vt/vttablet/endtoend/vstreamer_test.go | 317 ++++++++++++++++++ .../tabletserver/replication_watcher.go | 11 +- .../tabletserver/replication_watcher_test.go | 2 +- go/vt/vttablet/tabletserver/schema/engine.go | 3 - .../vttablet/tabletserver/schema/historian.go | 49 +-- go/vt/vttablet/tabletserver/schema/tracker.go | 18 +- .../tabletserver/vstreamer/main_test.go | 5 +- .../tabletserver/vstreamer/vstreamer.go | 4 +- .../tabletserver/vstreamer/vstreamer_test.go | 208 ------------ 11 files changed, 384 insertions(+), 254 deletions(-) create mode 100644 go/vt/vttablet/endtoend/vstreamer_test.go diff --git a/go/vt/vttablet/endtoend/framework/server.go b/go/vt/vttablet/endtoend/framework/server.go index ec2d593318c..1383b0a0d1e 100644 --- a/go/vt/vttablet/endtoend/framework/server.go +++ b/go/vt/vttablet/endtoend/framework/server.go @@ -22,6 +22,8 @@ import ( "net/http" "time" + "vitess.io/vitess/go/vt/topo" + "vitess.io/vitess/go/vt/topo/memorytopo" "vitess.io/vitess/go/vt/vterrors" @@ -47,6 +49,8 @@ var ( ServerAddress string // ResolveChan is the channel that sends dtids that are to be resolved. ResolveChan = make(chan string, 1) + // TopoServer is the topology for the server + TopoServer *topo.Server ) // StartServer starts the server and initializes @@ -76,8 +80,9 @@ func StartServer(connParams, connAppDebugParams mysql.ConnParams, dbName string) Shard: "0", TabletType: topodatapb.TabletType_MASTER, } + TopoServer = memorytopo.NewServer("") - Server = tabletserver.NewTabletServer("", config, memorytopo.NewServer(""), topodatapb.TabletAlias{}) + Server = tabletserver.NewTabletServer("", config, TopoServer, topodatapb.TabletAlias{}) Server.Register() err := Server.StartService(Target, dbcfgs) if err != nil { diff --git a/go/vt/vttablet/endtoend/main_test.go b/go/vt/vttablet/endtoend/main_test.go index 46375ae2549..db494bbfa80 100644 --- a/go/vt/vttablet/endtoend/main_test.go +++ b/go/vt/vttablet/endtoend/main_test.go @@ -268,6 +268,20 @@ var tableACLConfig = `{ "table_names_or_prefixes": ["vitess_test_debuguser"], "readers": ["dev", "vt_appdebug"], "writers": ["dev", "vt_appdebug"] + }, + { + "name": "version", + "table_names_or_prefixes": ["vitess_version"], + "readers": ["dev"], + "writers": ["dev"], + "admins": ["dev"] + }, + { + "name": "schema_version", + "table_names_or_prefixes": ["schema_version"], + "readers": ["dev"], + "writers": ["dev"], + "admins": ["dev"] } ] }` diff --git a/go/vt/vttablet/endtoend/vstreamer_test.go b/go/vt/vttablet/endtoend/vstreamer_test.go new file mode 100644 index 00000000000..24d662719d6 --- /dev/null +++ b/go/vt/vttablet/endtoend/vstreamer_test.go @@ -0,0 +1,317 @@ +/* +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 endtoend + +import ( + "bytes" + "context" + "errors" + "fmt" + "strings" + "testing" + "time" + + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/callerid" + "vitess.io/vitess/go/vt/log" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + querypb "vitess.io/vitess/go/vt/proto/query" + tabletpb "vitess.io/vitess/go/vt/proto/topodata" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/srvtopo" + "vitess.io/vitess/go/vt/vttablet/endtoend/framework" + "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" + "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" + "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer" +) + +type test struct { + query string + output []string +} + +func TestSchemaVersioning(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + tsv := framework.Server + origTrackSchemaVersions := tsv.Config().TrackSchemaVersions //TODO check if needed/works ... + tsv.Config().TrackSchemaVersions = true + defer func() { + tsv.Config().TrackSchemaVersions = origTrackSchemaVersions + }() + + tsv.StartTracker() + srvTopo := srvtopo.NewResilientServer(framework.TopoServer, "SchemaVersionE2ETestTopo") + historian := schema.NewHistorian(tsv.SchemaEngine()) + historian.Init(tabletpb.TabletType_MASTER) + + vstreamer.NewEngine(tabletenv.NewEnv(tsv.Config(), "SchemaVersionE2ETest"), srvTopo, historian) + target := &querypb.Target{ + Keyspace: "vttest", + Shard: "0", + TabletType: tabletpb.TabletType_MASTER, + Cell: "", + } + filter := &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "/.*/", + }}, + } + + var cases = []test{ + { + query: "create table vitess_version (id1 int, id2 int)", + output: []string{ + `gtid`, + `other`, + `gtid`, + `type:DDL ddl:"create table vitess_version (id1 int, id2 int)" `, + `gtid`, + `other`, + `version`, + `gtid`, + }, + }, + { + query: "insert into vitess_version values(1, 10)", + output: []string{ + `type:FIELD field_event: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + }, + }, { + query: "alter table vitess_version add column id3 int", + output: []string{ + `gtid`, + `type:DDL ddl:"alter table vitess_version add column id3 int" `, + `gtid`, + `other`, + `version`, + `gtid`, + }, + }, { + query: "insert into vitess_version values(2, 20, 200)", + output: []string{ + `type:FIELD field_event: fields: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + }, + }, { + query: "alter table vitess_version modify column id3 varbinary(16)", + output: []string{ + `gtid`, + `type:DDL ddl:"alter table vitess_version modify column id3 varbinary(16)" `, + `gtid`, + `other`, + `version`, + `gtid`, + }, + }, { + query: "insert into vitess_version values(3, 30, 'TTT')", + output: []string{ + `type:FIELD field_event: fields: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + }, + }, + } + eventCh := make(chan []*binlogdatapb.VEvent) + send := func(events []*binlogdatapb.VEvent) error { + var evs []*binlogdatapb.VEvent + for _, event := range events { + if event.Type == binlogdatapb.VEventType_HEARTBEAT { + continue + } + log.Infof("Received event %v", event) + evs = append(evs, event) + } + select { + case eventCh <- evs: + case <-ctx.Done(): + return fmt.Errorf("engine.Stream Done() stream ended early") + } + return nil + } + go func() { + defer close(eventCh) + if err := tsv.VStream(ctx, target, "current", filter, send); err != nil { + fmt.Printf("Error in tsv.VStream: %v", err) + t.Error(err) + } + }() + runCases(ctx, t, cases, eventCh) + + tsv.StopTracker() + cases = []test{ + { + //comment prefix required so we don't look for ddl in schema_version + query: "/**/alter table vitess_version add column id4 varbinary(16)", + output: []string{ + `gtid`, + `type:DDL ddl:"alter table vitess_version add column id4 varbinary(16)" `, + }, + }, { + query: "insert into vitess_version values(4, 40, 'FFF', 'GGGG' )", + output: []string{ + `type:FIELD field_event: fields: fields: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + }, + }, + } + runCases(ctx, t, cases, eventCh) + //cancel ctx + //wait for eventCh to close + //new context + + //TEST with past position + + //cancel ctx + //wait for eventCh to close + //new context + //drop table + + log.Info("=== END OF TEST") +} + +func runCases(ctx context.Context, t *testing.T, tests []test, eventCh chan []*binlogdatapb.VEvent) { + client := framework.NewClientWithContext(callerid.NewContext( + context.Background(), + &vtrpcpb.CallerID{}, + &querypb.VTGateCallerID{Username: "dev"}, + )) + + for _, test := range tests { + query := test.query + client.Execute(query, nil) + if len(test.output) > 0 { + expectLogs(ctx, t, query, eventCh, test.output) + } + if strings.HasPrefix(query, "create") || strings.HasPrefix(query, "alter") || strings.HasPrefix(query, "drop") { + ok, err := waitForVersionInsert(client, query) + if err != nil || !ok { + t.Fatalf("Query %s never got inserted into the schema_version table", query) + } + } + } +} + +func expectLogs(ctx context.Context, t *testing.T, query string, eventCh chan []*binlogdatapb.VEvent, output []string) { + t.Helper() + timer := time.NewTimer(5 * time.Second) + defer timer.Stop() + var evs []*binlogdatapb.VEvent + log.Infof("In expectLogs for query %s, output len %s", query, len(output)) + for { + select { + case allevs, ok := <-eventCh: + if !ok { + t.Fatal("expectLogs: not ok, stream ended early") + } + for _, ev := range allevs { + // Ignore spurious heartbeats that can happen on slow machines. + if ev.Type == binlogdatapb.VEventType_HEARTBEAT { + continue + } + if ev.Type == binlogdatapb.VEventType_BEGIN { + continue + } + if ev.Type == binlogdatapb.VEventType_COMMIT { + continue + } + evs = append(evs, ev) + } + log.Infof("In expectLogs, have got %d events, want %d", len(evs), len(output)) + case <-ctx.Done(): + t.Fatalf("expectLog: Done(), stream ended early") + case <-timer.C: + t.Fatalf("expectLog: timed out waiting for events: %v: evs\n%v, want\n%v, >> got length %d, wanted length %d", query, evs, output, len(evs), len(output)) + } + if len(evs) >= len(output) { + break + } + } + if len(evs) > len(output) { + t.Fatalf("expectLog: got too many events: %v: evs\n%v, want\n%v, >> got length %d, wanted length %d", query, evs, output, len(evs), len(output)) + } + for i, want := range output { + // CurrentTime is not testable. + evs[i].CurrentTime = 0 + switch want { + case "begin": + if evs[i].Type != binlogdatapb.VEventType_BEGIN { + t.Fatalf("%v (%d): event: %v, want begin", query, i, evs[i]) + } + case "gtid": + if evs[i].Type != binlogdatapb.VEventType_GTID { + t.Fatalf("%v (%d): event: %v, want gtid", query, i, evs[i]) + } + case "commit": + if evs[i].Type != binlogdatapb.VEventType_COMMIT { + t.Fatalf("%v (%d): event: %v, want commit", query, i, evs[i]) + } + case "other": + if evs[i].Type != binlogdatapb.VEventType_OTHER { + t.Fatalf("%v (%d): event: %v, want other", query, i, evs[i]) + } + case "version": + if evs[i].Type != binlogdatapb.VEventType_VERSION { + t.Fatalf("%v (%d): event: %v, want version", query, i, evs[i]) + } + default: + evs[i].Timestamp = 0 + if got := fmt.Sprintf("%v", evs[i]); got != want { + t.Fatalf("%v (%d): event:\n%q, want\n%q", query, i, got, want) + } + } + } +} + +func encodeString(in string) string { + buf := bytes.NewBuffer(nil) + sqltypes.NewVarChar(in).EncodeSQL(buf) + return buf.String() +} + +func validateSchemaInserted(client *framework.QueryClient, ddl string) (bool, error) { + qr, _ := client.Execute(fmt.Sprintf("select * from _vt.schema_version where ddl = %s", encodeString(ddl)), nil) + if len(qr.Rows) == 1 { + log.Infof("Found ddl in schema_version: %s", ddl) + return true, nil + } + return false, fmt.Errorf("Found %d rows for gtid %s", len(qr.Rows), ddl) +} + +func waitForVersionInsert(client *framework.QueryClient, ddl string) (bool, error) { + timeout := time.After(1000 * time.Millisecond) + tick := time.Tick(100 * time.Millisecond) + for { + select { + case <-timeout: + return false, errors.New("waitForVersionInsert timed out") + case <-tick: + ok, err := validateSchemaInserted(client, ddl) + if err != nil { + return false, err + } else if ok { + log.Infof("Found version insert for %s", ddl) + return true, nil + } + } + } +} diff --git a/go/vt/vttablet/tabletserver/replication_watcher.go b/go/vt/vttablet/tabletserver/replication_watcher.go index 61412243860..485a7dfdfb3 100644 --- a/go/vt/vttablet/tabletserver/replication_watcher.go +++ b/go/vt/vttablet/tabletserver/replication_watcher.go @@ -41,13 +41,13 @@ type ReplicationWatcher struct { env tabletenv.Env watchReplication bool vs VStreamer - subscriber schema.SchemaSubscriber + subscriber schema.Subscriber cancel context.CancelFunc } // NewReplicationWatcher creates a new ReplicationWatcher. -func NewReplicationWatcher(env tabletenv.Env, vs VStreamer, config *tabletenv.TabletConfig, schemaTracker schema.SchemaSubscriber) *ReplicationWatcher { +func NewReplicationWatcher(env tabletenv.Env, vs VStreamer, config *tabletenv.TabletConfig, schemaTracker schema.Subscriber) *ReplicationWatcher { return &ReplicationWatcher{ env: env, vs: vs, @@ -86,12 +86,17 @@ func (rpw *ReplicationWatcher) Process(ctx context.Context) { }}, } + var gtid string for { // The tracker will reload the schema and save it into _vt.schema_tracking when the vstream encounters a DDL. err := rpw.vs.Stream(ctx, "current", filter, func(events []*binlogdatapb.VEvent) error { for _, event := range events { + if event.Type == binlogdatapb.VEventType_GTID { + gtid = event.Gtid + } if event.Type == binlogdatapb.VEventType_DDL { - rpw.subscriber.SchemaUpdated(event.Gtid, event.Ddl, event.Timestamp) + log.Infof("Calling schema updated for %s %s", gtid, event.Ddl) + rpw.subscriber.SchemaUpdated(gtid, event.Ddl, event.Timestamp) } } return nil diff --git a/go/vt/vttablet/tabletserver/replication_watcher_test.go b/go/vt/vttablet/tabletserver/replication_watcher_test.go index 48f14da83fe..37f4f18448b 100644 --- a/go/vt/vttablet/tabletserver/replication_watcher_test.go +++ b/go/vt/vttablet/tabletserver/replication_watcher_test.go @@ -37,7 +37,7 @@ func (t *mockSubscriber) SchemaUpdated(gtid string, ddl string, timestamp int64) t.timestamps = append(t.timestamps, timestamp) } -var _ schema.SchemaSubscriber = (*mockSubscriber)(nil) +var _ schema.Subscriber = (*mockSubscriber)(nil) var _ VStreamer = (*fakeVstreamer)(nil) var _ tabletenv.Env = (*fakeEnv)(nil) diff --git a/go/vt/vttablet/tabletserver/schema/engine.go b/go/vt/vttablet/tabletserver/schema/engine.go index d592ea1a90e..fc089c3b175 100644 --- a/go/vt/vttablet/tabletserver/schema/engine.go +++ b/go/vt/vttablet/tabletserver/schema/engine.go @@ -217,7 +217,6 @@ func (se *Engine) reload(ctx context.Context) error { changedTables := make(map[string]*Table) // created and altered contain the names of created and altered tables for broadcast. var created, altered []string - log.Infof("In se Reload with %d tables found", len(tableData.Rows)) for _, row := range tableData.Rows { tableName := row[0].ToString() curTables[tableName] = true @@ -225,8 +224,6 @@ func (se *Engine) reload(ctx context.Context) error { if _, ok := se.tables[tableName]; ok && createTime < se.lastChange { continue } - log.Infof("Reading schema for table: %s", tableName) - table, err := LoadTable(conn, tableName, row[1].ToString(), row[3].ToString()) if err != nil { rec.RecordError(err) diff --git a/go/vt/vttablet/tabletserver/schema/historian.go b/go/vt/vttablet/tabletserver/schema/historian.go index 9a16d0e86ff..2e31f8ce306 100644 --- a/go/vt/vttablet/tabletserver/schema/historian.go +++ b/go/vt/vttablet/tabletserver/schema/historian.go @@ -18,11 +18,12 @@ package schema import ( "context" - "encoding/json" "fmt" "sort" "sync" + "github.com/gogo/protobuf/proto" + "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/log" @@ -35,7 +36,7 @@ import ( "vitess.io/vitess/go/vt/sqlparser" ) -const GetSchemaVersions = "select id, pos, ddl, time_updated, schemax from _vt.schema_version where id >= %d order by id desc" +const getSchemaVersions = "select id, pos, ddl, time_updated, schemax from _vt.schema_version where id >= %d order by id desc" // Historian defines the interface to reload a db schema or get the schema of a table for a given position type Historian interface { @@ -59,7 +60,7 @@ type HistoryEngine interface { GetSchema() map[string]*Table } -// TODO: rename TableMetaData and TableMetaDataCollection: to TableLite and SchemaLite? +// TODO: rename TableMetaData and TableMetaDataCollection: to MinimalSchema and MinimalTable? // TrackedSchema has the snapshot of the table at a given pos (reached by ddl) type TrackedSchema struct { @@ -75,7 +76,7 @@ var _ Historian = (*HistorianSvc)(nil) // The schema version table is populated by the Tracker type HistorianSvc struct { he HistoryEngine - lastId int + lastID int schemas []*TrackedSchema isMaster bool mu sync.Mutex @@ -106,7 +107,7 @@ func (h *HistorianSvc) readRow(row []sqltypes.Value) (*TrackedSchema, error) { ddl := string(row[2].ToBytes()) timeUpdated, _ := evalengine.ToInt64(row[3]) sch := &binlogdata.TableMetaDataCollection{} - if err := json.Unmarshal(row[4].ToBytes(), sch); err != nil { + if err := proto.Unmarshal(row[4].ToBytes(), sch); err != nil { return nil, err } log.Infof("Read tracked schema from db: id %d, pos %v, ddl %s, schema len %d, time_updated %d \n", id, pos, ddl, len(sch.Tables), timeUpdated) @@ -124,27 +125,29 @@ func (h *HistorianSvc) readRow(row []sqltypes.Value) (*TrackedSchema, error) { } // loadFromDB loads all rows from the schema_version table that the historian does not have as yet -func (h *HistorianSvc) loadFromDB(ctx context.Context, lastId int) error { +func (h *HistorianSvc) loadFromDB(ctx context.Context, lastID int) error { conn, err := h.he.GetConnection(ctx) if err != nil { return err } defer conn.Recycle() - log.Infof("In loadFromDb for lastId %d", lastId) - tableData, err := conn.Exec(ctx, fmt.Sprintf(GetSchemaVersions, lastId), 1, true) + log.Infof("In loadFromDb for lastID %d", lastID) + tableData, err := conn.Exec(ctx, fmt.Sprintf(getSchemaVersions, lastID), 10000, true) if err != nil { log.Errorf("Error reading schema_tracking table %v", err) return err } + log.Infof("Received rows from schema_version: %v", len(tableData.Rows)) if len(tableData.Rows) > 0 { - log.Infof("Received rows from schema_version: %v", tableData.Rows) - trackedSchema, err := h.readRow(tableData.Rows[0]) - if err != nil { - return err - } h.mu.Lock() defer h.mu.Unlock() - h.schemas = append(h.schemas, trackedSchema) + for _, row := range tableData.Rows { + trackedSchema, err := h.readRow(row) + if err != nil { + return err + } + h.schemas = append(h.schemas, trackedSchema) + } h.sortSchemas() } return nil @@ -168,7 +171,7 @@ func (h *HistorianSvc) Init(tabletType topodatapb.TabletType) error { h.isMaster = tabletType == topodatapb.TabletType_MASTER if h.isMaster { h.Reload(ctx) - if err := h.loadFromDB(ctx, h.lastId); err != nil { + if err := h.loadFromDB(ctx, h.lastID); err != nil { return err } } @@ -187,19 +190,19 @@ func (h *HistorianSvc) reload(ctx context.Context) error { return err } tables := map[string]*binlogdata.TableMetaData{} - log.Infof("Schema returned by engine is %v", h.he.GetSchema()) + log.Infof("Schema returned by engine is %d", len(h.he.GetSchema())) for _, t := range h.he.GetSchema() { table := &binlogdata.TableMetaData{ Name: t.Name.String(), Fields: t.Fields, } - pkc := make([]int64, 0) + var pkc []int64 for _, pk := range t.PKColumns { pkc = append(pkc, int64(pk)) } table.PKColumns = pkc tables[table.Name] = table - log.Infof("Historian, found table %s", table.Name) + //log.Infof("Historian, found table %s", table.Name) } h.lastSchema = tables return nil @@ -223,6 +226,7 @@ func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid strin return t } } + h.reload(context.Background()) if h.lastSchema == nil { return nil } @@ -239,10 +243,9 @@ func (h *HistorianSvc) getTableFromHistoryForPos(tableName sqlparser.TableIdent, if idx >= len(h.schemas) || idx == 0 && !h.schemas[idx].pos.Equal(pos) { log.Infof("Not found schema in cache for %s with pos %s", tableName, pos) return nil - } else { - log.Infof("Found tracked schema. Looking for %s, found %s", pos, h.schemas[idx]) - return h.schemas[idx].schema[tableName.String()] } + log.Infof("Found tracked schema. Looking for %s, found %s", pos, h.schemas[idx]) + return h.schemas[idx].schema[tableName.String()] } // RegisterVersionEvent is called by the vstream when it encounters a version event (an insert into _vt.schema_tracking) @@ -252,7 +255,7 @@ func (h *HistorianSvc) RegisterVersionEvent() { return } ctx := tabletenv.LocalContext() - if err := h.loadFromDB(ctx, h.lastId); err != nil { - log.Error("Error loading schema version information from database in RegisterVersionEvent(): %v", err) + if err := h.loadFromDB(ctx, h.lastID); err != nil { + log.Errorf("Error loading schema version information from database in RegisterVersionEvent(): %v", err) } } diff --git a/go/vt/vttablet/tabletserver/schema/tracker.go b/go/vt/vttablet/tabletserver/schema/tracker.go index 314a9ee73df..9fe87fde753 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker.go +++ b/go/vt/vttablet/tabletserver/schema/tracker.go @@ -28,7 +28,6 @@ import ( "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" ) -//todo: pos as primary key, add timestamp and use instead of id and use default AND on update current_timestamp ... const createSchemaTrackingTable = `CREATE TABLE IF NOT EXISTS _vt.schema_version ( id INT AUTO_INCREMENT, pos VARBINARY(10000) NOT NULL, @@ -44,14 +43,14 @@ type trackerEngine interface { GetSchema() map[string]*Table } -//SchemaSubscriber will get notified when the schema has been updated -type SchemaSubscriber interface { +//Subscriber will get notified when the schema has been updated +type Subscriber interface { SchemaUpdated(gtid string, ddl string, timestamp int64) } -var _ SchemaSubscriber = (*Tracker)(nil) +var _ Subscriber = (*Tracker)(nil) -// Tracker implements SchemaSubscriber and persists versions into the ddb +// Tracker implements Subscriber and persists versions into the ddb type Tracker struct { engine trackerEngine } @@ -61,7 +60,7 @@ func NewTracker(engine trackerEngine) *Tracker { return &Tracker{engine: engine} } -// SchemaUpdated is called by a vstream when it encounters a DDL //TODO multiple might come for same pos, ok? +// SchemaUpdated is called by a vstream when it encounters a DDL func (st *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) { if gtid == "" || ddl == "" { @@ -87,22 +86,21 @@ func (st *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) { t.PKColumns = pks dbSchema.Tables = append(dbSchema.Tables, t) } - blob, err := proto.Marshal(dbSchema) + blob, _ := proto.Marshal(dbSchema) conn, err := st.engine.GetConnection(ctx) if err != nil { return } defer conn.Recycle() - log.Infof("Creating schema tracking table") _, err = conn.Exec(ctx, createSchemaTrackingTable, 1, false) if err != nil { log.Errorf("Error creating schema_tracking table %v", err) return } - log.Infof("Inserting version for position %s: %+v", gtid, dbSchema) - query := fmt.Sprintf("insert ignore into _vt.schema_version "+ + log.Infof("Inserting version for position %s: %s : %+v", gtid, ddl, len(dbSchema.Tables)) + 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) diff --git a/go/vt/vttablet/tabletserver/vstreamer/main_test.go b/go/vt/vttablet/tabletserver/vstreamer/main_test.go index 192ac3835a0..4dccf6320bd 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/main_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/main_test.go @@ -35,7 +35,6 @@ var ( engine *Engine env *testenv.Env historian schema.Historian - config *tabletenv.TabletConfig ) func TestMain(m *testing.M) { @@ -56,7 +55,7 @@ func TestMain(m *testing.M) { // engine cannot be initialized in testenv because it introduces // circular dependencies - historian = schema.NewHistorian(env.SchemaEngine) //newMockHistorian + historian = schema.NewHistorian(env.SchemaEngine) historian.Init(tabletpb.TabletType_MASTER) engine = NewEngine(env.TabletEnv, env.SrvTopo, historian) engine.Open(env.KeyspaceName, env.Cells[0]) @@ -74,7 +73,7 @@ func customEngine(t *testing.T, modifier func(mysql.ConnParams) mysql.ConnParams config := env.TabletEnv.Config().Clone() config.WatchReplication = true config.DB = dbconfigs.NewTestDBConfigs(modified, modified, modified.DbName) - historian = schema.NewHistorian(env.SchemaEngine) //newMockHistorian + historian = schema.NewHistorian(env.SchemaEngine) historian.Init(tabletpb.TabletType_MASTER) engine := NewEngine(tabletenv.NewEnv(config, "VStreamerTest"), env.SrvTopo, historian) diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index 5c8d41120da..5ea61ff0431 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -217,13 +217,13 @@ func (vs *vstreamer) parseEvents(ctx context.Context, events <-chan mysql.Binlog bufferAndTransmit := func(vevent *binlogdatapb.VEvent) error { switch vevent.Type { case binlogdatapb.VEventType_GTID, binlogdatapb.VEventType_BEGIN, binlogdatapb.VEventType_FIELD, - binlogdatapb.VEventType_JOURNAL, binlogdatapb.VEventType_VERSION: + binlogdatapb.VEventType_JOURNAL: // We never have to send GTID, BEGIN, FIELD events on their own. // A JOURNAL event is always preceded by a BEGIN and followed by a COMMIT. // So, we don't have to send it right away. bufferedEvents = append(bufferedEvents, vevent) case binlogdatapb.VEventType_COMMIT, binlogdatapb.VEventType_DDL, binlogdatapb.VEventType_OTHER, - binlogdatapb.VEventType_HEARTBEAT: + binlogdatapb.VEventType_HEARTBEAT, binlogdatapb.VEventType_VERSION: // COMMIT, DDL, OTHER and HEARTBEAT must be immediately sent. // Although unlikely, it's possible to get a HEARTBEAT in the middle // of a transaction. If so, we still send the partial transaction along diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index 7d1efd51cf1..c69d894cbd7 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -120,203 +120,6 @@ func TestVersion(t *testing.T) { assert.Equal(t, 2, numVersionEventsReceived) } -func execAndExpect(t *testing.T, ctx context.Context, exec []string, expect [][]string) { - filter := &binlogdatapb.Filter{ - Rules: []*binlogdatapb.Rule{{ - Match: "/.*/", - }}, - } - // Record position before the next few statements. - pos := masterPosition(t) - execStatements(t, exec) - - engine.sh.Reload(context.Background()) - - ch := make(chan []*binlogdatapb.VEvent) - go func() { - defer close(ch) - if err := vstream(ctx, t, pos, filter, ch); err != nil { - t.Error(err) - } - }() - expectLog(ctx, t, "versions", ch, expect) -} - -func TestVersionEndToEnd(t *testing.T) { - if testing.Short() { - t.Skip() - } - - execStatements(t, []string{ - "create table version_test1(id1 int, id2 int, primary key(id1))", - }) - defer execStatements(t, []string{ - "drop table version_test1", - }) - - // Record position before the next few statements. - pos := masterPosition(t) - execStatements(t, []string{ - "begin", - "insert into version_test1 values(1, 10)", - "commit", - "sleep", - "begin", - "alter table version_test1 add column id3 int", - "commit", - "sleep", - "begin", - "insert into version_test1 values(2, 20, 200)", - "commit", - "sleep", - "begin", - "alter table version_test1 modify column id3 varbinary(16)", - "commit", - "sleep", - "begin", - "insert into version_test1 values(3, 30, 'abc')", - "commit", - }) - engine.sh.Reload(context.Background()) - - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - // Test RE as well as select-based filters. - filter := &binlogdatapb.Filter{ - Rules: []*binlogdatapb.Rule{{ - Match: "/.*/", - }}, - } - - ch := make(chan []*binlogdatapb.VEvent) - go func() { - defer close(ch) - if err := vstream(ctx, t, pos, filter, ch); err != nil { - t.Error(err) - } - }() - expectLog(ctx, t, "present", ch, [][]string{{ - `begin`, - `type:FIELD field_event: fields: > `, - `type:ROW row_event: > > `, - `gtid`, - `commit`, - }, { - `gtid`, - `type:DDL ddl:"alter table version_test1 add column id3 int" `, - }, { - `begin`, - `type:FIELD field_event: fields: fields: > `, - `type:ROW row_event: > > `, - `gtid`, - `commit`, - }, { - `gtid`, - `type:DDL ddl:"alter table version_test1 modify column id3 varbinary(16)" `, - }, { - `begin`, - `type:FIELD field_event: fields: fields: > `, - `type:ROW row_event: > > `, - `gtid`, - `commit`, - }, - }) - -} - -func XXTestVersionEndToEnd(t *testing.T) { - ctx, cancel := context.WithCancel(context.Background()) - _ = cancel - defer cancel() - if testing.Short() { - t.Skip() - } - - execStatements(t, []string{ - "create table version_test1(id1 int, id2 int, primary key(id1))", - }) - defer execStatements(t, []string{ - "drop table version_test1", - }) - - var exec = []string{ - "begin", - "insert into version_test1 values(1, 10)", - "commit", - "begin", - "alter table version_test1 add column id3 int", - "commit", - } - var expect = [][]string{{ - `begin`, - `type:FIELD field_event: fields: > `, - `type:ROW row_event: > > `, - `gtid`, - `commit`, - }, { - `gtid`, - `type:DDL ddl:"alter table version_test1 add column id3 int" `, - }, { - `gtid`, - `type:OTHER `, - }, { - `begin`, - `type:VERSION `, - `gtid`, - `commit`, - }, - } - - execAndExpect(t, ctx, exec, expect) - - return - exec = []string{ - "begin", - "insert into version_test1 values(2, 20, 200)", - "commit", - } - expect = [][]string{{ - `begin`, - `type:FIELD field_event: fields: fields: > `, - `type:ROW row_event: > > `, - `gtid`, - `commit`, - }, - } - execAndExpect(t, ctx, exec, expect) - time.Sleep(1 * time.Second) - - exec = []string{ - "begin", - "alter table version_test1 modify column id3 varbinary(16)", - "commit", - } - expect = [][]string{{ - `gtid`, - `type:DDL ddl:"alter table version_test1 modify column id3 varbinary(16)" `, - }, - } - execAndExpect(t, ctx, exec, expect) - time.Sleep(1 * time.Second) - - exec = []string{ - "begin", - "insert into version_test1 values(3, 30, 'abc')", - "commit", - } - expect = [][]string{{ - `begin`, - `type:FIELD field_event: fields: fields: > `, - `type:ROW row_event: > > `, - `gtid`, - `commit`, - }, - } - execAndExpect(t, ctx, exec, expect) - time.Sleep(1 * time.Second) -} - func TestFilteredVarBinary(t *testing.T) { if testing.Short() { t.Skip() @@ -1696,8 +1499,6 @@ func vstream(ctx context.Context, t *testing.T, pos string, filter *binlogdatapb case <-ctx.Done(): return fmt.Errorf("engine.Stream Done() stream ended early") } - log.Infof("Waiting for a second after sending events %v", evs) - time.Sleep(1 * time.Second) return nil }) } @@ -1710,15 +1511,6 @@ func execStatement(t *testing.T, query string) { } func execStatements(t *testing.T, queries []string) { - t.Helper() - for _, query := range queries { - if query == "sleep" { - //time.Sleep(5000 * time.Millisecond) - continue - } - execStatement(t, query) - } - return if err := env.Mysqld.ExecuteSuperQueryList(context.Background(), queries); err != nil { t.Fatal(err) } From f3ac8bd57c0c8bc5da6f9b10672f74f4f1171d10 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Mon, 4 May 2020 14:45:44 +0200 Subject: [PATCH 06/17] Working version with e2e for 'current' stream Signed-off-by: Rohit Nayak --- go/vt/vttablet/tabletserver/tabletserver.go | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 06b64209ab3..412ecff9574 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -238,8 +238,7 @@ func NewTabletServer(name string, config *tabletenv.TabletConfig, topoServer *to tsv.txThrottler = txthrottler.NewTxThrottler(tsv.config, topoServer) tsOnce.Do(func() { srvTopoServer = srvtopo.NewResilientServer(topoServer, "TabletSrvTopo") }) tsv.vstreamer = vstreamer.NewEngine(tsv, srvTopoServer, tsv.sh) - schemaTracker := schema.NewTracker(tsv.se) - tsv.watcher = NewReplicationWatcher(tsv, tsv.vstreamer, tsv.config, schemaTracker) + tsv.StartTracker() tsv.messager = messager.NewEngine(tsv, tsv.se, tsv.vstreamer) tsv.exporter.NewGaugeFunc("TabletState", "Tablet server state", func() int64 { @@ -263,6 +262,18 @@ func NewTabletServer(name string, config *tabletenv.TabletConfig, topoServer *to return tsv } +// StartTracker() starts a new replication watcher +// Exporting it allows it to be called separately in endtoend tests +func (tsv *TabletServer) StartTracker() { + schemaTracker := schema.NewTracker(tsv.se) + tsv.watcher = NewReplicationWatcher(tsv, tsv.vstreamer, tsv.config, schemaTracker) + tsv.watcher.Open() +} + +func (tsv *TabletServer) StopTracker() { + tsv.watcher.Close() +} + // Register prepares TabletServer for serving by calling // all the registrations functions. func (tsv *TabletServer) Register() { @@ -1690,9 +1701,9 @@ func (tsv *TabletServer) BroadcastHealth(terTimestamp int64, stats *querypb.Real target := tsv.target tsv.mu.Unlock() shr := &querypb.StreamHealthResponse{ - Target: &target, - TabletAlias: &tsv.alias, - Serving: tsv.IsServing(), + Target: &target, + TabletAlias: &tsv.alias, + Serving: tsv.IsServing(), TabletExternallyReparentedTimestamp: terTimestamp, RealtimeStats: stats, } From a22b5eacef115f7e872b8e7c9434cabcf0ea5c75 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Tue, 5 May 2020 00:16:00 +0200 Subject: [PATCH 07/17] e2e test for past events with and without tracking Signed-off-by: Rohit Nayak --- go/vt/vttablet/endtoend/vstreamer_test.go | 177 +++++++++++++++--- .../vttablet/tabletserver/schema/historian.go | 69 ++++--- go/vt/vttablet/tabletserver/tabletserver.go | 6 + 3 files changed, 200 insertions(+), 52 deletions(-) diff --git a/go/vt/vttablet/endtoend/vstreamer_test.go b/go/vt/vttablet/endtoend/vstreamer_test.go index 24d662719d6..0131b74b8bb 100644 --- a/go/vt/vttablet/endtoend/vstreamer_test.go +++ b/go/vt/vttablet/endtoend/vstreamer_test.go @@ -26,15 +26,12 @@ import ( "time" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/callerid" "vitess.io/vitess/go/vt/log" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" tabletpb "vitess.io/vitess/go/vt/proto/topodata" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" "vitess.io/vitess/go/vt/srvtopo" "vitess.io/vitess/go/vt/vttablet/endtoend/framework" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer" ) @@ -46,21 +43,19 @@ type test struct { func TestSchemaVersioning(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) - defer cancel() tsv := framework.Server - origTrackSchemaVersions := tsv.Config().TrackSchemaVersions //TODO check if needed/works ... - tsv.Config().TrackSchemaVersions = true + origWatchReplication := tsv.Config().WatchReplication + tsv.Config().WatchReplication = true defer func() { - tsv.Config().TrackSchemaVersions = origTrackSchemaVersions - }() + tsv.Config().WatchReplication = origWatchReplication + }() + tsv.Historian().SetTrackSchemaVersions(true) tsv.StartTracker() srvTopo := srvtopo.NewResilientServer(framework.TopoServer, "SchemaVersionE2ETestTopo") - historian := schema.NewHistorian(tsv.SchemaEngine()) - historian.Init(tabletpb.TabletType_MASTER) - vstreamer.NewEngine(tabletenv.NewEnv(tsv.Config(), "SchemaVersionE2ETest"), srvTopo, historian) + vstreamer.NewEngine(tabletenv.NewEnv(tsv.Config(), "SchemaVersionE2ETest"), srvTopo, tsv.Historian()) target := &querypb.Target{ Keyspace: "vttest", Shard: "0", @@ -131,9 +126,15 @@ func TestSchemaVersioning(t *testing.T) { }, } eventCh := make(chan []*binlogdatapb.VEvent) + var startPos string send := func(events []*binlogdatapb.VEvent) error { var evs []*binlogdatapb.VEvent for _, event := range events { + if event.Type == binlogdatapb.VEventType_GTID { + if startPos == "" { + startPos = event.Gtid + } + } if event.Type == binlogdatapb.VEventType_HEARTBEAT { continue } @@ -143,7 +144,7 @@ func TestSchemaVersioning(t *testing.T) { select { case eventCh <- evs: case <-ctx.Done(): - return fmt.Errorf("engine.Stream Done() stream ended early") + t.Fatal("Context Done() in send") } return nil } @@ -154,6 +155,7 @@ func TestSchemaVersioning(t *testing.T) { t.Error(err) } }() + log.Infof("\n\n\n=============================================== CURRENT EVENTS START HERE ======================\n\n\n") runCases(ctx, t, cases, eventCh) tsv.StopTracker() @@ -175,26 +177,150 @@ func TestSchemaVersioning(t *testing.T) { }, } runCases(ctx, t, cases, eventCh) - //cancel ctx - //wait for eventCh to close - //new context + cancel() + log.Infof("\n\n\n=============================================== PAST EVENTS START HERE ======================\n\n\n") + ctx, cancel = context.WithCancel(context.Background()) + defer cancel() + eventCh = make(chan []*binlogdatapb.VEvent) + send = func(events []*binlogdatapb.VEvent) error { + var evs []*binlogdatapb.VEvent + for _, event := range events { + if event.Type == binlogdatapb.VEventType_HEARTBEAT { + continue + } + log.Infof("Received event %v", event) + evs = append(evs, event) + } + select { + case eventCh <- evs: + case <-ctx.Done(): + t.Fatal("Context Done() in send") + } + return nil + } + go func() { + defer close(eventCh) + if err := tsv.VStream(ctx, target, startPos, filter, send); err != nil { + fmt.Printf("Error in tsv.VStream: %v", err) + t.Error(err) + } + }() + + output := []string{ + `gtid`, + `type:DDL ddl:"create table vitess_version (id1 int, id2 int)" `, + `gtid`, + `other`, + `version`, + `gtid`, + `type:FIELD field_event: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + `gtid`, + `type:DDL ddl:"alter table vitess_version add column id3 int" `, + `gtid`, + `other`, + `version`, + `gtid`, + `type:FIELD field_event: fields: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + `gtid`, + `type:DDL ddl:"alter table vitess_version modify column id3 varbinary(16)" `, + `gtid`, + `other`, + `version`, + `gtid`, + `type:FIELD field_event: fields: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + `gtid`, + `type:DDL ddl:"alter table vitess_version add column id4 varbinary(16)" `, + `type:FIELD field_event: fields: fields: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + } - //TEST with past position + expectLogs(ctx, t, "Past stream", eventCh, output) - //cancel ctx - //wait for eventCh to close - //new context - //drop table + cancel() + + log.Infof("\n\n\n=============================================== PAST EVENTS WITHOUT TRACK VERSIONS START HERE ======================\n\n\n") + tsv.Historian().SetTrackSchemaVersions(false) + ctx, cancel = context.WithCancel(context.Background()) + defer cancel() + eventCh = make(chan []*binlogdatapb.VEvent) + send = func(events []*binlogdatapb.VEvent) error { + var evs []*binlogdatapb.VEvent + for _, event := range events { + if event.Type == binlogdatapb.VEventType_HEARTBEAT { + continue + } + log.Infof("Received event %v", event) + evs = append(evs, event) + } + select { + case eventCh <- evs: + case <-ctx.Done(): + t.Fatal("Context Done() in send") + } + return nil + } + go func() { + defer close(eventCh) + if err := tsv.VStream(ctx, target, startPos, filter, send); err != nil { + fmt.Printf("Error in tsv.VStream: %v", err) + t.Error(err) + } + }() + + output = []string{ + `gtid`, + `type:DDL ddl:"create table vitess_version (id1 int, id2 int)" `, + `gtid`, + `other`, + `version`, + `gtid`, + `type:FIELD field_event: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + `gtid`, + `type:DDL ddl:"alter table vitess_version add column id3 int" `, + `gtid`, + `other`, + `version`, + `gtid`, + `type:FIELD field_event: fields: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + `gtid`, + `type:DDL ddl:"alter table vitess_version modify column id3 varbinary(16)" `, + `gtid`, + `other`, + `version`, + `gtid`, + `type:FIELD field_event: fields: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + `gtid`, + `type:DDL ddl:"alter table vitess_version add column id4 varbinary(16)" `, + `type:FIELD field_event: fields: fields: fields: > `, + `type:ROW row_event: > > `, + `gtid`, + } + + expectLogs(ctx, t, "Past stream", eventCh, output) + cancel() + + client := framework.NewClient() + client.Execute("drop table vitess_version", nil) + client.Execute("drop table _vt.schema_version", nil) log.Info("=== END OF TEST") } func runCases(ctx context.Context, t *testing.T, tests []test, eventCh chan []*binlogdatapb.VEvent) { - client := framework.NewClientWithContext(callerid.NewContext( - context.Background(), - &vtrpcpb.CallerID{}, - &querypb.VTGateCallerID{Username: "dev"}, - )) + client := framework.NewClient() for _, test := range tests { query := test.query @@ -234,6 +360,7 @@ func expectLogs(ctx context.Context, t *testing.T, query string, eventCh chan [] if ev.Type == binlogdatapb.VEventType_COMMIT { continue } + evs = append(evs, ev) } log.Infof("In expectLogs, have got %d events, want %d", len(evs), len(output)) diff --git a/go/vt/vttablet/tabletserver/schema/historian.go b/go/vt/vttablet/tabletserver/schema/historian.go index 2e31f8ce306..7048bee6421 100644 --- a/go/vt/vttablet/tabletserver/schema/historian.go +++ b/go/vt/vttablet/tabletserver/schema/historian.go @@ -36,7 +36,7 @@ import ( "vitess.io/vitess/go/vt/sqlparser" ) -const getSchemaVersions = "select id, pos, ddl, time_updated, schemax from _vt.schema_version where id >= %d order by id desc" +const getSchemaVersions = "select id, pos, ddl, time_updated, schemax from _vt.schema_version where id > %d order by id asc" // Historian defines the interface to reload a db schema or get the schema of a table for a given position type Historian interface { @@ -45,6 +45,7 @@ type Historian interface { GetTableForPos(tableName sqlparser.TableIdent, pos string) *binlogdata.TableMetaData Open() error Init(tabletType topodatapb.TabletType) error + SetTrackSchemaVersions(val bool) } // HistoryEngine exposes only those schema.Engine APIs that are needed by the HistorianSvc @@ -76,7 +77,7 @@ var _ Historian = (*HistorianSvc)(nil) // The schema version table is populated by the Tracker type HistorianSvc struct { he HistoryEngine - lastID int + lastID int64 schemas []*TrackedSchema isMaster bool mu sync.Mutex @@ -91,24 +92,27 @@ func (h *HistorianSvc) Reload(ctx context.Context) error { // NewHistorian creates a new historian. It expects a schema.Engine instance func NewHistorian(he HistoryEngine) *HistorianSvc { - sh := HistorianSvc{he: he} - sh.trackSchemaVersions = true //TODO use flag - + sh := HistorianSvc{he: he, lastID: -1} return &sh } +// SetTrackSchemaVersions can be used to turn on/off the use of the schema_version history in the historian +func (h *HistorianSvc) SetTrackSchemaVersions(val bool) { + h.trackSchemaVersions = val +} + // readRow converts a row from the schema_version table to a TrackedSchema -func (h *HistorianSvc) readRow(row []sqltypes.Value) (*TrackedSchema, error) { +func (h *HistorianSvc) readRow(row []sqltypes.Value) (*TrackedSchema, int64, error) { id, _ := evalengine.ToInt64(row[0]) pos, err := mysql.DecodePosition(string(row[1].ToBytes())) if err != nil { - return nil, err + return nil, 0, err } ddl := string(row[2].ToBytes()) timeUpdated, _ := evalengine.ToInt64(row[3]) sch := &binlogdata.TableMetaDataCollection{} if err := proto.Unmarshal(row[4].ToBytes(), sch); err != nil { - return nil, err + return nil, 0, err } log.Infof("Read tracked schema from db: id %d, pos %v, ddl %s, schema len %d, time_updated %d \n", id, pos, ddl, len(sch.Tables), timeUpdated) @@ -121,44 +125,39 @@ func (h *HistorianSvc) readRow(row []sqltypes.Value) (*TrackedSchema, error) { pos: pos, ddl: ddl, } - return trackedSchema, nil + return trackedSchema, id, nil } // loadFromDB loads all rows from the schema_version table that the historian does not have as yet -func (h *HistorianSvc) loadFromDB(ctx context.Context, lastID int) error { +func (h *HistorianSvc) loadFromDB(ctx context.Context) error { + h.mu.Lock() + defer h.mu.Unlock() conn, err := h.he.GetConnection(ctx) if err != nil { return err } defer conn.Recycle() - log.Infof("In loadFromDb for lastID %d", lastID) - tableData, err := conn.Exec(ctx, fmt.Sprintf(getSchemaVersions, lastID), 10000, true) + log.Infof("In loadFromDb for lastID %d", h.lastID) + tableData, err := conn.Exec(ctx, fmt.Sprintf(getSchemaVersions, h.lastID), 10000, true) if err != nil { log.Errorf("Error reading schema_tracking table %v", err) return err } log.Infof("Received rows from schema_version: %v", len(tableData.Rows)) if len(tableData.Rows) > 0 { - h.mu.Lock() - defer h.mu.Unlock() for _, row := range tableData.Rows { - trackedSchema, err := h.readRow(row) + trackedSchema, id, err := h.readRow(row) if err != nil { return err } h.schemas = append(h.schemas, trackedSchema) + h.lastID = id } h.sortSchemas() } return nil } -func (h *HistorianSvc) sortSchemas() { - sort.Slice(h.schemas, func(i int, j int) bool { - return h.schemas[j].pos.AtLeast(h.schemas[i].pos) - }) -} - // Init needs to be called once the tablet's type is known. It also warms the cache from the db func (h *HistorianSvc) Init(tabletType topodatapb.TabletType) error { h.Open() @@ -171,7 +170,7 @@ func (h *HistorianSvc) Init(tabletType topodatapb.TabletType) error { h.isMaster = tabletType == topodatapb.TabletType_MASTER if h.isMaster { h.Reload(ctx) - if err := h.loadFromDB(ctx, h.lastID); err != nil { + if err := h.loadFromDB(ctx); err != nil { return err } } @@ -204,6 +203,8 @@ func (h *HistorianSvc) reload(ctx context.Context) error { tables[table.Name] = table //log.Infof("Historian, found table %s", table.Name) } + h.mu.Lock() + defer h.mu.Unlock() h.lastSchema = tables return nil } @@ -234,18 +235,32 @@ func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid strin return h.lastSchema[tableName.String()] } +// sortSchemas sorts entries in ascending order of gtid, ex: 40,44,48 +func (h *HistorianSvc) sortSchemas() { + log.Infof("In sortSchemas with len %d", len(h.schemas)) + sort.Slice(h.schemas, func(i int, j int) bool { + return h.schemas[j].pos.AtLeast(h.schemas[i].pos) + }) + log.Infof("Ending sortSchemas with len %d", len(h.schemas)) +} + // getTableFromHistoryForPos looks in the cache for a schema for a specific gtid func (h *HistorianSvc) getTableFromHistoryForPos(tableName sqlparser.TableIdent, pos mysql.Position) *binlogdata.TableMetaData { idx := sort.Search(len(h.schemas), func(i int) bool { return !pos.AtLeast(h.schemas[i].pos) }) - log.Infof("Found index of schema to be %d", idx) - if idx >= len(h.schemas) || idx == 0 && !h.schemas[idx].pos.Equal(pos) { + + if idx >= len(h.schemas) || idx == 0 && !pos.Equal(h.schemas[idx].pos) { // beyond the range of the cache log.Infof("Not found schema in cache for %s with pos %s", tableName, pos) return nil } - log.Infof("Found tracked schema. Looking for %s, found %s", pos, h.schemas[idx]) - return h.schemas[idx].schema[tableName.String()] + if pos.Equal(h.schemas[idx].pos) { //exact match to a cache entry + log.Infof("Found tracked schema for pos %s, ddl %s", pos, h.schemas[idx].ddl) + return h.schemas[idx].schema[tableName.String()] + } + //not an exact match, so based on our sort algo idx is one less than found: from 40,44,48 : 43 < 44 but we want 40 + log.Infof("Found tracked schema for pos %s, ddl %s", pos, h.schemas[idx-1].ddl) + return h.schemas[idx-1].schema[tableName.String()] } // RegisterVersionEvent is called by the vstream when it encounters a version event (an insert into _vt.schema_tracking) @@ -255,7 +270,7 @@ func (h *HistorianSvc) RegisterVersionEvent() { return } ctx := tabletenv.LocalContext() - if err := h.loadFromDB(ctx, h.lastID); err != nil { + if err := h.loadFromDB(ctx); err != nil { log.Errorf("Error loading schema version information from database in RegisterVersionEvent(): %v", err) } } diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 412ecff9574..ad72fccdf0a 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -230,6 +230,7 @@ func NewTabletServer(name string, config *tabletenv.TabletConfig, topoServer *to } tsv.se = schema.NewEngine(tsv) tsv.sh = schema.NewHistorian(tsv.se) + tsv.sh.SetTrackSchemaVersions(config.TrackSchemaVersions) tsv.qe = NewQueryEngine(tsv, tsv.se) tsv.te = NewTxEngine(tsv) tsv.txController = tsv.te @@ -1929,6 +1930,11 @@ func (tsv *TabletServer) SetConsolidatorMode(mode string) { tsv.qe.consolidatorMode = mode } +// Historian returns the associated historian service +func (tsv *TabletServer) Historian() schema.Historian { + return tsv.sh +} + // queryAsString returns a readable version of query+bind variables. func queryAsString(sql string, bindVariables map[string]*querypb.BindVariable) string { buf := &bytes.Buffer{} From 98e80a8002a1f55bcfe18a3e6f891327421c6955 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Tue, 5 May 2020 00:45:48 +0200 Subject: [PATCH 08/17] Add comments to explain generated events Signed-off-by: Rohit Nayak --- go/vt/vttablet/endtoend/vstreamer_test.go | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/go/vt/vttablet/endtoend/vstreamer_test.go b/go/vt/vttablet/endtoend/vstreamer_test.go index 0131b74b8bb..d5e08449620 100644 --- a/go/vt/vttablet/endtoend/vstreamer_test.go +++ b/go/vt/vttablet/endtoend/vstreamer_test.go @@ -72,11 +72,11 @@ func TestSchemaVersioning(t *testing.T) { { query: "create table vitess_version (id1 int, id2 int)", output: []string{ - `gtid`, + `gtid`, //gtid+other => vstream current pos `other`, - `gtid`, + `gtid`, //gtid+ddl => actual query `type:DDL ddl:"create table vitess_version (id1 int, id2 int)" `, - `gtid`, + `gtid`, //gtid+other => insert into schema_version resulting in version+other `other`, `version`, `gtid`, @@ -94,7 +94,7 @@ func TestSchemaVersioning(t *testing.T) { output: []string{ `gtid`, `type:DDL ddl:"alter table vitess_version add column id3 int" `, - `gtid`, + `gtid`, //gtid+other => insert into schema_version resulting in version+other `other`, `version`, `gtid`, @@ -111,7 +111,7 @@ func TestSchemaVersioning(t *testing.T) { output: []string{ `gtid`, `type:DDL ddl:"alter table vitess_version modify column id3 varbinary(16)" `, - `gtid`, + `gtid`, //gtid+other => insert into schema_version resulting in version+other `other`, `version`, `gtid`, @@ -164,7 +164,7 @@ func TestSchemaVersioning(t *testing.T) { //comment prefix required so we don't look for ddl in schema_version query: "/**/alter table vitess_version add column id4 varbinary(16)", output: []string{ - `gtid`, + `gtid`, //no tracker, so no insert into schema_version or version event `type:DDL ddl:"alter table vitess_version add column id4 varbinary(16)" `, }, }, { @@ -206,6 +206,7 @@ func TestSchemaVersioning(t *testing.T) { } }() + // playing events from the past: same events as original since historian is providing the latest schema output := []string{ `gtid`, `type:DDL ddl:"create table vitess_version (id1 int, id2 int)" `, @@ -274,6 +275,7 @@ func TestSchemaVersioning(t *testing.T) { } }() + // playing events from the past: same as earlier except one below, see comments output = []string{ `gtid`, `type:DDL ddl:"create table vitess_version (id1 int, id2 int)" `, @@ -290,6 +292,7 @@ func TestSchemaVersioning(t *testing.T) { `other`, `version`, `gtid`, + /*at this point we only have latest schema so we have types (int32, int32, varbinary, varbinary) so the types don't match. Hence the @ fieldnames*/ `type:FIELD field_event: fields: fields: > `, `type:ROW row_event: > > `, `gtid`, @@ -299,6 +302,8 @@ func TestSchemaVersioning(t *testing.T) { `other`, `version`, `gtid`, + /*at this point we only have latest schema so we have types (int32, int32, varbinary, varbinary), + but the three fields below match the first three types in the latest, so the field names are correct*/ `type:FIELD field_event: fields: fields: > `, `type:ROW row_event: > > `, `gtid`, From 7191d1a401f69d407ca5a7e0f12a5dd074795c6d Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Tue, 5 May 2020 22:08:33 +0200 Subject: [PATCH 09/17] Working unit tests for schema_versioning Signed-off-by: Rohit Nayak --- .../vreplication/framework_test.go | 4 +- .../tabletserver/replication_watcher_test.go | 3 +- .../vttablet/tabletserver/schema/historian.go | 23 +-- .../tabletserver/schema/historian_test.go | 146 +++++++++++++++++- .../vttablet/tabletserver/schema/main_test.go | 46 ++++++ go/vt/vttablet/tabletserver/schema/tracker.go | 26 ++-- .../tabletserver/schema/tracker_test.go | 47 ++++++ .../tabletserver/vstreamer/main_test.go | 1 - .../tabletserver/vstreamer/rowstreamer.go | 2 +- .../tabletserver/vstreamer/vstreamer_test.go | 47 +++--- 10 files changed, 290 insertions(+), 55 deletions(-) create mode 100644 go/vt/vttablet/tabletserver/schema/main_test.go diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index 9ad110f2133..6ec25853ca5 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -27,6 +27,8 @@ import ( "testing" "time" + "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" + "github.com/golang/protobuf/proto" "golang.org/x/net/context" @@ -88,7 +90,7 @@ func TestMain(m *testing.M) { // engines cannot be initialized in testenv because it introduces // circular dependencies. - streamerEngine = vstreamer.NewEngine(env.TabletEnv, env.SrvTopo, env.SchemaEngine) + streamerEngine = vstreamer.NewEngine(env.TabletEnv, env.SrvTopo, schema.NewHistorian(env.SchemaEngine)) streamerEngine.Open(env.KeyspaceName, env.Cells[0]) defer streamerEngine.Close() diff --git a/go/vt/vttablet/tabletserver/replication_watcher_test.go b/go/vt/vttablet/tabletserver/replication_watcher_test.go index 37f4f18448b..eb508c3a81c 100644 --- a/go/vt/vttablet/tabletserver/replication_watcher_test.go +++ b/go/vt/vttablet/tabletserver/replication_watcher_test.go @@ -31,10 +31,11 @@ import ( "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" ) -func (t *mockSubscriber) SchemaUpdated(gtid string, ddl string, timestamp int64) { +func (t *mockSubscriber) SchemaUpdated(gtid string, ddl string, timestamp int64) error { t.gtids = append(t.gtids, gtid) t.ddls = append(t.ddls, ddl) t.timestamps = append(t.timestamps, timestamp) + return nil } var _ schema.Subscriber = (*mockSubscriber)(nil) diff --git a/go/vt/vttablet/tabletserver/schema/historian.go b/go/vt/vttablet/tabletserver/schema/historian.go index 7048bee6421..5de64d140f0 100644 --- a/go/vt/vttablet/tabletserver/schema/historian.go +++ b/go/vt/vttablet/tabletserver/schema/historian.go @@ -40,7 +40,7 @@ const getSchemaVersions = "select id, pos, ddl, time_updated, schemax from _vt.s // Historian defines the interface to reload a db schema or get the schema of a table for a given position type Historian interface { - RegisterVersionEvent() + RegisterVersionEvent() error Reload(ctx context.Context) error GetTableForPos(tableName sqlparser.TableIdent, pos string) *binlogdata.TableMetaData Open() error @@ -92,7 +92,7 @@ func (h *HistorianSvc) Reload(ctx context.Context) error { // NewHistorian creates a new historian. It expects a schema.Engine instance func NewHistorian(he HistoryEngine) *HistorianSvc { - sh := HistorianSvc{he: he, lastID: -1} + sh := HistorianSvc{he: he, lastID: 0} return &sh } @@ -114,7 +114,7 @@ func (h *HistorianSvc) readRow(row []sqltypes.Value) (*TrackedSchema, int64, err if err := proto.Unmarshal(row[4].ToBytes(), sch); err != nil { return nil, 0, err } - log.Infof("Read tracked schema from db: id %d, pos %v, ddl %s, schema len %d, time_updated %d \n", id, pos, ddl, len(sch.Tables), timeUpdated) + log.Infof("Read tracked schema from db: id %d, pos %v, ddl %s, schema len %d, time_updated %d \n", id, mysql.EncodePosition(pos), ddl, len(sch.Tables), timeUpdated) tables := map[string]*binlogdata.TableMetaData{} for _, t := range sch.Tables { @@ -177,7 +177,8 @@ func (h *HistorianSvc) Init(tabletType topodatapb.TabletType) error { return nil } -// Open opens the underlying schema Engine. Called directly by any user of the historian other than tabletserver which calls Init +// Open opens the underlying schema Engine. Called directly by a user purely interested in schema.Engine functionality +// Other users like tabletserver and tests call Init func (h *HistorianSvc) Open() error { return h.he.Open() } @@ -189,7 +190,7 @@ func (h *HistorianSvc) reload(ctx context.Context) error { return err } tables := map[string]*binlogdata.TableMetaData{} - log.Infof("Schema returned by engine is %d", len(h.he.GetSchema())) + log.Infof("Schema returned by engine has %d tables", len(h.he.GetSchema())) for _, t := range h.he.GetSchema() { table := &binlogdata.TableMetaData{ Name: t.Name.String(), @@ -247,9 +248,10 @@ func (h *HistorianSvc) sortSchemas() { // getTableFromHistoryForPos looks in the cache for a schema for a specific gtid func (h *HistorianSvc) getTableFromHistoryForPos(tableName sqlparser.TableIdent, pos mysql.Position) *binlogdata.TableMetaData { idx := sort.Search(len(h.schemas), func(i int) bool { - return !pos.AtLeast(h.schemas[i].pos) + log.Infof("idx %d pos %s checking pos %s equality %t less %t", i, mysql.EncodePosition(pos), mysql.EncodePosition(h.schemas[i].pos), pos.Equal(h.schemas[i].pos), !pos.AtLeast(h.schemas[i].pos)) + return pos.Equal(h.schemas[i].pos) || !pos.AtLeast(h.schemas[i].pos) }) - + log.Infof("Index %d: len schemas %d", idx, len(h.schemas)) if idx >= len(h.schemas) || idx == 0 && !pos.Equal(h.schemas[idx].pos) { // beyond the range of the cache log.Infof("Not found schema in cache for %s with pos %s", tableName, pos) return nil @@ -265,12 +267,13 @@ func (h *HistorianSvc) getTableFromHistoryForPos(tableName sqlparser.TableIdent, // RegisterVersionEvent is called by the vstream when it encounters a version event (an insert into _vt.schema_tracking) // It triggers the historian to load the newer rows from the database to update its cache -func (h *HistorianSvc) RegisterVersionEvent() { +func (h *HistorianSvc) RegisterVersionEvent() error { if !h.trackSchemaVersions { - return + return nil } ctx := tabletenv.LocalContext() if err := h.loadFromDB(ctx); err != nil { - log.Errorf("Error loading schema version information from database in RegisterVersionEvent(): %v", err) + return err } + return nil } diff --git a/go/vt/vttablet/tabletserver/schema/historian_test.go b/go/vt/vttablet/tabletserver/schema/historian_test.go index 43b1e49ec28..4d6e373ee47 100644 --- a/go/vt/vttablet/tabletserver/schema/historian_test.go +++ b/go/vt/vttablet/tabletserver/schema/historian_test.go @@ -1,5 +1,5 @@ /* -Copyright 2019 The Vitess Authors. +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. @@ -15,3 +15,147 @@ limitations under the License. */ package schema + +import ( + "fmt" + "testing" + + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/sqltypes" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + querypb "vitess.io/vitess/go/vt/proto/query" + tabletpb "vitess.io/vitess/go/vt/proto/topodata" + "vitess.io/vitess/go/vt/sqlparser" +) + +func getTable(name string, fieldNames []string, fieldTypes []querypb.Type, pks []int64) *binlogdatapb.TableMetaData { + if name == "" || len(fieldNames) == 0 || len(fieldNames) != len(fieldTypes) || len(pks) == 0 { + return nil + } + fields := []*querypb.Field{} + for i := range fieldNames { + fields = append(fields, &querypb.Field{ + Name: fieldNames[i], + Type: fieldTypes[i], + Table: name, + }) + } + table := &binlogdatapb.TableMetaData{ + Name: name, + Fields: fields, + PKColumns: pks, + } + return table +} + +func getDbSchemaBlob(t *testing.T, tables map[string]*binlogdatapb.TableMetaData) string { + dbSchema := &binlogdatapb.TableMetaDataCollection{ + Tables: []*binlogdatapb.TableMetaData{}, + } + for name, table := range tables { + t := &binlogdatapb.TableMetaData{ + Name: name, + Fields: table.Fields, + } + pks := make([]int64, 0) + for _, pk := range table.PKColumns { + pks = append(pks, int64(pk)) + } + t.PKColumns = pks + dbSchema.Tables = append(dbSchema.Tables, t) + } + blob, err := proto.Marshal(dbSchema) + require.NoError(t, err) + return string(blob) +} + +func TestHistorian(t *testing.T) { + se, db, cancel := getTestSchemaEngine(t) + defer cancel() + h := NewHistorian(se) + h.Init(tabletpb.TabletType_MASTER) + + h.SetTrackSchemaVersions(false) + require.Nil(t, h.RegisterVersionEvent()) + gtidPrefix := "MySQL56/7b04699f-f5e9-11e9-bf88-9cb6d089e1c3:" + gtid1 := gtidPrefix + "1-10" + ddl1 := "create table tracker_test (id int)" + ts1 := int64(1427325876) + _, _, _ = ddl1, ts1, db + require.Nil(t, h.GetTableForPos(sqlparser.NewTableIdent("t1"), gtid1)) + require.Equal(t, fmt.Sprintf("%v", h.GetTableForPos(sqlparser.NewTableIdent("dual"), gtid1)), `name:"dual" `) + h.SetTrackSchemaVersions(true) + require.Regexp(t, ".*not supported.*", h.RegisterVersionEvent()) + require.Nil(t, h.GetTableForPos(sqlparser.NewTableIdent("t1"), gtid1)) + var blob1 string + + fields := []*querypb.Field{{ + Name: "id", + Type: sqltypes.Int32, + }, { + Name: "pos", + Type: sqltypes.VarBinary, + }, { + Name: "ddl", + Type: sqltypes.VarBinary, + }, { + Name: "time_updated", + Type: sqltypes.Int32, + }, { + Name: "schemax", + Type: sqltypes.Blob, + }} + + table := getTable("t1", []string{"id1", "id2"}, []querypb.Type{querypb.Type_INT32, querypb.Type_INT32}, []int64{0}) + tables := make(map[string]*binlogdatapb.TableMetaData) + tables["t1"] = table + blob1 = getDbSchemaBlob(t, tables) + db.AddQuery("select id, pos, ddl, time_updated, schemax from _vt.schema_version where id > 0 order by id asc", &sqltypes.Result{ + Fields: fields, + Rows: [][]sqltypes.Value{ + {sqltypes.NewInt32(1), sqltypes.NewVarBinary(gtid1), sqltypes.NewVarBinary(ddl1), sqltypes.NewInt32(int32(ts1)), sqltypes.NewVarBinary(blob1)}, + }, + }) + require.Nil(t, h.RegisterVersionEvent()) + exp1 := `name:"t1" fields: fields: p_k_columns:0 ` + require.Equal(t, exp1, fmt.Sprintf("%v", h.GetTableForPos(sqlparser.NewTableIdent("t1"), gtid1))) + gtid2 := gtidPrefix + "1-20" + require.Nil(t, h.GetTableForPos(sqlparser.NewTableIdent("t1"), gtid2)) + + table = getTable("t1", []string{"id1", "id2"}, []querypb.Type{querypb.Type_INT32, querypb.Type_VARBINARY}, []int64{0}) + tables["t1"] = table + blob2 := getDbSchemaBlob(t, tables) + ddl2 := "alter table t1 modify column id2 varbinary" + ts2 := ts1 + 100 + db.AddQuery("select id, pos, ddl, time_updated, schemax from _vt.schema_version where id > 1 order by id asc", &sqltypes.Result{ + Fields: fields, + Rows: [][]sqltypes.Value{ + {sqltypes.NewInt32(2), sqltypes.NewVarBinary(gtid2), sqltypes.NewVarBinary(ddl2), sqltypes.NewInt32(int32(ts2)), sqltypes.NewVarBinary(blob2)}, + }, + }) + require.Nil(t, h.RegisterVersionEvent()) + exp2 := `name:"t1" fields: fields: p_k_columns:0 ` + require.Equal(t, exp2, fmt.Sprintf("%v", h.GetTableForPos(sqlparser.NewTableIdent("t1"), gtid2))) + gtid3 := gtidPrefix + "1-30" + require.Nil(t, h.GetTableForPos(sqlparser.NewTableIdent("t1"), gtid3)) + + table = getTable("t1", []string{"id1", "id2", "id3"}, []querypb.Type{querypb.Type_INT32, querypb.Type_VARBINARY, querypb.Type_INT32}, []int64{0}) + tables["t1"] = table + blob3 := getDbSchemaBlob(t, tables) + ddl3 := "alter table t1 add column id3 int" + ts3 := ts2 + 100 + db.AddQuery("select id, pos, ddl, time_updated, schemax from _vt.schema_version where id > 2 order by id asc", &sqltypes.Result{ + Fields: fields, + Rows: [][]sqltypes.Value{ + {sqltypes.NewInt32(3), sqltypes.NewVarBinary(gtid3), sqltypes.NewVarBinary(ddl3), sqltypes.NewInt32(int32(ts3)), sqltypes.NewVarBinary(blob3)}, + }, + }) + require.Nil(t, h.RegisterVersionEvent()) + exp3 := `name:"t1" fields: fields: fields: p_k_columns:0 ` + require.Equal(t, exp3, fmt.Sprintf("%v", h.GetTableForPos(sqlparser.NewTableIdent("t1"), gtid3))) + + require.Equal(t, exp1, fmt.Sprintf("%v", h.GetTableForPos(sqlparser.NewTableIdent("t1"), gtid1))) + require.Equal(t, exp2, fmt.Sprintf("%v", h.GetTableForPos(sqlparser.NewTableIdent("t1"), gtid2))) + require.Equal(t, exp3, fmt.Sprintf("%v", h.GetTableForPos(sqlparser.NewTableIdent("t1"), gtid3))) +} diff --git a/go/vt/vttablet/tabletserver/schema/main_test.go b/go/vt/vttablet/tabletserver/schema/main_test.go new file mode 100644 index 00000000000..b0fd634c3f1 --- /dev/null +++ b/go/vt/vttablet/tabletserver/schema/main_test.go @@ -0,0 +1,46 @@ +/* +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 schema + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/fakesqldb" + "vitess.io/vitess/go/sqltypes" +) + +func getTestSchemaEngine(t *testing.T) (*Engine, *fakesqldb.DB, func()) { + db := fakesqldb.New(t) + db.AddQuery("select unix_timestamp()", sqltypes.MakeTestResult(sqltypes.MakeTestFields( + "t", + "int64"), + "1427325876", + )) + db.AddQuery(mysql.BaseShowTables, &sqltypes.Result{}) + + db.AddQuery(mysql.BaseShowPrimary, &sqltypes.Result{}) + se := newEngine(10, 10*time.Second, 10*time.Second, true, db) + require.NoError(t, se.Open()) + cancel := func() { + defer db.Close() + defer se.Close() + } + return se, db, cancel +} diff --git a/go/vt/vttablet/tabletserver/schema/tracker.go b/go/vt/vttablet/tabletserver/schema/tracker.go index 9fe87fde753..4e53dd8635d 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker.go +++ b/go/vt/vttablet/tabletserver/schema/tracker.go @@ -45,7 +45,7 @@ type trackerEngine interface { //Subscriber will get notified when the schema has been updated type Subscriber interface { - SchemaUpdated(gtid string, ddl string, timestamp int64) + SchemaUpdated(gtid string, ddl string, timestamp int64) error } var _ Subscriber = (*Tracker)(nil) @@ -55,22 +55,20 @@ type Tracker struct { engine trackerEngine } -// NewTracker creates a Tracker, needs a SchemaEngine (which implements the trackerEngine interface) +// NewTracker creates a Tracker, needs an Open SchemaEngine (which implements the trackerEngine interface) func NewTracker(engine trackerEngine) *Tracker { return &Tracker{engine: engine} } // SchemaUpdated is called by a vstream when it encounters a DDL -func (st *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) { +func (t *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) error { if gtid == "" || ddl == "" { - log.Errorf("Got invalid gtid or ddl in SchemaUpdated") - return + return fmt.Errorf("Got invalid gtid or ddl in SchemaUpdated") } ctx := context.Background() - - st.engine.Reload(ctx) - tables := st.engine.GetSchema() + t.engine.Reload(ctx) + tables := t.engine.GetSchema() dbSchema := &binlogdatapb.TableMetaDataCollection{ Tables: []*binlogdatapb.TableMetaData{}, } @@ -88,17 +86,15 @@ func (st *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) { } blob, _ := proto.Marshal(dbSchema) - conn, err := st.engine.GetConnection(ctx) + conn, err := t.engine.GetConnection(ctx) if err != nil { - return + return err } defer conn.Recycle() _, err = conn.Exec(ctx, createSchemaTrackingTable, 1, false) if err != nil { - log.Errorf("Error creating schema_tracking table %v", err) - return + return err } - log.Infof("Inserting version for position %s: %s : %+v", gtid, ddl, len(dbSchema.Tables)) query := fmt.Sprintf("insert into _vt.schema_version "+ "(pos, ddl, schemax, time_updated) "+ @@ -106,9 +102,9 @@ func (st *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) { _, err = conn.Exec(ctx, query, 1, false) if err != nil { - log.Errorf("Error inserting version for position %s, ddl %s, %v", gtid, ddl, err) - return + return err } + return nil } func encodeString(in string) string { diff --git a/go/vt/vttablet/tabletserver/schema/tracker_test.go b/go/vt/vttablet/tabletserver/schema/tracker_test.go index b9e149c53eb..5409371866a 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker_test.go +++ b/go/vt/vttablet/tabletserver/schema/tracker_test.go @@ -1 +1,48 @@ +/* +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 schema + +import ( + "fmt" + "regexp" + "testing" + + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/sqltypes" +) + +func TestTracker(t *testing.T) { + se, db, cancel := getTestSchemaEngine(t) + defer cancel() + + tracker := NewTracker(se) + gtid1 := "MySQL56/7b04699f-f5e9-11e9-bf88-9cb6d089e1c3:1-10" + ddl1 := "create table tracker_test (id int)" + ts1 := int64(1427325876) + var query string + query = "CREATE TABLE IF NOT EXISTS _vt.schema_version.*" + require.Regexp(t, ".*is not supported.*", tracker.SchemaUpdated(gtid1, ddl1, ts1)) + db.AddQueryPattern(query, &sqltypes.Result{}) + + query = fmt.Sprintf("insert into _vt.schema_version.*%s.*%s.*%d.*", gtid1, regexp.QuoteMeta(ddl1), ts1) + require.Regexp(t, ".*is not supported.*", tracker.SchemaUpdated(gtid1, ddl1, ts1)) + db.AddQueryPattern(query, &sqltypes.Result{}) + + require.NoError(t, tracker.SchemaUpdated(gtid1, ddl1, ts1)) + require.Error(t, tracker.SchemaUpdated("", ddl1, ts1)) + require.Error(t, tracker.SchemaUpdated(gtid1, "", ts1)) +} diff --git a/go/vt/vttablet/tabletserver/vstreamer/main_test.go b/go/vt/vttablet/tabletserver/vstreamer/main_test.go index 4dccf6320bd..3142d4c1547 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/main_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/main_test.go @@ -71,7 +71,6 @@ func customEngine(t *testing.T, modifier func(mysql.ConnParams) mysql.ConnParams require.NoError(t, err) modified := modifier(*original) config := env.TabletEnv.Config().Clone() - config.WatchReplication = true config.DB = dbconfigs.NewTestDBConfigs(modified, modified, modified.DbName) historian = schema.NewHistorian(env.SchemaEngine) historian.Init(tabletpb.TabletType_MASTER) diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go index 3b5e6ae3b07..6459ac1a0d0 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go @@ -113,7 +113,7 @@ func (rs *rowStreamer) buildPlan() error { if err != nil { return err } - st := rs.sh.GetTableForPos(fromTable, "") //TODO + st := rs.sh.GetTableForPos(fromTable, "") if st == nil { return fmt.Errorf("unknown table %v in schema", fromTable) } diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index c69d894cbd7..1e93b6c4061 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -46,6 +46,10 @@ type mockHistorian struct { he schema.HistoryEngine } +func (h *mockHistorian) SetTrackSchemaVersions(val bool) { + +} + func (h *mockHistorian) Init(tabletType topodatapb.TabletType) error { return nil } @@ -67,8 +71,9 @@ func (h *mockHistorian) GetTableForPos(tableName sqlparser.TableIdent, pos strin return nil } -func (h *mockHistorian) RegisterVersionEvent() { +func (h *mockHistorian) RegisterVersionEvent() error { numVersionEventsReceived++ + return nil } var _ schema.Historian = (*mockHistorian)(nil) @@ -83,14 +88,12 @@ func TestVersion(t *testing.T) { engine = oldEngine }() - log.Infof("Old engine is %v", engine.sh) mh := newMockHistorian(env.SchemaEngine) - log.Infof("Mock historian is %v", mh) engine = NewEngine(engine.env, env.SrvTopo, mh) + engine.Open(env.KeyspaceName, env.Cells[0]) defer engine.Close() - log.Infof("New engine is %v:%v", engine.sh, mh) 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))", @@ -102,22 +105,16 @@ func TestVersion(t *testing.T) { testcases := []testcase{{ input: []string{ fmt.Sprintf("insert into _vt.schema_version values(1, 'MariaDB/0-41983-20', 123, 'create table t1', 'abc')"), - fmt.Sprintf("insert into _vt.schema_version values(2, 'MariaDB/0-41983-22', 125, 'alter table t1', 'abc')"), }, // External table events don't get sent. output: [][]string{{ `begin`, - `type:VERSION `, + `type:VERSION `}, { `gtid`, - `commit`}, { - `begin`, - `type:VERSION `, - `gtid`, - `commit`, - }}, + `commit`}}, }} runCases(t, nil, testcases, "") - assert.Equal(t, 2, numVersionEventsReceived) + assert.Equal(t, 1, numVersionEventsReceived) } func TestFilteredVarBinary(t *testing.T) { @@ -1458,6 +1455,8 @@ func expectLog(ctx context.Context, t *testing.T, input interface{}, ch <-chan [ } } +var lastPos string + func startStream(ctx context.Context, t *testing.T, filter *binlogdatapb.Filter, position string) <-chan []*binlogdatapb.VEvent { if position == "" { position = masterPosition(t) @@ -1471,8 +1470,6 @@ func startStream(ctx context.Context, t *testing.T, filter *binlogdatapb.Filter, return ch } -var lastPos string - func vstream(ctx context.Context, t *testing.T, pos string, filter *binlogdatapb.Filter, ch chan []*binlogdatapb.VEvent) error { if filter == nil { filter = &binlogdatapb.Filter{ @@ -1482,18 +1479,18 @@ func vstream(ctx context.Context, t *testing.T, pos string, filter *binlogdatapb } } return engine.Stream(ctx, pos, filter, func(evs []*binlogdatapb.VEvent) error { - log.Infof("Received events: %v", evs) - for _, ev := range evs { - log.Infof("Original stream: %s event found %v", ev.Type, ev) - if ev.Type == binlogdatapb.VEventType_GTID { - lastPos = ev.Gtid - } - if ev.Type == binlogdatapb.VEventType_DDL { - schemaTracker := schema.NewTracker(env.SchemaEngine) - schemaTracker.SchemaUpdated(lastPos, ev.Ddl, ev.Timestamp) + if t.Name() == "TestVersion" { // emulate tracker only for the version test + for _, ev := range evs { + log.Infof("Original stream: %s event found %v", ev.Type, ev) + if ev.Type == binlogdatapb.VEventType_GTID { + lastPos = ev.Gtid + } + if ev.Type == binlogdatapb.VEventType_DDL { + schemaTracker := schema.NewTracker(env.SchemaEngine) + schemaTracker.SchemaUpdated(lastPos, ev.Ddl, ev.Timestamp) + } } } - select { case ch <- evs: case <-ctx.Done(): From 8104d77230f258d4c587fdc90707293ae2e84ca8 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Tue, 5 May 2020 22:29:32 +0200 Subject: [PATCH 10/17] Rename protos Signed-off-by: Rohit Nayak --- go/vt/proto/binlogdata/binlogdata.pb.go | 306 +++++++++--------- .../vttablet/tabletserver/schema/historian.go | 22 +- .../tabletserver/schema/historian_test.go | 14 +- go/vt/vttablet/tabletserver/schema/tracker.go | 8 +- .../tabletserver/vstreamer/rowstreamer.go | 2 +- .../tabletserver/vstreamer/vstreamer_test.go | 2 +- proto/binlogdata.proto | 6 +- 7 files changed, 179 insertions(+), 181 deletions(-) diff --git a/go/vt/proto/binlogdata/binlogdata.pb.go b/go/vt/proto/binlogdata/binlogdata.pb.go index ec1a62d3f16..b7fbb6901a7 100644 --- a/go/vt/proto/binlogdata/binlogdata.pb.go +++ b/go/vt/proto/binlogdata/binlogdata.pb.go @@ -1379,7 +1379,7 @@ func (m *VEvent) GetCurrentTime() int64 { return 0 } -type TableMetaData struct { +type MinimalTable struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Fields []*query.Field `protobuf:"bytes,2,rep,name=fields,proto3" json:"fields,omitempty"` PKColumns []int64 `protobuf:"varint,3,rep,packed,name=p_k_columns,json=pKColumns,proto3" json:"p_k_columns,omitempty"` @@ -1388,85 +1388,85 @@ type TableMetaData struct { XXX_sizecache int32 `json:"-"` } -func (m *TableMetaData) Reset() { *m = TableMetaData{} } -func (m *TableMetaData) String() string { return proto.CompactTextString(m) } -func (*TableMetaData) ProtoMessage() {} -func (*TableMetaData) Descriptor() ([]byte, []int) { +func (m *MinimalTable) Reset() { *m = MinimalTable{} } +func (m *MinimalTable) String() string { return proto.CompactTextString(m) } +func (*MinimalTable) ProtoMessage() {} +func (*MinimalTable) Descriptor() ([]byte, []int) { return fileDescriptor_5fd02bcb2e350dad, []int{17} } -func (m *TableMetaData) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TableMetaData.Unmarshal(m, b) +func (m *MinimalTable) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MinimalTable.Unmarshal(m, b) } -func (m *TableMetaData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TableMetaData.Marshal(b, m, deterministic) +func (m *MinimalTable) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MinimalTable.Marshal(b, m, deterministic) } -func (m *TableMetaData) XXX_Merge(src proto.Message) { - xxx_messageInfo_TableMetaData.Merge(m, src) +func (m *MinimalTable) XXX_Merge(src proto.Message) { + xxx_messageInfo_MinimalTable.Merge(m, src) } -func (m *TableMetaData) XXX_Size() int { - return xxx_messageInfo_TableMetaData.Size(m) +func (m *MinimalTable) XXX_Size() int { + return xxx_messageInfo_MinimalTable.Size(m) } -func (m *TableMetaData) XXX_DiscardUnknown() { - xxx_messageInfo_TableMetaData.DiscardUnknown(m) +func (m *MinimalTable) XXX_DiscardUnknown() { + xxx_messageInfo_MinimalTable.DiscardUnknown(m) } -var xxx_messageInfo_TableMetaData proto.InternalMessageInfo +var xxx_messageInfo_MinimalTable proto.InternalMessageInfo -func (m *TableMetaData) GetName() string { +func (m *MinimalTable) GetName() string { if m != nil { return m.Name } return "" } -func (m *TableMetaData) GetFields() []*query.Field { +func (m *MinimalTable) GetFields() []*query.Field { if m != nil { return m.Fields } return nil } -func (m *TableMetaData) GetPKColumns() []int64 { +func (m *MinimalTable) GetPKColumns() []int64 { if m != nil { return m.PKColumns } return nil } -type TableMetaDataCollection struct { - Tables []*TableMetaData `protobuf:"bytes,1,rep,name=tables,proto3" json:"tables,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` +type MinimalSchema struct { + Tables []*MinimalTable `protobuf:"bytes,1,rep,name=tables,proto3" json:"tables,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *TableMetaDataCollection) Reset() { *m = TableMetaDataCollection{} } -func (m *TableMetaDataCollection) String() string { return proto.CompactTextString(m) } -func (*TableMetaDataCollection) ProtoMessage() {} -func (*TableMetaDataCollection) Descriptor() ([]byte, []int) { +func (m *MinimalSchema) Reset() { *m = MinimalSchema{} } +func (m *MinimalSchema) String() string { return proto.CompactTextString(m) } +func (*MinimalSchema) ProtoMessage() {} +func (*MinimalSchema) Descriptor() ([]byte, []int) { return fileDescriptor_5fd02bcb2e350dad, []int{18} } -func (m *TableMetaDataCollection) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TableMetaDataCollection.Unmarshal(m, b) +func (m *MinimalSchema) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_MinimalSchema.Unmarshal(m, b) } -func (m *TableMetaDataCollection) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TableMetaDataCollection.Marshal(b, m, deterministic) +func (m *MinimalSchema) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_MinimalSchema.Marshal(b, m, deterministic) } -func (m *TableMetaDataCollection) XXX_Merge(src proto.Message) { - xxx_messageInfo_TableMetaDataCollection.Merge(m, src) +func (m *MinimalSchema) XXX_Merge(src proto.Message) { + xxx_messageInfo_MinimalSchema.Merge(m, src) } -func (m *TableMetaDataCollection) XXX_Size() int { - return xxx_messageInfo_TableMetaDataCollection.Size(m) +func (m *MinimalSchema) XXX_Size() int { + return xxx_messageInfo_MinimalSchema.Size(m) } -func (m *TableMetaDataCollection) XXX_DiscardUnknown() { - xxx_messageInfo_TableMetaDataCollection.DiscardUnknown(m) +func (m *MinimalSchema) XXX_DiscardUnknown() { + xxx_messageInfo_MinimalSchema.DiscardUnknown(m) } -var xxx_messageInfo_TableMetaDataCollection proto.InternalMessageInfo +var xxx_messageInfo_MinimalSchema proto.InternalMessageInfo -func (m *TableMetaDataCollection) GetTables() []*TableMetaData { +func (m *MinimalSchema) GetTables() []*MinimalTable { if m != nil { return m.Tables } @@ -1876,8 +1876,8 @@ func init() { proto.RegisterType((*KeyspaceShard)(nil), "binlogdata.KeyspaceShard") proto.RegisterType((*Journal)(nil), "binlogdata.Journal") proto.RegisterType((*VEvent)(nil), "binlogdata.VEvent") - proto.RegisterType((*TableMetaData)(nil), "binlogdata.TableMetaData") - proto.RegisterType((*TableMetaDataCollection)(nil), "binlogdata.TableMetaDataCollection") + proto.RegisterType((*MinimalTable)(nil), "binlogdata.MinimalTable") + proto.RegisterType((*MinimalSchema)(nil), "binlogdata.MinimalSchema") proto.RegisterType((*VStreamRequest)(nil), "binlogdata.VStreamRequest") proto.RegisterType((*VStreamResponse)(nil), "binlogdata.VStreamResponse") proto.RegisterType((*VStreamRowsRequest)(nil), "binlogdata.VStreamRowsRequest") @@ -1889,117 +1889,117 @@ func init() { func init() { proto.RegisterFile("binlogdata.proto", fileDescriptor_5fd02bcb2e350dad) } var fileDescriptor_5fd02bcb2e350dad = []byte{ - // 1778 bytes of a gzipped FileDescriptorProto + // 1779 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xdc, 0x58, 0xcd, 0x72, 0xdb, 0xc8, - 0x11, 0x36, 0xf8, 0xcf, 0x86, 0x44, 0x41, 0xa3, 0x9f, 0x65, 0x5c, 0xd9, 0x2d, 0x2d, 0x2a, 0x5e, - 0x6b, 0x55, 0x15, 0x2a, 0xcb, 0x24, 0xce, 0x69, 0xb3, 0xe1, 0x0f, 0x24, 0xd3, 0x02, 0x49, 0x79, - 0x08, 0xcb, 0xa9, 0xbd, 0xa0, 0x20, 0x72, 0x24, 0x23, 0xc2, 0x9f, 0x81, 0xa1, 0xb4, 0x7c, 0x80, + 0x11, 0x36, 0xff, 0xc9, 0x86, 0x44, 0x41, 0x23, 0x59, 0x61, 0x5c, 0xd9, 0x2d, 0x2d, 0x2a, 0x5e, + 0x6b, 0x55, 0x15, 0x6a, 0xc3, 0x24, 0xce, 0x69, 0xb3, 0xe1, 0x0f, 0x24, 0xd3, 0x02, 0x49, 0x79, + 0x08, 0xcb, 0xa9, 0xbd, 0xa0, 0x20, 0x70, 0x24, 0x21, 0xc2, 0x9f, 0x81, 0xa1, 0xb4, 0x7c, 0x80, 0x54, 0x1e, 0x20, 0x4f, 0x91, 0x73, 0xae, 0xc9, 0x35, 0x4f, 0x91, 0xca, 0x2d, 0xa7, 0x9c, 0xf2, - 0x06, 0xa9, 0xf9, 0x01, 0x08, 0xc8, 0x5b, 0xb6, 0xbc, 0x55, 0x39, 0x24, 0x17, 0x56, 0x4f, 0x4f, - 0x77, 0x4f, 0xf7, 0x37, 0xdd, 0x8d, 0x1e, 0x82, 0x76, 0xe9, 0x06, 0x5e, 0x78, 0xbd, 0x70, 0xa8, - 0xd3, 0x89, 0xe2, 0x90, 0x86, 0x08, 0xd6, 0x9c, 0xc7, 0xea, 0x2d, 0x8d, 0xa3, 0xb9, 0xd8, 0x78, - 0xac, 0xbe, 0x5d, 0x92, 0x78, 0x25, 0x17, 0x2d, 0x1a, 0x46, 0xe1, 0x5a, 0x4b, 0x1f, 0x43, 0x7d, - 0xf0, 0xc6, 0x89, 0x13, 0x42, 0xd1, 0x3e, 0xd4, 0xe6, 0x9e, 0x4b, 0x02, 0xda, 0x56, 0x0e, 0x94, - 0xc3, 0x2a, 0x96, 0x2b, 0x84, 0xa0, 0x32, 0x0f, 0x83, 0xa0, 0x5d, 0xe2, 0x5c, 0x4e, 0x33, 0xd9, - 0x84, 0xc4, 0xb7, 0x24, 0x6e, 0x97, 0x85, 0xac, 0x58, 0xe9, 0xff, 0x2c, 0xc3, 0x76, 0x9f, 0xfb, - 0x61, 0xc5, 0x4e, 0x90, 0x38, 0x73, 0xea, 0x86, 0x01, 0x3a, 0x05, 0x48, 0xa8, 0x43, 0x89, 0x4f, - 0x02, 0x9a, 0xb4, 0x95, 0x83, 0xf2, 0xa1, 0xda, 0x7d, 0xda, 0xc9, 0x45, 0xf0, 0x8e, 0x4a, 0x67, - 0x96, 0xca, 0xe3, 0x9c, 0x2a, 0xea, 0x82, 0x4a, 0x6e, 0x49, 0x40, 0x6d, 0x1a, 0xde, 0x90, 0xa0, - 0x5d, 0x39, 0x50, 0x0e, 0xd5, 0xee, 0x76, 0x47, 0x04, 0x68, 0xb0, 0x1d, 0x8b, 0x6d, 0x60, 0x20, - 0x19, 0xfd, 0xf8, 0x6f, 0x25, 0x68, 0x66, 0xd6, 0x90, 0x09, 0x8d, 0xb9, 0x43, 0xc9, 0x75, 0x18, - 0xaf, 0x78, 0x98, 0xad, 0xee, 0xcf, 0x1e, 0xe8, 0x48, 0x67, 0x20, 0xf5, 0x70, 0x66, 0x01, 0xfd, - 0x14, 0xea, 0x73, 0x81, 0x1e, 0x47, 0x47, 0xed, 0xee, 0xe4, 0x8d, 0x49, 0x60, 0x71, 0x2a, 0x83, - 0x34, 0x28, 0x27, 0x6f, 0x3d, 0x0e, 0xd9, 0x06, 0x66, 0xa4, 0xfe, 0x27, 0x05, 0x1a, 0xa9, 0x5d, - 0xb4, 0x03, 0x5b, 0x7d, 0xd3, 0x7e, 0x35, 0xc1, 0xc6, 0x60, 0x7a, 0x3a, 0x19, 0x7d, 0x6b, 0x0c, - 0xb5, 0x47, 0x68, 0x03, 0x1a, 0x7d, 0xd3, 0xee, 0x1b, 0xa7, 0xa3, 0x89, 0xa6, 0xa0, 0x4d, 0x68, - 0xf6, 0x4d, 0x7b, 0x30, 0x1d, 0x8f, 0x47, 0x96, 0x56, 0x42, 0x5b, 0xa0, 0xf6, 0x4d, 0x1b, 0x4f, - 0x4d, 0xb3, 0xdf, 0x1b, 0x9c, 0x69, 0x65, 0xb4, 0x07, 0xdb, 0x7d, 0xd3, 0x1e, 0x8e, 0x4d, 0x7b, - 0x68, 0x9c, 0x63, 0x63, 0xd0, 0xb3, 0x8c, 0xa1, 0x56, 0x41, 0x00, 0x35, 0xc6, 0x1e, 0x9a, 0x5a, - 0x55, 0xd2, 0x33, 0xc3, 0xd2, 0x6a, 0xd2, 0xdc, 0x68, 0x32, 0x33, 0xb0, 0xa5, 0xd5, 0xe5, 0xf2, - 0xd5, 0xf9, 0xb0, 0x67, 0x19, 0x5a, 0x43, 0x2e, 0x87, 0x86, 0x69, 0x58, 0x86, 0xd6, 0x7c, 0x51, - 0x69, 0x94, 0xb4, 0xf2, 0x8b, 0x4a, 0xa3, 0xac, 0x55, 0xf4, 0x3f, 0x2a, 0xb0, 0x37, 0xa3, 0x31, - 0x71, 0xfc, 0x33, 0xb2, 0xc2, 0x4e, 0x70, 0x4d, 0x30, 0x79, 0xbb, 0x24, 0x09, 0x45, 0x8f, 0xa1, - 0x11, 0x85, 0x89, 0xcb, 0xb0, 0xe3, 0x00, 0x37, 0x71, 0xb6, 0x46, 0xc7, 0xd0, 0xbc, 0x21, 0x2b, - 0x3b, 0x66, 0xf2, 0x12, 0x30, 0xd4, 0xc9, 0x12, 0x32, 0xb3, 0xd4, 0xb8, 0x91, 0x54, 0x1e, 0xdf, - 0xf2, 0x87, 0xf1, 0xd5, 0xaf, 0x60, 0xff, 0xbe, 0x53, 0x49, 0x14, 0x06, 0x09, 0x41, 0x26, 0x20, - 0xa1, 0x68, 0xd3, 0xf5, 0xdd, 0x72, 0xff, 0xd4, 0xee, 0xa7, 0xef, 0x4d, 0x00, 0xbc, 0x7d, 0x79, - 0x9f, 0xa5, 0x7f, 0x07, 0x3b, 0xe2, 0x1c, 0xcb, 0xb9, 0xf4, 0x48, 0xf2, 0x90, 0xd0, 0xf7, 0xa1, - 0x46, 0xb9, 0x70, 0xbb, 0x74, 0x50, 0x3e, 0x6c, 0x62, 0xb9, 0xfa, 0xd8, 0x08, 0x17, 0xb0, 0x5b, - 0x3c, 0xf9, 0xbf, 0x12, 0xdf, 0x2f, 0xa0, 0x82, 0x97, 0x1e, 0x41, 0xbb, 0x50, 0xf5, 0x1d, 0x3a, - 0x7f, 0x23, 0xa3, 0x11, 0x0b, 0x16, 0xca, 0x95, 0xeb, 0x51, 0x12, 0xf3, 0x2b, 0x6c, 0x62, 0xb9, - 0xd2, 0xff, 0xac, 0x40, 0xed, 0x84, 0x93, 0xe8, 0x0b, 0xa8, 0xc6, 0x4b, 0x16, 0xac, 0xa8, 0x75, - 0x2d, 0xef, 0x01, 0xb3, 0x8c, 0xc5, 0x36, 0x1a, 0x41, 0xeb, 0xca, 0x25, 0xde, 0x82, 0x97, 0xee, - 0x38, 0x5c, 0x88, 0xac, 0x68, 0x75, 0x3f, 0xcf, 0x2b, 0x08, 0x9b, 0x9d, 0x93, 0x82, 0x20, 0xbe, - 0xa7, 0xa8, 0x3f, 0x83, 0x56, 0x51, 0x82, 0x95, 0x93, 0x81, 0xb1, 0x3d, 0x9d, 0xd8, 0xe3, 0xd1, - 0x6c, 0xdc, 0xb3, 0x06, 0xcf, 0xb5, 0x47, 0xbc, 0x62, 0x8c, 0x99, 0x65, 0x1b, 0x27, 0x27, 0x53, - 0x6c, 0x69, 0x8a, 0xfe, 0xaf, 0x12, 0x6c, 0x08, 0x50, 0x66, 0xe1, 0x32, 0x9e, 0x13, 0x76, 0x8b, - 0x37, 0x64, 0x95, 0x44, 0xce, 0x9c, 0xa4, 0xb7, 0x98, 0xae, 0x19, 0x20, 0xc9, 0x1b, 0x27, 0x5e, - 0xc8, 0xc8, 0xc5, 0x02, 0xfd, 0x12, 0x54, 0x7e, 0x9b, 0xd4, 0xa6, 0xab, 0x88, 0xf0, 0x7b, 0x6c, - 0x75, 0x77, 0xd7, 0x89, 0xcd, 0xef, 0x8a, 0x5a, 0xab, 0x88, 0x60, 0xa0, 0x19, 0x5d, 0xac, 0x86, - 0xca, 0x03, 0xaa, 0x61, 0x9d, 0x43, 0xd5, 0x42, 0x0e, 0x1d, 0x65, 0x17, 0x52, 0x93, 0x56, 0xde, - 0x41, 0x2f, 0xbd, 0x24, 0xd4, 0x81, 0x5a, 0x18, 0xd8, 0x8b, 0x85, 0xd7, 0xae, 0x73, 0x37, 0x3f, - 0xc9, 0xcb, 0x4e, 0x83, 0xe1, 0xd0, 0xec, 0x89, 0xb4, 0xa8, 0x86, 0xc1, 0x70, 0xe1, 0xa1, 0x27, - 0xd0, 0x22, 0xdf, 0x51, 0x12, 0x07, 0x8e, 0x67, 0xfb, 0x2b, 0xd6, 0xbd, 0x1a, 0x3c, 0xf4, 0xcd, - 0x94, 0x3b, 0x66, 0x4c, 0xf4, 0x05, 0x6c, 0x25, 0x34, 0x8c, 0x6c, 0xe7, 0x8a, 0x92, 0xd8, 0x9e, - 0x87, 0xd1, 0xaa, 0xdd, 0x3c, 0x50, 0x0e, 0x1b, 0x78, 0x93, 0xb1, 0x7b, 0x8c, 0x3b, 0x08, 0xa3, - 0x95, 0xfe, 0x12, 0x9a, 0x38, 0xbc, 0x1b, 0xbc, 0xe1, 0xf1, 0xe8, 0x50, 0xbb, 0x24, 0x57, 0x61, - 0x4c, 0x64, 0xa2, 0x82, 0x6c, 0xe4, 0x38, 0xbc, 0xc3, 0x72, 0x07, 0x1d, 0x40, 0x95, 0xdb, 0x94, - 0xed, 0x22, 0x2f, 0x22, 0x36, 0x74, 0x07, 0x1a, 0x38, 0xbc, 0xe3, 0xd7, 0x8e, 0x3e, 0x05, 0x01, - 0xb0, 0x1d, 0x38, 0x7e, 0x7a, 0x7b, 0x4d, 0xce, 0x99, 0x38, 0x3e, 0x41, 0xcf, 0x40, 0x8d, 0xc3, - 0x3b, 0x7b, 0xce, 0x8f, 0x17, 0x95, 0xa8, 0x76, 0xf7, 0x0a, 0xc9, 0x99, 0x3a, 0x87, 0x21, 0x4e, - 0xc9, 0x44, 0x7f, 0x09, 0xb0, 0xce, 0xad, 0x0f, 0x1d, 0xf2, 0x13, 0x76, 0x1b, 0xc4, 0x5b, 0xa4, - 0xf6, 0x37, 0xa4, 0xcb, 0xdc, 0x02, 0x96, 0x7b, 0x0c, 0x88, 0x19, 0x4b, 0x9e, 0x53, 0xea, 0x2e, - 0x7e, 0x40, 0xca, 0x21, 0xa8, 0x5c, 0x53, 0x77, 0xc1, 0x73, 0xad, 0x89, 0x39, 0xad, 0x7f, 0x03, - 0xd5, 0x0b, 0x6e, 0xee, 0x19, 0xa8, 0x5c, 0xca, 0x66, 0xec, 0xb4, 0x06, 0x0b, 0x61, 0x66, 0x47, - 0x63, 0x48, 0x52, 0x32, 0xd1, 0x7b, 0xb0, 0x79, 0x26, 0x8f, 0xe5, 0x02, 0x1f, 0xef, 0x97, 0xfe, - 0x97, 0x12, 0xd4, 0x5f, 0x84, 0x4b, 0x96, 0x18, 0xa8, 0x05, 0x25, 0x77, 0xc1, 0xf5, 0xca, 0xb8, - 0xe4, 0x2e, 0xd0, 0x6f, 0xa0, 0xe5, 0xbb, 0xd7, 0xb1, 0xc3, 0xd2, 0x4b, 0x54, 0x8a, 0x28, 0xf6, - 0x1f, 0xe5, 0x3d, 0x1b, 0xa7, 0x12, 0xbc, 0x5c, 0x36, 0xfd, 0xfc, 0x32, 0x57, 0x00, 0xe5, 0x42, - 0x01, 0x3c, 0x81, 0x96, 0x17, 0xce, 0x1d, 0xcf, 0xce, 0xda, 0x6f, 0x45, 0x24, 0x29, 0xe7, 0x9e, - 0xa7, 0x3d, 0xf8, 0x1e, 0x2e, 0xd5, 0x07, 0xe2, 0x82, 0xbe, 0x86, 0x8d, 0xc8, 0x89, 0xa9, 0x3b, - 0x77, 0x23, 0x87, 0x0d, 0x30, 0x35, 0xae, 0x58, 0x70, 0xbb, 0x80, 0x1b, 0x2e, 0x88, 0xa3, 0x2f, - 0x41, 0x4b, 0x78, 0x6b, 0xb1, 0xef, 0xc2, 0xf8, 0xe6, 0xca, 0x0b, 0xef, 0x92, 0x76, 0x9d, 0xfb, - 0xbf, 0x25, 0xf8, 0xaf, 0x53, 0xb6, 0xfe, 0xef, 0x12, 0xd4, 0x2e, 0x44, 0x96, 0x1d, 0x41, 0x85, - 0x63, 0x24, 0x86, 0x94, 0xfd, 0xfc, 0x61, 0x42, 0x82, 0x03, 0xc4, 0x65, 0xd0, 0x8f, 0xa1, 0x49, - 0x5d, 0x9f, 0x24, 0xd4, 0xf1, 0x23, 0x0e, 0x6a, 0x19, 0xaf, 0x19, 0xdf, 0x97, 0x2b, 0x6c, 0x12, - 0x61, 0x3d, 0x40, 0xc0, 0xc4, 0x48, 0xf4, 0x15, 0x34, 0x59, 0x6d, 0xf0, 0xc1, 0xa9, 0x5d, 0xe5, - 0xc5, 0xb6, 0x7b, 0xaf, 0x32, 0xf8, 0xb1, 0xb8, 0x11, 0xa7, 0xd5, 0xf6, 0x2b, 0x50, 0x79, 0x36, - 0x4b, 0x25, 0xd1, 0x7c, 0xf6, 0x8b, 0xcd, 0x27, 0xad, 0x1a, 0x0c, 0xeb, 0x7e, 0x8d, 0x9e, 0x42, - 0xf5, 0x96, 0xbb, 0x54, 0x97, 0x03, 0x5c, 0x3e, 0x38, 0x0e, 0xbf, 0xd8, 0x67, 0x5f, 0xc7, 0xdf, - 0x89, 0x6c, 0xe2, 0x6d, 0xe7, 0xde, 0xd7, 0x51, 0x26, 0x1a, 0x4e, 0x65, 0x78, 0x54, 0xbe, 0xc7, - 0x3b, 0x0f, 0x8b, 0xca, 0xf7, 0xd0, 0xe7, 0xb0, 0x31, 0x5f, 0xc6, 0x31, 0x1f, 0x19, 0x5d, 0x9f, - 0xb4, 0x77, 0x39, 0x38, 0xaa, 0xe4, 0x59, 0xae, 0x4f, 0x74, 0x17, 0x36, 0x79, 0x83, 0x1e, 0x13, - 0xea, 0x0c, 0x1d, 0xea, 0x30, 0xbc, 0x72, 0x95, 0xcd, 0xe9, 0x87, 0x15, 0x35, 0xfa, 0x0c, 0xd4, - 0xc8, 0xbe, 0xb1, 0xe7, 0xa1, 0xb7, 0xf4, 0x03, 0x91, 0xa4, 0x65, 0xdc, 0x8c, 0xce, 0x06, 0x82, - 0xa1, 0x9b, 0xf0, 0x49, 0xe1, 0xa8, 0x41, 0xe8, 0x79, 0x44, 0x8c, 0xc8, 0x5f, 0x65, 0xa9, 0xad, - 0xbc, 0x9b, 0x5d, 0x05, 0xa5, 0x34, 0xeb, 0xf5, 0x3f, 0x94, 0xa0, 0x75, 0x21, 0xa6, 0x81, 0x74, - 0x02, 0xf9, 0x06, 0x76, 0xc8, 0xd5, 0x15, 0x33, 0x79, 0x4b, 0xec, 0xb9, 0xe3, 0x79, 0x24, 0xb6, - 0x65, 0x0d, 0xaa, 0xdd, 0xad, 0x8e, 0x78, 0x15, 0x0c, 0x38, 0x7f, 0x34, 0xc4, 0xdb, 0x99, 0xac, - 0x64, 0x2d, 0x90, 0x01, 0x3b, 0xae, 0xef, 0x93, 0x85, 0xeb, 0xd0, 0xbc, 0x01, 0xd1, 0x7c, 0xf7, - 0x64, 0xd0, 0x17, 0xd6, 0xa9, 0x43, 0xc9, 0xda, 0x4c, 0xa6, 0x91, 0x99, 0x79, 0xc2, 0xa2, 0x89, - 0xaf, 0xb3, 0xa1, 0x66, 0x53, 0x6a, 0x5a, 0x9c, 0x89, 0xe5, 0x66, 0x61, 0x60, 0xaa, 0xdc, 0x1b, - 0x98, 0xd6, 0x1f, 0xb5, 0xea, 0x87, 0x3e, 0x6a, 0xfa, 0xd7, 0xb0, 0x95, 0x01, 0x21, 0x07, 0xa2, - 0x23, 0xa8, 0xf1, 0xac, 0x4c, 0xf1, 0x44, 0xef, 0x16, 0x10, 0x96, 0x12, 0xfa, 0xef, 0x4b, 0x80, - 0x52, 0xfd, 0xf0, 0x2e, 0xf9, 0x1f, 0x05, 0x73, 0x17, 0xaa, 0x9c, 0x2f, 0x91, 0x14, 0x0b, 0x86, - 0x83, 0xe7, 0x24, 0x34, 0xba, 0xc9, 0x60, 0x14, 0xca, 0x2f, 0xd9, 0x2f, 0x26, 0xc9, 0xd2, 0xa3, - 0x58, 0x4a, 0xe8, 0x7f, 0x55, 0x60, 0xa7, 0x80, 0x83, 0xc4, 0x72, 0x9d, 0xfc, 0xca, 0x7b, 0x92, - 0xff, 0x10, 0x1a, 0xd1, 0xcd, 0x7b, 0x8a, 0x24, 0xdb, 0xfd, 0xde, 0x86, 0xf4, 0x19, 0x54, 0x62, - 0xd6, 0x18, 0x2b, 0x5c, 0x33, 0xff, 0x99, 0xe7, 0x7c, 0x36, 0x2b, 0x14, 0xe2, 0x28, 0xcc, 0x0a, - 0xd2, 0xff, 0xbf, 0x2b, 0xb0, 0xb7, 0xce, 0x83, 0xa5, 0x47, 0xff, 0xaf, 0xae, 0x52, 0x8f, 0x61, - 0xff, 0x7e, 0x74, 0x1f, 0x75, 0x41, 0x3f, 0x00, 0xf6, 0xa3, 0x5f, 0x83, 0x9a, 0x1b, 0x0a, 0xd9, - 0xdb, 0x71, 0x74, 0x3a, 0x99, 0x62, 0x43, 0x7b, 0x84, 0x1a, 0x50, 0x99, 0x59, 0xd3, 0x73, 0x4d, - 0x61, 0x94, 0xf1, 0x5b, 0x63, 0x20, 0xde, 0xa3, 0x8c, 0xb2, 0xa5, 0x50, 0xf9, 0xe8, 0x1f, 0x0a, - 0xc0, 0xfa, 0x73, 0x85, 0x54, 0xa8, 0xbf, 0x9a, 0x9c, 0x4d, 0xa6, 0xaf, 0x27, 0xc2, 0xc0, 0xa9, - 0x35, 0x1a, 0x6a, 0x0a, 0x6a, 0x42, 0x55, 0x3c, 0x70, 0x4b, 0xec, 0x04, 0xf9, 0xba, 0x2d, 0xb3, - 0xa7, 0x6f, 0xf6, 0xb4, 0xad, 0xa0, 0x3a, 0x94, 0xb3, 0x07, 0xac, 0x7c, 0xb1, 0xd6, 0x98, 0x41, - 0x6c, 0x9c, 0x9b, 0xbd, 0x81, 0xa1, 0xd5, 0xd9, 0x46, 0xf6, 0x76, 0x05, 0xa8, 0xa5, 0x0f, 0x57, - 0xa6, 0xc9, 0x9e, 0xbb, 0xc0, 0xce, 0x99, 0x5a, 0xcf, 0x0d, 0xac, 0xa9, 0x8c, 0x87, 0xa7, 0xaf, - 0xb5, 0x0d, 0xc6, 0x3b, 0x19, 0x19, 0xe6, 0x50, 0xdb, 0x64, 0xef, 0xdd, 0xe7, 0x46, 0x0f, 0x5b, - 0x7d, 0xa3, 0x67, 0x69, 0x2d, 0xb6, 0x73, 0xc1, 0x1d, 0xdc, 0x62, 0xc7, 0xbc, 0x98, 0xbe, 0xc2, - 0x93, 0x9e, 0xa9, 0x69, 0x6c, 0x71, 0x61, 0xe0, 0xd9, 0x68, 0x3a, 0xd1, 0xb6, 0x8f, 0x9e, 0xc2, - 0x66, 0x61, 0x64, 0x61, 0x07, 0x5b, 0xbd, 0xbe, 0x69, 0xcc, 0xb4, 0x47, 0x8c, 0x9e, 0x3d, 0xef, - 0xe1, 0xe1, 0x4c, 0x53, 0xfa, 0x5f, 0x7e, 0xfb, 0xf4, 0xd6, 0xa5, 0x24, 0x49, 0x3a, 0x6e, 0x78, - 0x2c, 0xa8, 0xe3, 0xeb, 0xf0, 0xf8, 0x96, 0x1e, 0xf3, 0x3f, 0x62, 0x8e, 0xd7, 0xed, 0xe9, 0xb2, - 0xc6, 0x39, 0x3f, 0xff, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x15, 0xca, 0xd6, 0xe6, 0xe4, 0x11, - 0x00, 0x00, + 0x06, 0xa9, 0xf9, 0x01, 0x08, 0xc8, 0x5b, 0xb6, 0xbc, 0x55, 0x39, 0x64, 0x2f, 0xac, 0x9e, 0x9e, + 0xee, 0x9e, 0xee, 0x6f, 0xba, 0x1b, 0x3d, 0x04, 0xf5, 0xc2, 0x0d, 0xbc, 0xf0, 0x6a, 0x61, 0x53, + 0xbb, 0x1b, 0xc5, 0x21, 0x0d, 0x11, 0xac, 0x39, 0x4f, 0x94, 0x5b, 0x1a, 0x47, 0x8e, 0xd8, 0x78, + 0xa2, 0xbc, 0x5d, 0x92, 0x78, 0x25, 0x17, 0x6d, 0x1a, 0x46, 0xe1, 0x5a, 0x4b, 0x9b, 0x40, 0x63, + 0x78, 0x6d, 0xc7, 0x09, 0xa1, 0x68, 0x0f, 0xea, 0x8e, 0xe7, 0x92, 0x80, 0x76, 0x4a, 0xfb, 0xa5, + 0x83, 0x1a, 0x96, 0x2b, 0x84, 0xa0, 0xea, 0x84, 0x41, 0xd0, 0x29, 0x73, 0x2e, 0xa7, 0x99, 0x6c, + 0x42, 0xe2, 0x5b, 0x12, 0x77, 0x2a, 0x42, 0x56, 0xac, 0xb4, 0x7f, 0x55, 0x60, 0x7b, 0xc0, 0xfd, + 0x30, 0x63, 0x3b, 0x48, 0x6c, 0x87, 0xba, 0x61, 0x80, 0x4e, 0x00, 0x12, 0x6a, 0x53, 0xe2, 0x93, + 0x80, 0x26, 0x9d, 0xd2, 0x7e, 0xe5, 0x40, 0xe9, 0x3d, 0xeb, 0xe6, 0x22, 0x78, 0x47, 0xa5, 0x3b, + 0x4f, 0xe5, 0x71, 0x4e, 0x15, 0xf5, 0x40, 0x21, 0xb7, 0x24, 0xa0, 0x16, 0x0d, 0x6f, 0x48, 0xd0, + 0xa9, 0xee, 0x97, 0x0e, 0x94, 0xde, 0x76, 0x57, 0x04, 0xa8, 0xb3, 0x1d, 0x93, 0x6d, 0x60, 0x20, + 0x19, 0xfd, 0xe4, 0xef, 0x65, 0x68, 0x65, 0xd6, 0x90, 0x01, 0x4d, 0xc7, 0xa6, 0xe4, 0x2a, 0x8c, + 0x57, 0x3c, 0xcc, 0x76, 0xef, 0xcb, 0x07, 0x3a, 0xd2, 0x1d, 0x4a, 0x3d, 0x9c, 0x59, 0x40, 0x3f, + 0x83, 0x86, 0x23, 0xd0, 0xe3, 0xe8, 0x28, 0xbd, 0x9d, 0xbc, 0x31, 0x09, 0x2c, 0x4e, 0x65, 0x90, + 0x0a, 0x95, 0xe4, 0xad, 0xc7, 0x21, 0xdb, 0xc0, 0x8c, 0xd4, 0xfe, 0x5c, 0x82, 0x66, 0x6a, 0x17, + 0xed, 0xc0, 0xd6, 0xc0, 0xb0, 0x5e, 0x4f, 0xb1, 0x3e, 0x9c, 0x9d, 0x4c, 0xc7, 0xdf, 0xe8, 0x23, + 0xf5, 0x11, 0xda, 0x80, 0xe6, 0xc0, 0xb0, 0x06, 0xfa, 0xc9, 0x78, 0xaa, 0x96, 0xd0, 0x26, 0xb4, + 0x06, 0x86, 0x35, 0x9c, 0x4d, 0x26, 0x63, 0x53, 0x2d, 0xa3, 0x2d, 0x50, 0x06, 0x86, 0x85, 0x67, + 0x86, 0x31, 0xe8, 0x0f, 0x4f, 0xd5, 0x0a, 0x7a, 0x0c, 0xdb, 0x03, 0xc3, 0x1a, 0x4d, 0x0c, 0x6b, + 0xa4, 0x9f, 0x61, 0x7d, 0xd8, 0x37, 0xf5, 0x91, 0x5a, 0x45, 0x00, 0x75, 0xc6, 0x1e, 0x19, 0x6a, + 0x4d, 0xd2, 0x73, 0xdd, 0x54, 0xeb, 0xd2, 0xdc, 0x78, 0x3a, 0xd7, 0xb1, 0xa9, 0x36, 0xe4, 0xf2, + 0xf5, 0xd9, 0xa8, 0x6f, 0xea, 0x6a, 0x53, 0x2e, 0x47, 0xba, 0xa1, 0x9b, 0xba, 0xda, 0x7a, 0x59, + 0x6d, 0x96, 0xd5, 0xca, 0xcb, 0x6a, 0xb3, 0xa2, 0x56, 0xb5, 0x3f, 0x95, 0xe0, 0xf1, 0x9c, 0xc6, + 0xc4, 0xf6, 0x4f, 0xc9, 0x0a, 0xdb, 0xc1, 0x15, 0xc1, 0xe4, 0xed, 0x92, 0x24, 0x14, 0x3d, 0x81, + 0x66, 0x14, 0x26, 0x2e, 0xc3, 0x8e, 0x03, 0xdc, 0xc2, 0xd9, 0x1a, 0x1d, 0x41, 0xeb, 0x86, 0xac, + 0xac, 0x98, 0xc9, 0x4b, 0xc0, 0x50, 0x37, 0x4b, 0xc8, 0xcc, 0x52, 0xf3, 0x46, 0x52, 0x79, 0x7c, + 0x2b, 0x1f, 0xc6, 0x57, 0xbb, 0x84, 0xbd, 0xfb, 0x4e, 0x25, 0x51, 0x18, 0x24, 0x04, 0x19, 0x80, + 0x84, 0xa2, 0x45, 0xd7, 0x77, 0xcb, 0xfd, 0x53, 0x7a, 0x9f, 0xbc, 0x37, 0x01, 0xf0, 0xf6, 0xc5, + 0x7d, 0x96, 0xf6, 0x2d, 0xec, 0x88, 0x73, 0x4c, 0xfb, 0xc2, 0x23, 0xc9, 0x43, 0x42, 0xdf, 0x83, + 0x3a, 0xe5, 0xc2, 0x9d, 0xf2, 0x7e, 0xe5, 0xa0, 0x85, 0xe5, 0xea, 0x63, 0x23, 0x5c, 0xc0, 0x6e, + 0xf1, 0xe4, 0xff, 0x49, 0x7c, 0xbf, 0x84, 0x2a, 0x5e, 0x7a, 0x04, 0xed, 0x42, 0xcd, 0xb7, 0xa9, + 0x73, 0x2d, 0xa3, 0x11, 0x0b, 0x16, 0xca, 0xa5, 0xeb, 0x51, 0x12, 0xf3, 0x2b, 0x6c, 0x61, 0xb9, + 0xd2, 0xfe, 0x52, 0x82, 0xfa, 0x31, 0x27, 0xd1, 0xe7, 0x50, 0x8b, 0x97, 0x2c, 0x58, 0x51, 0xeb, + 0x6a, 0xde, 0x03, 0x66, 0x19, 0x8b, 0x6d, 0x34, 0x86, 0xf6, 0xa5, 0x4b, 0xbc, 0x05, 0x2f, 0xdd, + 0x49, 0xb8, 0x10, 0x59, 0xd1, 0xee, 0x7d, 0x96, 0x57, 0x10, 0x36, 0xbb, 0xc7, 0x05, 0x41, 0x7c, + 0x4f, 0x51, 0x7b, 0x0e, 0xed, 0xa2, 0x04, 0x2b, 0x27, 0x1d, 0x63, 0x6b, 0x36, 0xb5, 0x26, 0xe3, + 0xf9, 0xa4, 0x6f, 0x0e, 0x5f, 0xa8, 0x8f, 0x78, 0xc5, 0xe8, 0x73, 0xd3, 0xd2, 0x8f, 0x8f, 0x67, + 0xd8, 0x54, 0x4b, 0xda, 0xbf, 0xcb, 0xb0, 0x21, 0x40, 0x99, 0x87, 0xcb, 0xd8, 0x21, 0xec, 0x16, + 0x6f, 0xc8, 0x2a, 0x89, 0x6c, 0x87, 0xa4, 0xb7, 0x98, 0xae, 0x19, 0x20, 0xc9, 0xb5, 0x1d, 0x2f, + 0x64, 0xe4, 0x62, 0x81, 0x7e, 0x05, 0x0a, 0xbf, 0x4d, 0x6a, 0xd1, 0x55, 0x44, 0xf8, 0x3d, 0xb6, + 0x7b, 0xbb, 0xeb, 0xc4, 0xe6, 0x77, 0x45, 0xcd, 0x55, 0x44, 0x30, 0xd0, 0x8c, 0x2e, 0x56, 0x43, + 0xf5, 0x01, 0xd5, 0xb0, 0xce, 0xa1, 0x5a, 0x21, 0x87, 0x0e, 0xb3, 0x0b, 0xa9, 0x4b, 0x2b, 0xef, + 0xa0, 0x97, 0x5e, 0x12, 0xea, 0x42, 0x3d, 0x0c, 0xac, 0xc5, 0xc2, 0xeb, 0x34, 0xb8, 0x9b, 0x3f, + 0xca, 0xcb, 0xce, 0x82, 0xd1, 0xc8, 0xe8, 0x8b, 0xb4, 0xa8, 0x85, 0xc1, 0x68, 0xe1, 0xa1, 0xa7, + 0xd0, 0x26, 0xdf, 0x52, 0x12, 0x07, 0xb6, 0x67, 0xf9, 0x2b, 0xd6, 0xbd, 0x9a, 0x3c, 0xf4, 0xcd, + 0x94, 0x3b, 0x61, 0x4c, 0xf4, 0x39, 0x6c, 0x25, 0x34, 0x8c, 0x2c, 0xfb, 0x92, 0x92, 0xd8, 0x72, + 0xc2, 0x68, 0xd5, 0x69, 0xed, 0x97, 0x0e, 0x9a, 0x78, 0x93, 0xb1, 0xfb, 0x8c, 0x3b, 0x0c, 0xa3, + 0x95, 0xf6, 0x0a, 0x5a, 0x38, 0xbc, 0x1b, 0x5e, 0xf3, 0x78, 0x34, 0xa8, 0x5f, 0x90, 0xcb, 0x30, + 0x26, 0x32, 0x51, 0x41, 0x36, 0x72, 0x1c, 0xde, 0x61, 0xb9, 0x83, 0xf6, 0xa1, 0xc6, 0x6d, 0xca, + 0x76, 0x91, 0x17, 0x11, 0x1b, 0x9a, 0x0d, 0x4d, 0x1c, 0xde, 0xf1, 0x6b, 0x47, 0x9f, 0x80, 0x00, + 0xd8, 0x0a, 0x6c, 0x3f, 0xbd, 0xbd, 0x16, 0xe7, 0x4c, 0x6d, 0x9f, 0xa0, 0xe7, 0xa0, 0xc4, 0xe1, + 0x9d, 0xe5, 0xf0, 0xe3, 0x45, 0x25, 0x2a, 0xbd, 0xc7, 0x85, 0xe4, 0x4c, 0x9d, 0xc3, 0x10, 0xa7, + 0x64, 0xa2, 0xbd, 0x02, 0x58, 0xe7, 0xd6, 0x87, 0x0e, 0xf9, 0x29, 0xbb, 0x0d, 0xe2, 0x2d, 0x52, + 0xfb, 0x1b, 0xd2, 0x65, 0x6e, 0x01, 0xcb, 0x3d, 0x06, 0xc4, 0x9c, 0x25, 0xcf, 0x09, 0x75, 0x17, + 0xdf, 0x23, 0xe5, 0x10, 0x54, 0xaf, 0xa8, 0xbb, 0xe0, 0xb9, 0xd6, 0xc2, 0x9c, 0xd6, 0xbe, 0x86, + 0xda, 0x39, 0x37, 0xf7, 0x1c, 0x14, 0x2e, 0x65, 0x31, 0x76, 0x5a, 0x83, 0x85, 0x30, 0xb3, 0xa3, + 0x31, 0x24, 0x29, 0x99, 0x68, 0x7d, 0xd8, 0x3c, 0x95, 0xc7, 0x72, 0x81, 0x8f, 0xf7, 0x4b, 0xfb, + 0x6b, 0x19, 0x1a, 0x2f, 0xc3, 0x25, 0x4b, 0x0c, 0xd4, 0x86, 0xb2, 0xbb, 0xe0, 0x7a, 0x15, 0x5c, + 0x76, 0x17, 0xe8, 0xb7, 0xd0, 0xf6, 0xdd, 0xab, 0xd8, 0x66, 0xe9, 0x25, 0x2a, 0x45, 0x14, 0xfb, + 0x8f, 0xf3, 0x9e, 0x4d, 0x52, 0x09, 0x5e, 0x2e, 0x9b, 0x7e, 0x7e, 0x99, 0x2b, 0x80, 0x4a, 0xa1, + 0x00, 0x9e, 0x42, 0xdb, 0x0b, 0x1d, 0xdb, 0xb3, 0xb2, 0xf6, 0x5b, 0x15, 0x49, 0xca, 0xb9, 0x67, + 0x69, 0x0f, 0xbe, 0x87, 0x4b, 0xed, 0x81, 0xb8, 0xa0, 0xaf, 0x60, 0x23, 0xb2, 0x63, 0xea, 0x3a, + 0x6e, 0x64, 0xb3, 0x01, 0xa6, 0xce, 0x15, 0x0b, 0x6e, 0x17, 0x70, 0xc3, 0x05, 0x71, 0xf4, 0x05, + 0xa8, 0x09, 0x6f, 0x2d, 0xd6, 0x5d, 0x18, 0xdf, 0x5c, 0x7a, 0xe1, 0x5d, 0xd2, 0x69, 0x70, 0xff, + 0xb7, 0x04, 0xff, 0x4d, 0xca, 0xd6, 0xfe, 0x53, 0x86, 0xfa, 0xb9, 0xc8, 0xb2, 0x43, 0xa8, 0x72, + 0x8c, 0xc4, 0x90, 0xb2, 0x97, 0x3f, 0x4c, 0x48, 0x70, 0x80, 0xb8, 0x0c, 0xfa, 0x09, 0xb4, 0xa8, + 0xeb, 0x93, 0x84, 0xda, 0x7e, 0xc4, 0x41, 0xad, 0xe0, 0x35, 0xe3, 0xbb, 0x72, 0x85, 0x4d, 0x22, + 0xac, 0x07, 0x08, 0x98, 0x18, 0x89, 0x7e, 0x0e, 0x2d, 0x56, 0x1b, 0x7c, 0x70, 0xea, 0xd4, 0x78, + 0xb1, 0xed, 0xde, 0xab, 0x0c, 0x7e, 0x2c, 0x6e, 0xc6, 0x69, 0xb5, 0xfd, 0x1a, 0x14, 0x9e, 0xcd, + 0x52, 0x49, 0x34, 0x9f, 0xbd, 0x62, 0xf3, 0x49, 0xab, 0x06, 0xc3, 0xba, 0x5f, 0xa3, 0x67, 0x50, + 0xbb, 0xe5, 0x2e, 0x35, 0xe4, 0x00, 0x97, 0x0f, 0x8e, 0xc3, 0x2f, 0xf6, 0xd9, 0xd7, 0xf1, 0xf7, + 0x22, 0x9b, 0x78, 0xdb, 0xb9, 0xf7, 0x75, 0x94, 0x89, 0x86, 0x53, 0x19, 0x1e, 0x95, 0xef, 0xf1, + 0xce, 0xc3, 0xa2, 0xf2, 0x3d, 0xf4, 0x19, 0x6c, 0x38, 0xcb, 0x38, 0xe6, 0x23, 0xa3, 0xeb, 0x93, + 0xce, 0x2e, 0x07, 0x47, 0x91, 0x3c, 0xd3, 0xf5, 0x89, 0x76, 0x0d, 0x1b, 0x13, 0x37, 0x70, 0x7d, + 0xdb, 0xe3, 0x7d, 0x9a, 0xc1, 0x95, 0x2b, 0x6c, 0x4e, 0x3f, 0xac, 0xa6, 0xd1, 0xa7, 0xa0, 0x44, + 0xd6, 0x8d, 0xe5, 0x84, 0xde, 0xd2, 0x0f, 0x44, 0x8e, 0x56, 0x70, 0x2b, 0x3a, 0x1d, 0x0a, 0x06, + 0xab, 0x2f, 0x79, 0xd2, 0xdc, 0xb9, 0x26, 0xbe, 0x8d, 0xbe, 0xcc, 0xf2, 0x59, 0xd4, 0x68, 0xa7, + 0x58, 0x09, 0x6b, 0xa7, 0xd2, 0x4c, 0xd7, 0xfe, 0x58, 0x86, 0xf6, 0xb9, 0x98, 0x00, 0xd2, 0xa9, + 0xe3, 0x6b, 0xd8, 0x21, 0x97, 0x97, 0xc4, 0xa1, 0xee, 0x2d, 0xb1, 0x1c, 0xdb, 0xf3, 0x48, 0x6c, + 0xc9, 0xba, 0x53, 0x7a, 0x5b, 0x5d, 0xf1, 0x12, 0x18, 0x72, 0xfe, 0x78, 0x84, 0xb7, 0x33, 0x59, + 0xc9, 0x5a, 0x20, 0x1d, 0x76, 0x5c, 0xdf, 0x27, 0x0b, 0xd7, 0xa6, 0x79, 0x03, 0xa2, 0xe1, 0x3e, + 0x96, 0x91, 0x9e, 0x9b, 0x27, 0x36, 0x25, 0x6b, 0x33, 0x99, 0x46, 0x66, 0xe6, 0x29, 0x0b, 0x26, + 0xbe, 0xca, 0x06, 0x99, 0x4d, 0xa9, 0x69, 0x72, 0x26, 0x96, 0x9b, 0x85, 0x21, 0xa9, 0x7a, 0x6f, + 0x48, 0x5a, 0x7f, 0xc8, 0x6a, 0x1f, 0xfa, 0x90, 0x69, 0x5f, 0xc1, 0x56, 0x06, 0x84, 0x1c, 0x82, + 0x0e, 0xa1, 0xce, 0x33, 0x31, 0x85, 0x13, 0xbd, 0x5b, 0x34, 0x58, 0x4a, 0x68, 0x7f, 0x28, 0x03, + 0x4a, 0xf5, 0xc3, 0xbb, 0xe4, 0xff, 0x14, 0xcc, 0x5d, 0xa8, 0x71, 0xbe, 0x44, 0x52, 0x2c, 0x18, + 0x0e, 0x9e, 0x9d, 0xd0, 0xe8, 0x26, 0x83, 0x51, 0x28, 0xbf, 0x62, 0xbf, 0x98, 0x24, 0x4b, 0x8f, + 0x62, 0x29, 0xa1, 0xfd, 0xad, 0x04, 0x3b, 0x05, 0x1c, 0x24, 0x96, 0xeb, 0x8c, 0x2f, 0xbd, 0x27, + 0xe3, 0x0f, 0xa0, 0x19, 0xdd, 0xbc, 0xa7, 0x32, 0xb2, 0xdd, 0xef, 0x6c, 0x42, 0x9f, 0x42, 0x35, + 0x66, 0xcd, 0xb0, 0xca, 0x35, 0xf3, 0x9f, 0x76, 0xce, 0x67, 0xf3, 0x41, 0x21, 0x8e, 0xc2, 0x7c, + 0x20, 0xfd, 0xff, 0x47, 0x09, 0x1e, 0xaf, 0xf3, 0x60, 0xe9, 0xd1, 0x1f, 0xd4, 0x55, 0x6a, 0x31, + 0xec, 0xdd, 0x8f, 0xee, 0xa3, 0x2e, 0xe8, 0x7b, 0xc0, 0x7e, 0xf8, 0x1b, 0x50, 0x72, 0x83, 0x20, + 0x7b, 0x2f, 0x8e, 0x4f, 0xa6, 0x33, 0xac, 0xab, 0x8f, 0x50, 0x13, 0xaa, 0x73, 0x73, 0x76, 0xa6, + 0x96, 0x18, 0xa5, 0xff, 0x4e, 0x1f, 0x8a, 0x37, 0x28, 0xa3, 0x2c, 0x29, 0x54, 0x39, 0xfc, 0x67, + 0x09, 0x60, 0xfd, 0x89, 0x42, 0x0a, 0x34, 0x5e, 0x4f, 0x4f, 0xa7, 0xb3, 0x37, 0x53, 0x61, 0xe0, + 0xc4, 0x1c, 0x8f, 0xd4, 0x12, 0x6a, 0x41, 0x4d, 0x3c, 0x6a, 0xcb, 0xec, 0x04, 0xf9, 0xa2, 0xad, + 0xb0, 0xe7, 0x6e, 0xf6, 0x9c, 0xad, 0xa2, 0x06, 0x54, 0xb2, 0x47, 0xab, 0x7c, 0xa5, 0xd6, 0x99, + 0x41, 0xac, 0x9f, 0x19, 0xfd, 0xa1, 0xae, 0x36, 0xd8, 0x46, 0xf6, 0x5e, 0x05, 0xa8, 0xa7, 0x8f, + 0x55, 0xa6, 0xc9, 0x9e, 0xb8, 0xc0, 0xce, 0x99, 0x99, 0x2f, 0x74, 0xac, 0x2a, 0x8c, 0x87, 0x67, + 0x6f, 0xd4, 0x0d, 0xc6, 0x3b, 0x1e, 0xeb, 0xc6, 0x48, 0xdd, 0x64, 0x6f, 0xdc, 0x17, 0x7a, 0x1f, + 0x9b, 0x03, 0xbd, 0x6f, 0xaa, 0x6d, 0xb6, 0x73, 0xce, 0x1d, 0xdc, 0x62, 0xc7, 0xbc, 0x9c, 0xbd, + 0xc6, 0xd3, 0xbe, 0xa1, 0xaa, 0x6c, 0x71, 0xae, 0xe3, 0xf9, 0x78, 0x36, 0x55, 0xb7, 0x0f, 0x9f, + 0xb1, 0x3e, 0x9e, 0x9f, 0x4b, 0x00, 0xea, 0x66, 0x7f, 0x60, 0xe8, 0x73, 0xf5, 0x11, 0xa3, 0xe7, + 0x2f, 0xfa, 0x78, 0x34, 0x57, 0x4b, 0x83, 0x2f, 0xbe, 0x79, 0x76, 0xeb, 0x52, 0x92, 0x24, 0x5d, + 0x37, 0x3c, 0x12, 0xd4, 0xd1, 0x55, 0x78, 0x74, 0x4b, 0x8f, 0xf8, 0x9f, 0x2f, 0x47, 0xeb, 0xf6, + 0x74, 0x51, 0xe7, 0x9c, 0x5f, 0xfc, 0x37, 0x00, 0x00, 0xff, 0xff, 0x1b, 0xb2, 0xd4, 0x99, 0xd8, + 0x11, 0x00, 0x00, } diff --git a/go/vt/vttablet/tabletserver/schema/historian.go b/go/vt/vttablet/tabletserver/schema/historian.go index 5de64d140f0..ace106c6cba 100644 --- a/go/vt/vttablet/tabletserver/schema/historian.go +++ b/go/vt/vttablet/tabletserver/schema/historian.go @@ -42,7 +42,7 @@ const getSchemaVersions = "select id, pos, ddl, time_updated, schemax from _vt.s type Historian interface { RegisterVersionEvent() error Reload(ctx context.Context) error - GetTableForPos(tableName sqlparser.TableIdent, pos string) *binlogdata.TableMetaData + GetTableForPos(tableName sqlparser.TableIdent, pos string) *binlogdata.MinimalTable Open() error Init(tabletType topodatapb.TabletType) error SetTrackSchemaVersions(val bool) @@ -61,11 +61,9 @@ type HistoryEngine interface { GetSchema() map[string]*Table } -// TODO: rename TableMetaData and TableMetaDataCollection: to MinimalSchema and MinimalTable? - // TrackedSchema has the snapshot of the table at a given pos (reached by ddl) type TrackedSchema struct { - schema map[string]*binlogdata.TableMetaData + schema map[string]*binlogdata.MinimalTable pos mysql.Position ddl string } @@ -82,7 +80,7 @@ type HistorianSvc struct { isMaster bool mu sync.Mutex trackSchemaVersions bool - lastSchema map[string]*binlogdata.TableMetaData + lastSchema map[string]*binlogdata.MinimalTable } // Reload gets the latest schema from the database @@ -110,13 +108,13 @@ func (h *HistorianSvc) readRow(row []sqltypes.Value) (*TrackedSchema, int64, err } ddl := string(row[2].ToBytes()) timeUpdated, _ := evalengine.ToInt64(row[3]) - sch := &binlogdata.TableMetaDataCollection{} + sch := &binlogdata.MinimalSchema{} if err := proto.Unmarshal(row[4].ToBytes(), sch); err != nil { return nil, 0, err } log.Infof("Read tracked schema from db: id %d, pos %v, ddl %s, schema len %d, time_updated %d \n", id, mysql.EncodePosition(pos), ddl, len(sch.Tables), timeUpdated) - tables := map[string]*binlogdata.TableMetaData{} + tables := map[string]*binlogdata.MinimalTable{} for _, t := range sch.Tables { tables[t.Name] = t } @@ -189,10 +187,10 @@ func (h *HistorianSvc) reload(ctx context.Context) error { if err := h.he.Reload(ctx); err != nil { return err } - tables := map[string]*binlogdata.TableMetaData{} + tables := map[string]*binlogdata.MinimalTable{} log.Infof("Schema returned by engine has %d tables", len(h.he.GetSchema())) for _, t := range h.he.GetSchema() { - table := &binlogdata.TableMetaData{ + table := &binlogdata.MinimalTable{ Name: t.Name.String(), Fields: t.Fields, } @@ -211,7 +209,7 @@ func (h *HistorianSvc) reload(ctx context.Context) error { } // GetTableForPos returns a best-effort schema for a specific gtid -func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid string) *binlogdata.TableMetaData { +func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid string) *binlogdata.MinimalTable { log.Infof("GetTableForPos called for %s with pos %s", tableName, gtid) if gtid != "" { pos, err := mysql.DecodePosition(gtid) @@ -219,7 +217,7 @@ func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid strin log.Errorf("Error decoding position for %s: %v", gtid, err) return nil } - var t *binlogdata.TableMetaData + var t *binlogdata.MinimalTable if h.trackSchemaVersions && len(h.schemas) > 0 { t = h.getTableFromHistoryForPos(tableName, pos) } @@ -246,7 +244,7 @@ func (h *HistorianSvc) sortSchemas() { } // getTableFromHistoryForPos looks in the cache for a schema for a specific gtid -func (h *HistorianSvc) getTableFromHistoryForPos(tableName sqlparser.TableIdent, pos mysql.Position) *binlogdata.TableMetaData { +func (h *HistorianSvc) getTableFromHistoryForPos(tableName sqlparser.TableIdent, pos mysql.Position) *binlogdata.MinimalTable { idx := sort.Search(len(h.schemas), func(i int) bool { log.Infof("idx %d pos %s checking pos %s equality %t less %t", i, mysql.EncodePosition(pos), mysql.EncodePosition(h.schemas[i].pos), pos.Equal(h.schemas[i].pos), !pos.AtLeast(h.schemas[i].pos)) return pos.Equal(h.schemas[i].pos) || !pos.AtLeast(h.schemas[i].pos) diff --git a/go/vt/vttablet/tabletserver/schema/historian_test.go b/go/vt/vttablet/tabletserver/schema/historian_test.go index 4d6e373ee47..d9c68a80f16 100644 --- a/go/vt/vttablet/tabletserver/schema/historian_test.go +++ b/go/vt/vttablet/tabletserver/schema/historian_test.go @@ -29,7 +29,7 @@ import ( "vitess.io/vitess/go/vt/sqlparser" ) -func getTable(name string, fieldNames []string, fieldTypes []querypb.Type, pks []int64) *binlogdatapb.TableMetaData { +func getTable(name string, fieldNames []string, fieldTypes []querypb.Type, pks []int64) *binlogdatapb.MinimalTable { if name == "" || len(fieldNames) == 0 || len(fieldNames) != len(fieldTypes) || len(pks) == 0 { return nil } @@ -41,7 +41,7 @@ func getTable(name string, fieldNames []string, fieldTypes []querypb.Type, pks [ Table: name, }) } - table := &binlogdatapb.TableMetaData{ + table := &binlogdatapb.MinimalTable{ Name: name, Fields: fields, PKColumns: pks, @@ -49,12 +49,12 @@ func getTable(name string, fieldNames []string, fieldTypes []querypb.Type, pks [ return table } -func getDbSchemaBlob(t *testing.T, tables map[string]*binlogdatapb.TableMetaData) string { - dbSchema := &binlogdatapb.TableMetaDataCollection{ - Tables: []*binlogdatapb.TableMetaData{}, +func getDbSchemaBlob(t *testing.T, tables map[string]*binlogdatapb.MinimalTable) string { + dbSchema := &binlogdatapb.MinimalSchema{ + Tables: []*binlogdatapb.MinimalTable{}, } for name, table := range tables { - t := &binlogdatapb.TableMetaData{ + t := &binlogdatapb.MinimalTable{ Name: name, Fields: table.Fields, } @@ -108,7 +108,7 @@ func TestHistorian(t *testing.T) { }} table := getTable("t1", []string{"id1", "id2"}, []querypb.Type{querypb.Type_INT32, querypb.Type_INT32}, []int64{0}) - tables := make(map[string]*binlogdatapb.TableMetaData) + tables := make(map[string]*binlogdatapb.MinimalTable) tables["t1"] = table blob1 = getDbSchemaBlob(t, tables) db.AddQuery("select id, pos, ddl, time_updated, schemax from _vt.schema_version where id > 0 order by id asc", &sqltypes.Result{ diff --git a/go/vt/vttablet/tabletserver/schema/tracker.go b/go/vt/vttablet/tabletserver/schema/tracker.go index 4e53dd8635d..a347a17af6e 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker.go +++ b/go/vt/vttablet/tabletserver/schema/tracker.go @@ -64,16 +64,16 @@ func NewTracker(engine trackerEngine) *Tracker { func (t *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) error { if gtid == "" || ddl == "" { - return fmt.Errorf("Got invalid gtid or ddl in SchemaUpdated") + return fmt.Errorf("got invalid gtid or ddl in SchemaUpdated") } ctx := context.Background() t.engine.Reload(ctx) tables := t.engine.GetSchema() - dbSchema := &binlogdatapb.TableMetaDataCollection{ - Tables: []*binlogdatapb.TableMetaData{}, + dbSchema := &binlogdatapb.MinimalSchema{ + Tables: []*binlogdatapb.MinimalTable{}, } for name, table := range tables { - t := &binlogdatapb.TableMetaData{ + t := &binlogdatapb.MinimalTable{ Name: name, Fields: table.Fields, } diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go index 6459ac1a0d0..e6fa88c4989 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer.go @@ -140,7 +140,7 @@ func (rs *rowStreamer) buildPlan() error { return err } -func buildPKColumns(st *binlogdatapb.TableMetaData) ([]int, error) { +func buildPKColumns(st *binlogdatapb.MinimalTable) ([]int, error) { var pkColumns = make([]int, 0) if len(st.PKColumns) == 0 { pkColumns = make([]int, len(st.Fields)) diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index 1e93b6c4061..e2d601322e2 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -67,7 +67,7 @@ func newMockHistorian(he schema.HistoryEngine) *mockHistorian { return &sh } -func (h *mockHistorian) GetTableForPos(tableName sqlparser.TableIdent, pos string) *binlogdatapb.TableMetaData { +func (h *mockHistorian) GetTableForPos(tableName sqlparser.TableIdent, pos string) *binlogdatapb.MinimalTable { return nil } diff --git a/proto/binlogdata.proto b/proto/binlogdata.proto index 41ba235a019..6f88e03c016 100644 --- a/proto/binlogdata.proto +++ b/proto/binlogdata.proto @@ -343,14 +343,14 @@ message VEvent { int64 current_time = 20; } -message TableMetaData { +message MinimalTable { string name = 1; repeated query.Field fields = 2; repeated int64 p_k_columns = 3; } -message TableMetaDataCollection { - repeated TableMetaData tables = 1; +message MinimalSchema { + repeated MinimalTable tables = 1; } // VStreamRequest is the payload for VStreamer From 80be5de7ed05903cd5b38a25c160509917604902 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Tue, 5 May 2020 23:17:30 +0200 Subject: [PATCH 11/17] Fix incorrect commit Signed-off-by: Rohit Nayak --- go/vt/binlog/binlogplayer/binlog_player.go | 10 ++++++---- go/vt/vttablet/tabletmanager/vreplication/engine.go | 2 +- .../vttablet/tabletmanager/vreplication/engine_test.go | 1 - 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/go/vt/binlog/binlogplayer/binlog_player.go b/go/vt/binlog/binlogplayer/binlog_player.go index 92baba07bd7..d77328a0018 100644 --- a/go/vt/binlog/binlogplayer/binlog_player.go +++ b/go/vt/binlog/binlogplayer/binlog_player.go @@ -492,10 +492,11 @@ func (blp *BinlogPlayer) setVReplicationState(state, message string) error { // transaction_timestamp: timestamp of the transaction (from the master). // state: Running, Error or Stopped. // message: Reason for current state. -var CreateVtReplication = []string{ - "CREATE DATABASE IF NOT EXISTS _vt", - "DROP TABLE IF EXISTS _vt.blp_checkpoint", - `CREATE TABLE IF NOT EXISTS _vt.vreplication ( +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, @@ -512,6 +513,7 @@ var CreateVtReplication = []string{ db_name VARBINARY(255) NOT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB`, + } } // AlterVReplicationTable adds new columns to vreplication table diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine.go b/go/vt/vttablet/tabletmanager/vreplication/engine.go index 489bbe5b4b5..0fdee83bdbe 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine.go @@ -175,7 +175,7 @@ func (vre *Engine) executeFetchMaybeCreateTable(dbClient binlogplayer.DBClient, log.Info("Looks like the vreplication tables may not exist. Trying to recreate... ") if merr.Num == mysql.ERNoSuchTable || merr.Num == mysql.ERBadDb { - for _, query := range binlogplayer.CreateVtReplication { + for _, query := range binlogplayer.CreateVReplicationTable() { if _, merr := dbClient.ExecuteFetch(query, 0); merr != nil { log.Warningf("Failed to ensure %s exists: %v", vreplicationTableName, merr) return nil, err diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine_test.go b/go/vt/vttablet/tabletmanager/vreplication/engine_test.go index 2e78b93e4c4..feccaa71b3f 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine_test.go @@ -450,7 +450,6 @@ func TestCreateDBAndTable(t *testing.T) { dbClient.ExpectRequest("CREATE DATABASE IF NOT EXISTS _vt", &sqltypes.Result{}, nil) dbClient.ExpectRequest("DROP TABLE IF EXISTS _vt.blp_checkpoint", &sqltypes.Result{}, nil) dbClient.ExpectRequestRE("CREATE TABLE IF NOT EXISTS _vt.vreplication.*", &sqltypes.Result{}, nil) - dbClient.ExpectRequestRE("CREATE TABLE IF NOT EXISTS _vt.schema_version.*", &sqltypes.Result{}, nil) dbClient.ExpectRequestRE("create table if not exists _vt.resharding_journal.*", &sqltypes.Result{}, nil) dbClient.ExpectRequest("use _vt", &sqltypes.Result{}, nil) From e8776768f1a7fc655b17d6e37279f598132c40a3 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Tue, 5 May 2020 23:58:08 +0200 Subject: [PATCH 12/17] Fix race Signed-off-by: Rohit Nayak --- go/vt/vttablet/tabletmanager/vreplication/journal_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/go/vt/vttablet/tabletmanager/vreplication/journal_test.go b/go/vt/vttablet/tabletmanager/vreplication/journal_test.go index 32402c1fdb4..884cc042790 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/journal_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/journal_test.go @@ -36,6 +36,7 @@ func TestJournalOneToOne(t *testing.T) { "drop table t", fmt.Sprintf("drop table %s.t", vrepldb), }) + env.SchemaEngine.Open() env.SchemaEngine.Reload(context.Background()) filter := &binlogdatapb.Filter{ From 88b9982c6bd6047a37578fcfbb95a8a60bd515cd Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Wed, 6 May 2020 15:05:31 +0200 Subject: [PATCH 13/17] Code cleanup Signed-off-by: Rohit Nayak --- go/vt/vttablet/endtoend/vstreamer_test.go | 4 +- .../tabletserver/replication_watcher_test.go | 16 +++---- .../vttablet/tabletserver/schema/historian.go | 35 +++++++--------- go/vt/vttablet/tabletserver/schema/tracker.go | 1 - .../tabletserver/vstreamer/main_test.go | 38 +++++++++++++++++ .../tabletserver/vstreamer/vstreamer_test.go | 42 +------------------ 6 files changed, 66 insertions(+), 70 deletions(-) diff --git a/go/vt/vttablet/endtoend/vstreamer_test.go b/go/vt/vttablet/endtoend/vstreamer_test.go index d5e08449620..240cb777222 100644 --- a/go/vt/vttablet/endtoend/vstreamer_test.go +++ b/go/vt/vttablet/endtoend/vstreamer_test.go @@ -178,7 +178,7 @@ func TestSchemaVersioning(t *testing.T) { } runCases(ctx, t, cases, eventCh) cancel() - log.Infof("\n\n\n=============================================== PAST EVENTS START HERE ======================\n\n\n") + log.Infof("\n\n\n=============================================== PAST EVENTS WITH TRACK VERSIONS START HERE ======================\n\n\n") ctx, cancel = context.WithCancel(context.Background()) defer cancel() eventCh = make(chan []*binlogdatapb.VEvent) @@ -359,6 +359,7 @@ func expectLogs(ctx context.Context, t *testing.T, query string, eventCh chan [] if ev.Type == binlogdatapb.VEventType_HEARTBEAT { continue } + // Also ignore begin/commit to reduce list of events to expect, for readability ... if ev.Type == binlogdatapb.VEventType_BEGIN { continue } @@ -429,6 +430,7 @@ func validateSchemaInserted(client *framework.QueryClient, ddl string) (bool, er return false, fmt.Errorf("Found %d rows for gtid %s", len(qr.Rows), ddl) } +// To avoid races between ddls and the historian refreshing its cache explicitly wait for tracker's insert to be visible func waitForVersionInsert(client *framework.QueryClient, ddl string) (bool, error) { timeout := time.After(1000 * time.Millisecond) tick := time.Tick(100 * time.Millisecond) diff --git a/go/vt/vttablet/tabletserver/replication_watcher_test.go b/go/vt/vttablet/tabletserver/replication_watcher_test.go index eb508c3a81c..b4f645b2773 100644 --- a/go/vt/vttablet/tabletserver/replication_watcher_test.go +++ b/go/vt/vttablet/tabletserver/replication_watcher_test.go @@ -74,14 +74,14 @@ func TestReplicationWatcher(t *testing.T) { Ddl: "create table", }, { Type: binlogdatapb.VEventType_INSERT, - Timestamp: 643, - CurrentTime: 943, + Timestamp: 644, + CurrentTime: 944, Gtid: "gtid2", Ddl: "insert", }, { Type: binlogdatapb.VEventType_DDL, - Timestamp: 643, - CurrentTime: 943, + Timestamp: 645, + CurrentTime: 945, Gtid: "gtid3", Ddl: "alter table", }}}, @@ -131,19 +131,19 @@ func (f *fakeEnv) CheckMySQL() { } func (f *fakeEnv) Config() *tabletenv.TabletConfig { - panic("implement me") + return nil } func (f *fakeEnv) DBConfigs() *dbconfigs.DBConfigs { - panic("implement me") + return nil } func (f *fakeEnv) Exporter() *servenv.Exporter { - panic("implement me") + return nil } func (f *fakeEnv) Stats() *tabletenv.Stats { - panic("implement me") + return nil } func (f *fakeEnv) LogError() { diff --git a/go/vt/vttablet/tabletserver/schema/historian.go b/go/vt/vttablet/tabletserver/schema/historian.go index ace106c6cba..b3733d795fe 100644 --- a/go/vt/vttablet/tabletserver/schema/historian.go +++ b/go/vt/vttablet/tabletserver/schema/historian.go @@ -1,5 +1,5 @@ /* -Copyright 2019 The Vitess Authors. +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. @@ -27,7 +27,7 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/log" - "vitess.io/vitess/go/vt/proto/binlogdata" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" @@ -42,7 +42,7 @@ const getSchemaVersions = "select id, pos, ddl, time_updated, schemax from _vt.s type Historian interface { RegisterVersionEvent() error Reload(ctx context.Context) error - GetTableForPos(tableName sqlparser.TableIdent, pos string) *binlogdata.MinimalTable + GetTableForPos(tableName sqlparser.TableIdent, pos string) *binlogdatapb.MinimalTable Open() error Init(tabletType topodatapb.TabletType) error SetTrackSchemaVersions(val bool) @@ -63,7 +63,7 @@ type HistoryEngine interface { // TrackedSchema has the snapshot of the table at a given pos (reached by ddl) type TrackedSchema struct { - schema map[string]*binlogdata.MinimalTable + schema map[string]*binlogdatapb.MinimalTable pos mysql.Position ddl string } @@ -80,7 +80,7 @@ type HistorianSvc struct { isMaster bool mu sync.Mutex trackSchemaVersions bool - lastSchema map[string]*binlogdata.MinimalTable + lastSchema map[string]*binlogdatapb.MinimalTable } // Reload gets the latest schema from the database @@ -108,13 +108,14 @@ func (h *HistorianSvc) readRow(row []sqltypes.Value) (*TrackedSchema, int64, err } ddl := string(row[2].ToBytes()) timeUpdated, _ := evalengine.ToInt64(row[3]) - sch := &binlogdata.MinimalSchema{} + sch := &binlogdatapb.MinimalSchema{} if err := proto.Unmarshal(row[4].ToBytes(), sch); err != nil { return nil, 0, err } - log.Infof("Read tracked schema from db: id %d, pos %v, ddl %s, schema len %d, time_updated %d \n", id, mysql.EncodePosition(pos), ddl, len(sch.Tables), timeUpdated) + log.Infof("Read tracked schema from db: id %d, pos %v, ddl %s, schema len %d, time_updated %d \n", + id, mysql.EncodePosition(pos), ddl, len(sch.Tables), timeUpdated) - tables := map[string]*binlogdata.MinimalTable{} + tables := map[string]*binlogdatapb.MinimalTable{} for _, t := range sch.Tables { tables[t.Name] = t } @@ -160,11 +161,9 @@ func (h *HistorianSvc) loadFromDB(ctx context.Context) error { func (h *HistorianSvc) Init(tabletType topodatapb.TabletType) error { h.Open() ctx := tabletenv.LocalContext() - if !h.trackSchemaVersions { return nil } - h.isMaster = tabletType == topodatapb.TabletType_MASTER if h.isMaster { h.Reload(ctx) @@ -176,7 +175,7 @@ func (h *HistorianSvc) Init(tabletType topodatapb.TabletType) error { } // Open opens the underlying schema Engine. Called directly by a user purely interested in schema.Engine functionality -// Other users like tabletserver and tests call Init +// Other users like tabletserver and historian tests call Init func (h *HistorianSvc) Open() error { return h.he.Open() } @@ -187,10 +186,10 @@ func (h *HistorianSvc) reload(ctx context.Context) error { if err := h.he.Reload(ctx); err != nil { return err } - tables := map[string]*binlogdata.MinimalTable{} + tables := map[string]*binlogdatapb.MinimalTable{} log.Infof("Schema returned by engine has %d tables", len(h.he.GetSchema())) for _, t := range h.he.GetSchema() { - table := &binlogdata.MinimalTable{ + table := &binlogdatapb.MinimalTable{ Name: t.Name.String(), Fields: t.Fields, } @@ -200,7 +199,6 @@ func (h *HistorianSvc) reload(ctx context.Context) error { } table.PKColumns = pkc tables[table.Name] = table - //log.Infof("Historian, found table %s", table.Name) } h.mu.Lock() defer h.mu.Unlock() @@ -209,7 +207,7 @@ func (h *HistorianSvc) reload(ctx context.Context) error { } // GetTableForPos returns a best-effort schema for a specific gtid -func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid string) *binlogdata.MinimalTable { +func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid string) *binlogdatapb.MinimalTable { log.Infof("GetTableForPos called for %s with pos %s", tableName, gtid) if gtid != "" { pos, err := mysql.DecodePosition(gtid) @@ -217,7 +215,7 @@ func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid strin log.Errorf("Error decoding position for %s: %v", gtid, err) return nil } - var t *binlogdata.MinimalTable + var t *binlogdatapb.MinimalTable if h.trackSchemaVersions && len(h.schemas) > 0 { t = h.getTableFromHistoryForPos(tableName, pos) } @@ -244,12 +242,11 @@ func (h *HistorianSvc) sortSchemas() { } // getTableFromHistoryForPos looks in the cache for a schema for a specific gtid -func (h *HistorianSvc) getTableFromHistoryForPos(tableName sqlparser.TableIdent, pos mysql.Position) *binlogdata.MinimalTable { +func (h *HistorianSvc) getTableFromHistoryForPos(tableName sqlparser.TableIdent, pos mysql.Position) *binlogdatapb.MinimalTable { idx := sort.Search(len(h.schemas), func(i int) bool { - log.Infof("idx %d pos %s checking pos %s equality %t less %t", i, mysql.EncodePosition(pos), mysql.EncodePosition(h.schemas[i].pos), pos.Equal(h.schemas[i].pos), !pos.AtLeast(h.schemas[i].pos)) + // log.Infof("idx %d pos %s checking pos %s equality %t less %t", i, mysql.EncodePosition(pos), mysql.EncodePosition(h.schemas[i].pos), pos.Equal(h.schemas[i].pos), !pos.AtLeast(h.schemas[i].pos)) return pos.Equal(h.schemas[i].pos) || !pos.AtLeast(h.schemas[i].pos) }) - log.Infof("Index %d: len schemas %d", idx, len(h.schemas)) if idx >= len(h.schemas) || idx == 0 && !pos.Equal(h.schemas[idx].pos) { // beyond the range of the cache log.Infof("Not found schema in cache for %s with pos %s", tableName, pos) return nil diff --git a/go/vt/vttablet/tabletserver/schema/tracker.go b/go/vt/vttablet/tabletserver/schema/tracker.go index a347a17af6e..e830ada4b66 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker.go +++ b/go/vt/vttablet/tabletserver/schema/tracker.go @@ -62,7 +62,6 @@ func NewTracker(engine trackerEngine) *Tracker { // SchemaUpdated is called by a vstream when it encounters a DDL func (t *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) error { - if gtid == "" || ddl == "" { return fmt.Errorf("got invalid gtid or ddl in SchemaUpdated") } diff --git a/go/vt/vttablet/tabletserver/vstreamer/main_test.go b/go/vt/vttablet/tabletserver/vstreamer/main_test.go index 3142d4c1547..78ff6656031 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/main_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/main_test.go @@ -17,11 +17,15 @@ limitations under the License. package vstreamer import ( + "context" "flag" "fmt" "os" "testing" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + "vitess.io/vitess/go/vt/sqlparser" + "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/dbconfigs" @@ -79,3 +83,37 @@ func customEngine(t *testing.T, modifier func(mysql.ConnParams) mysql.ConnParams engine.Open(env.KeyspaceName, env.Cells[0]) return engine } + +type mockHistorian struct { + he schema.HistoryEngine +} + +func (h *mockHistorian) SetTrackSchemaVersions(val bool) {} + +func (h *mockHistorian) Init(tabletType tabletpb.TabletType) error { + return nil +} + +func (h *mockHistorian) Open() error { + return nil +} + +func (h *mockHistorian) Reload(ctx context.Context) error { + return nil +} + +func newMockHistorian(he schema.HistoryEngine) *mockHistorian { + sh := mockHistorian{he: he} + return &sh +} + +func (h *mockHistorian) GetTableForPos(tableName sqlparser.TableIdent, pos string) *binlogdatapb.MinimalTable { + return nil +} + +func (h *mockHistorian) RegisterVersionEvent() error { + numVersionEventsReceived++ + return nil +} + +var _ schema.Historian = (*mockHistorian)(nil) diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index e2d601322e2..8f99835e7f8 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -17,6 +17,7 @@ limitations under the License. package vstreamer import ( + "context" "fmt" "strconv" "strings" @@ -25,11 +26,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "golang.org/x/net/context" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/log" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" - "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" @@ -42,42 +40,6 @@ type testcase struct { var numVersionEventsReceived int -type mockHistorian struct { - he schema.HistoryEngine -} - -func (h *mockHistorian) SetTrackSchemaVersions(val bool) { - -} - -func (h *mockHistorian) Init(tabletType topodatapb.TabletType) error { - return nil -} - -func (h *mockHistorian) Open() error { - return nil -} - -func (h *mockHistorian) Reload(ctx context.Context) error { - return nil -} - -func newMockHistorian(he schema.HistoryEngine) *mockHistorian { - sh := mockHistorian{he: he} - return &sh -} - -func (h *mockHistorian) GetTableForPos(tableName sqlparser.TableIdent, pos string) *binlogdatapb.MinimalTable { - return nil -} - -func (h *mockHistorian) RegisterVersionEvent() error { - numVersionEventsReceived++ - return nil -} - -var _ schema.Historian = (*mockHistorian)(nil) - func TestVersion(t *testing.T) { if testing.Short() { t.Skip() @@ -89,9 +51,7 @@ func TestVersion(t *testing.T) { }() mh := newMockHistorian(env.SchemaEngine) - engine = NewEngine(engine.env, env.SrvTopo, mh) - engine.Open(env.KeyspaceName, env.Cells[0]) defer engine.Close() From cdd3f600090085289bd9c6aa97c6ce188e40a36c Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Tue, 19 May 2020 21:34:47 +0200 Subject: [PATCH 14/17] Incorporate review comments Signed-off-by: Rohit Nayak --- go/vt/vttablet/endtoend/vstreamer_test.go | 2 +- .../vttablet/tabletserver/schema/historian.go | 62 +++++++++++-------- .../tabletserver/schema/historian_test.go | 4 +- go/vt/vttablet/tabletserver/schema/tracker.go | 25 +++++--- .../tabletserver/schema/tracker_test.go | 4 +- go/vt/vttablet/tabletserver/tabletserver.go | 30 +++++---- .../tabletserver/vstreamer/main_test.go | 10 ++- 7 files changed, 80 insertions(+), 57 deletions(-) diff --git a/go/vt/vttablet/endtoend/vstreamer_test.go b/go/vt/vttablet/endtoend/vstreamer_test.go index 240cb777222..5d84b50c7ad 100644 --- a/go/vt/vttablet/endtoend/vstreamer_test.go +++ b/go/vt/vttablet/endtoend/vstreamer_test.go @@ -43,7 +43,7 @@ type test struct { func TestSchemaVersioning(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) - + defer cancel() tsv := framework.Server origWatchReplication := tsv.Config().WatchReplication tsv.Config().WatchReplication = true diff --git a/go/vt/vttablet/tabletserver/schema/historian.go b/go/vt/vttablet/tabletserver/schema/historian.go index b3733d795fe..4c868e1de6b 100644 --- a/go/vt/vttablet/tabletserver/schema/historian.go +++ b/go/vt/vttablet/tabletserver/schema/historian.go @@ -28,7 +28,6 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/log" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -44,7 +43,7 @@ type Historian interface { Reload(ctx context.Context) error GetTableForPos(tableName sqlparser.TableIdent, pos string) *binlogdatapb.MinimalTable Open() error - Init(tabletType topodatapb.TabletType) error + Close() SetTrackSchemaVersions(val bool) } @@ -56,6 +55,7 @@ var _ HistoryEngine = (*Engine)(nil) // information from the database type HistoryEngine interface { Open() error + Close() Reload(ctx context.Context) error GetConnection(ctx context.Context) (*connpool.DBConn, error) GetSchema() map[string]*Table @@ -77,25 +77,30 @@ type HistorianSvc struct { he HistoryEngine lastID int64 schemas []*TrackedSchema - isMaster bool mu sync.Mutex trackSchemaVersions bool lastSchema map[string]*binlogdatapb.MinimalTable + isOpen bool } // Reload gets the latest schema from the database func (h *HistorianSvc) Reload(ctx context.Context) error { + h.mu.Lock() + defer h.mu.Unlock() return h.reload(ctx) } // NewHistorian creates a new historian. It expects a schema.Engine instance func NewHistorian(he HistoryEngine) *HistorianSvc { - sh := HistorianSvc{he: he, lastID: 0} + sh := HistorianSvc{he: he, lastID: 0, trackSchemaVersions: true} return &sh } // SetTrackSchemaVersions can be used to turn on/off the use of the schema_version history in the historian +// Only used for testing func (h *HistorianSvc) SetTrackSchemaVersions(val bool) { + h.mu.Lock() + defer h.mu.Unlock() h.trackSchemaVersions = val } @@ -129,8 +134,6 @@ func (h *HistorianSvc) readRow(row []sqltypes.Value) (*TrackedSchema, int64, err // loadFromDB loads all rows from the schema_version table that the historian does not have as yet func (h *HistorianSvc) loadFromDB(ctx context.Context) error { - h.mu.Lock() - defer h.mu.Unlock() conn, err := h.he.GetConnection(ctx) if err != nil { return err @@ -139,8 +142,8 @@ func (h *HistorianSvc) loadFromDB(ctx context.Context) error { log.Infof("In loadFromDb for lastID %d", h.lastID) tableData, err := conn.Exec(ctx, fmt.Sprintf(getSchemaVersions, h.lastID), 10000, true) if err != nil { - log.Errorf("Error reading schema_tracking table %v", err) - return err + log.Infof("Error reading schema_tracking table %v", err) + return nil } log.Infof("Received rows from schema_version: %v", len(tableData.Rows)) if len(tableData.Rows) > 0 { @@ -157,27 +160,34 @@ func (h *HistorianSvc) loadFromDB(ctx context.Context) error { return nil } -// Init needs to be called once the tablet's type is known. It also warms the cache from the db -func (h *HistorianSvc) Init(tabletType topodatapb.TabletType) error { - h.Open() - ctx := tabletenv.LocalContext() - if !h.trackSchemaVersions { +// Open opens the underlying schema Engine. Called directly by a user purely interested in schema.Engine functionality +func (h *HistorianSvc) Open() error { + h.mu.Lock() + defer h.mu.Unlock() + if h.isOpen { return nil } - h.isMaster = tabletType == topodatapb.TabletType_MASTER - if h.isMaster { - h.Reload(ctx) - if err := h.loadFromDB(ctx); err != nil { - return err - } + h.he.Open() + ctx := tabletenv.LocalContext() + h.reload(ctx) + if err := h.loadFromDB(ctx); err != nil { + return err } + h.isOpen = true return nil } -// Open opens the underlying schema Engine. Called directly by a user purely interested in schema.Engine functionality -// Other users like tabletserver and historian tests call Init -func (h *HistorianSvc) Open() error { - return h.he.Open() +// Close closes the underlying schema engine and empties the version cache +func (h *HistorianSvc) Close() { + h.mu.Lock() + defer h.mu.Unlock() + + if !h.isOpen { + return + } + h.schemas = nil + h.he.Close() + h.isOpen = false } // reload gets the latest schema and replaces the latest copy of the schema maintained by the historian @@ -200,14 +210,14 @@ func (h *HistorianSvc) reload(ctx context.Context) error { table.PKColumns = pkc tables[table.Name] = table } - h.mu.Lock() - defer h.mu.Unlock() h.lastSchema = tables return nil } // GetTableForPos returns a best-effort schema for a specific gtid func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid string) *binlogdatapb.MinimalTable { + h.mu.Lock() + defer h.mu.Unlock() log.Infof("GetTableForPos called for %s with pos %s", tableName, gtid) if gtid != "" { pos, err := mysql.DecodePosition(gtid) @@ -263,6 +273,8 @@ func (h *HistorianSvc) getTableFromHistoryForPos(tableName sqlparser.TableIdent, // RegisterVersionEvent is called by the vstream when it encounters a version event (an insert into _vt.schema_tracking) // It triggers the historian to load the newer rows from the database to update its cache func (h *HistorianSvc) RegisterVersionEvent() error { + h.mu.Lock() + defer h.mu.Unlock() if !h.trackSchemaVersions { return nil } diff --git a/go/vt/vttablet/tabletserver/schema/historian_test.go b/go/vt/vttablet/tabletserver/schema/historian_test.go index d9c68a80f16..ddddbe1a621 100644 --- a/go/vt/vttablet/tabletserver/schema/historian_test.go +++ b/go/vt/vttablet/tabletserver/schema/historian_test.go @@ -25,7 +25,6 @@ import ( "vitess.io/vitess/go/sqltypes" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" querypb "vitess.io/vitess/go/vt/proto/query" - tabletpb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/sqlparser" ) @@ -74,7 +73,7 @@ func TestHistorian(t *testing.T) { se, db, cancel := getTestSchemaEngine(t) defer cancel() h := NewHistorian(se) - h.Init(tabletpb.TabletType_MASTER) + h.Open() h.SetTrackSchemaVersions(false) require.Nil(t, h.RegisterVersionEvent()) @@ -86,7 +85,6 @@ func TestHistorian(t *testing.T) { require.Nil(t, h.GetTableForPos(sqlparser.NewTableIdent("t1"), gtid1)) require.Equal(t, fmt.Sprintf("%v", h.GetTableForPos(sqlparser.NewTableIdent("dual"), gtid1)), `name:"dual" `) h.SetTrackSchemaVersions(true) - require.Regexp(t, ".*not supported.*", h.RegisterVersionEvent()) require.Nil(t, h.GetTableForPos(sqlparser.NewTableIdent("t1"), gtid1)) var blob1 string diff --git a/go/vt/vttablet/tabletserver/schema/tracker.go b/go/vt/vttablet/tabletserver/schema/tracker.go index e830ada4b66..b251c09da50 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker.go +++ b/go/vt/vttablet/tabletserver/schema/tracker.go @@ -25,7 +25,6 @@ import ( "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/log" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" ) const createSchemaTrackingTable = `CREATE TABLE IF NOT EXISTS _vt.schema_version ( @@ -37,12 +36,6 @@ const createSchemaTrackingTable = `CREATE TABLE IF NOT EXISTS _vt.schema_version PRIMARY KEY (id) ) ENGINE=InnoDB` -type trackerEngine interface { - GetConnection(ctx context.Context) (*connpool.DBConn, error) - Reload(ctx context.Context) error - GetSchema() map[string]*Table -} - //Subscriber will get notified when the schema has been updated type Subscriber interface { SchemaUpdated(gtid string, ddl string, timestamp int64) error @@ -52,16 +45,30 @@ var _ Subscriber = (*Tracker)(nil) // Tracker implements Subscriber and persists versions into the ddb type Tracker struct { - engine trackerEngine + engine *Engine + enabled bool } // NewTracker creates a Tracker, needs an Open SchemaEngine (which implements the trackerEngine interface) -func NewTracker(engine trackerEngine) *Tracker { +func NewTracker(engine *Engine) *Tracker { return &Tracker{engine: engine} } +// Open enables the tracker functionality +func (t *Tracker) Open() { + t.enabled = true +} + +// Close disables the tracker functionality +func (t *Tracker) Close() { + t.enabled = false +} + // SchemaUpdated is called by a vstream when it encounters a DDL func (t *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) error { + if !t.enabled { + return nil + } if gtid == "" || ddl == "" { return fmt.Errorf("got invalid gtid or ddl in SchemaUpdated") } diff --git a/go/vt/vttablet/tabletserver/schema/tracker_test.go b/go/vt/vttablet/tabletserver/schema/tracker_test.go index 5409371866a..08631f0720a 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker_test.go +++ b/go/vt/vttablet/tabletserver/schema/tracker_test.go @@ -30,16 +30,16 @@ func TestTracker(t *testing.T) { defer cancel() tracker := NewTracker(se) + tracker.Open() + gtid1 := "MySQL56/7b04699f-f5e9-11e9-bf88-9cb6d089e1c3:1-10" ddl1 := "create table tracker_test (id int)" ts1 := int64(1427325876) var query string query = "CREATE TABLE IF NOT EXISTS _vt.schema_version.*" - require.Regexp(t, ".*is not supported.*", tracker.SchemaUpdated(gtid1, ddl1, ts1)) db.AddQueryPattern(query, &sqltypes.Result{}) query = fmt.Sprintf("insert into _vt.schema_version.*%s.*%s.*%d.*", gtid1, regexp.QuoteMeta(ddl1), ts1) - require.Regexp(t, ".*is not supported.*", tracker.SchemaUpdated(gtid1, ddl1, ts1)) db.AddQueryPattern(query, &sqltypes.Result{}) require.NoError(t, tracker.SchemaUpdated(gtid1, ddl1, ts1)) diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index ad72fccdf0a..deab11b9a4b 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -162,10 +162,12 @@ type TabletServer struct { // The following variables should only be accessed within // the context of a startRequest-endRequest. se *schema.Engine + sh schema.Historian qe *QueryEngine te *TxEngine hw *heartbeat.Writer hr *heartbeat.Reader + tracker *schema.Tracker watcher *ReplicationWatcher vstreamer *vstreamer.Engine messager *messager.Engine @@ -193,7 +195,6 @@ type TabletServer struct { // alias is used for identifying this tabletserver in healthcheck responses. alias topodatapb.TabletAlias - sh schema.Historian } // RegisterFunctions is a list of all the @@ -239,9 +240,9 @@ func NewTabletServer(name string, config *tabletenv.TabletConfig, topoServer *to tsv.txThrottler = txthrottler.NewTxThrottler(tsv.config, topoServer) tsOnce.Do(func() { srvTopoServer = srvtopo.NewResilientServer(topoServer, "TabletSrvTopo") }) tsv.vstreamer = vstreamer.NewEngine(tsv, srvTopoServer, tsv.sh) - tsv.StartTracker() + tsv.tracker = schema.NewTracker(tsv.se) + tsv.watcher = NewReplicationWatcher(tsv, tsv.vstreamer, tsv.config, tsv.tracker) tsv.messager = messager.NewEngine(tsv, tsv.se, tsv.vstreamer) - tsv.exporter.NewGaugeFunc("TabletState", "Tablet server state", func() int64 { tsv.mu.Lock() defer tsv.mu.Unlock() @@ -263,14 +264,16 @@ func NewTabletServer(name string, config *tabletenv.TabletConfig, topoServer *to return tsv } -// StartTracker() starts a new replication watcher -// Exporting it allows it to be called separately in endtoend tests +// StartTracker starts a new replication watcher +// Only to be used for testing func (tsv *TabletServer) StartTracker() { schemaTracker := schema.NewTracker(tsv.se) tsv.watcher = NewReplicationWatcher(tsv, tsv.vstreamer, tsv.config, schemaTracker) tsv.watcher.Open() } +// StopTracker turns the watcher off +// Only to be used for testing func (tsv *TabletServer) StopTracker() { tsv.watcher.Close() } @@ -424,7 +427,6 @@ func (tsv *TabletServer) StartService(target querypb.Target, dbcfgs *dbconfigs.D return err } _ /* state changed */, err = tsv.SetServingType(tabletType, true, nil) - tsv.sh.Init(tabletType) return err } @@ -533,8 +535,8 @@ func (tsv *TabletServer) fullStart() (err error) { } c.Close() - if err := tsv.se.Open(); err != nil { - log.Errorf("Could not load schema, but starting the query service anyways: %v", err) + if err := tsv.sh.Open(); err != nil { + log.Errorf("Could not load historian, but starting the query service anyways: %v", err) } if err := tsv.qe.Open(); err != nil { return err @@ -564,12 +566,17 @@ func (tsv *TabletServer) serveNewType() (err error) { tsv.messager.Open() tsv.hr.Close() tsv.hw.Open() + tsv.tracker.Open() + if tsv.config.TrackSchemaVersions { + tsv.watcher.Open() + } } else { tsv.txController.AcceptReadOnly() tsv.messager.Close() tsv.hr.Open() tsv.hw.Close() tsv.watcher.Open() + tsv.tracker.Close() // Reset the sequences. tsv.se.MakeNonMaster() @@ -1702,9 +1709,10 @@ func (tsv *TabletServer) BroadcastHealth(terTimestamp int64, stats *querypb.Real target := tsv.target tsv.mu.Unlock() shr := &querypb.StreamHealthResponse{ - Target: &target, - TabletAlias: &tsv.alias, - Serving: tsv.IsServing(), + Target: &target, + TabletAlias: &tsv.alias, + Serving: tsv.IsServing(), + TabletExternallyReparentedTimestamp: terTimestamp, RealtimeStats: stats, } diff --git a/go/vt/vttablet/tabletserver/vstreamer/main_test.go b/go/vt/vttablet/tabletserver/vstreamer/main_test.go index 78ff6656031..6496e32e650 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/main_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/main_test.go @@ -29,7 +29,6 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/vt/dbconfigs" - tabletpb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer/testenv" @@ -60,7 +59,7 @@ func TestMain(m *testing.M) { // engine cannot be initialized in testenv because it introduces // circular dependencies historian = schema.NewHistorian(env.SchemaEngine) - historian.Init(tabletpb.TabletType_MASTER) + historian.Open() engine = NewEngine(env.TabletEnv, env.SrvTopo, historian) engine.Open(env.KeyspaceName, env.Cells[0]) defer engine.Close() @@ -77,7 +76,7 @@ func customEngine(t *testing.T, modifier func(mysql.ConnParams) mysql.ConnParams config := env.TabletEnv.Config().Clone() config.DB = dbconfigs.NewTestDBConfigs(modified, modified, modified.DbName) historian = schema.NewHistorian(env.SchemaEngine) - historian.Init(tabletpb.TabletType_MASTER) + historian.Open() engine := NewEngine(tabletenv.NewEnv(config, "VStreamerTest"), env.SrvTopo, historian) engine.Open(env.KeyspaceName, env.Cells[0]) @@ -90,12 +89,11 @@ type mockHistorian struct { func (h *mockHistorian) SetTrackSchemaVersions(val bool) {} -func (h *mockHistorian) Init(tabletType tabletpb.TabletType) error { +func (h *mockHistorian) Open() error { return nil } -func (h *mockHistorian) Open() error { - return nil +func (h *mockHistorian) Close() { } func (h *mockHistorian) Reload(ctx context.Context) error { From 23460e31179d21935fde4dd0b3b7adb2a61cf3d8 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Thu, 21 May 2020 12:16:14 +0200 Subject: [PATCH 15/17] Fixed e2e test Signed-off-by: Rohit Nayak --- go/vt/tableacl/tableacl.go | 2 +- go/vt/vttablet/endtoend/vstreamer_test.go | 6 ------ go/vt/vttablet/tabletserver/schema/tracker.go | 4 ++++ go/vt/vttablet/tabletserver/tabletserver.go | 13 ++++++++++--- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/go/vt/tableacl/tableacl.go b/go/vt/tableacl/tableacl.go index 3d8305868a7..9a21dcd3d6a 100644 --- a/go/vt/tableacl/tableacl.go +++ b/go/vt/tableacl/tableacl.go @@ -117,7 +117,7 @@ func (tacl *tableACL) init(configFile string, aclCB func()) error { } config := &tableaclpb.Config{} if err := proto.Unmarshal(data, config); err != nil { - log.Infof("unable to parse tableACL config file as a protobuf file: %v", err) + log.Infof("unable to parse tableACL config file as a protobuf file: %v, will try as json", err) // try to parse tableacl as json file if jsonErr := json2.Unmarshal(data, config); jsonErr != nil { log.Infof("unable to parse tableACL config file as a json file: %v", jsonErr) diff --git a/go/vt/vttablet/endtoend/vstreamer_test.go b/go/vt/vttablet/endtoend/vstreamer_test.go index 5d84b50c7ad..3f2e85f7443 100644 --- a/go/vt/vttablet/endtoend/vstreamer_test.go +++ b/go/vt/vttablet/endtoend/vstreamer_test.go @@ -45,12 +45,6 @@ func TestSchemaVersioning(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() tsv := framework.Server - origWatchReplication := tsv.Config().WatchReplication - tsv.Config().WatchReplication = true - defer func() { - tsv.Config().WatchReplication = origWatchReplication - - }() tsv.Historian().SetTrackSchemaVersions(true) tsv.StartTracker() srvTopo := srvtopo.NewResilientServer(framework.TopoServer, "SchemaVersionE2ETestTopo") diff --git a/go/vt/vttablet/tabletserver/schema/tracker.go b/go/vt/vttablet/tabletserver/schema/tracker.go index b251c09da50..2e7a082460b 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker.go +++ b/go/vt/vttablet/tabletserver/schema/tracker.go @@ -56,19 +56,23 @@ func NewTracker(engine *Engine) *Tracker { // Open enables the tracker functionality func (t *Tracker) Open() { + log.Info("Enabling tracker") t.enabled = true } // Close disables the tracker functionality func (t *Tracker) Close() { + log.Info("Disabling tracker") t.enabled = false } // SchemaUpdated is called by a vstream when it encounters a DDL func (t *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) error { if !t.enabled { + log.Infof("Tracker not enabled, ignoring SchemaUpdated event") return nil } + log.Infof("Processing SchemaUpdated event for gtid %s, ddl %s", gtid, ddl) if gtid == "" || ddl == "" { return fmt.Errorf("got invalid gtid or ddl in SchemaUpdated") } diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index deab11b9a4b..35c8580b563 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -267,15 +267,20 @@ func NewTabletServer(name string, config *tabletenv.TabletConfig, topoServer *to // StartTracker starts a new replication watcher // Only to be used for testing func (tsv *TabletServer) StartTracker() { - schemaTracker := schema.NewTracker(tsv.se) - tsv.watcher = NewReplicationWatcher(tsv, tsv.vstreamer, tsv.config, schemaTracker) + tsv.config.TrackSchemaVersions = true + tsv.config.WatchReplication = true + tsv.tracker.Open() + //need to close and reopen watcher since it is already opened in the tsv init and is idempotent ... + tsv.watcher.Close() + tsv.watcher.watchReplication = true tsv.watcher.Open() } // StopTracker turns the watcher off // Only to be used for testing func (tsv *TabletServer) StopTracker() { - tsv.watcher.Close() + tsv.config.TrackSchemaVersions = false + tsv.tracker.Close() } // Register prepares TabletServer for serving by calling @@ -566,8 +571,10 @@ func (tsv *TabletServer) serveNewType() (err error) { tsv.messager.Open() tsv.hr.Close() tsv.hw.Open() + log.Info("Opening tracker, trackschemaversions is %t", tsv.config.TrackSchemaVersions) tsv.tracker.Open() if tsv.config.TrackSchemaVersions { + log.Info("Starting watcher") tsv.watcher.Open() } } else { From 84fb821bcf5469c0b1efc0be02ee77cf4811e5c5 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Thu, 28 May 2020 10:58:07 +0200 Subject: [PATCH 16/17] Addressed review comments Signed-off-by: Rohit Nayak --- .../vttablet/tabletserver/schema/historian.go | 75 +++++++------------ go/vt/vttablet/tabletserver/schema/tracker.go | 5 +- .../vttablet/tabletserver/tabletenv/config.go | 2 +- go/vt/vttablet/tabletserver/tabletserver.go | 1 + .../tabletserver/vstreamer/main_test.go | 6 +- 5 files changed, 34 insertions(+), 55 deletions(-) diff --git a/go/vt/vttablet/tabletserver/schema/historian.go b/go/vt/vttablet/tabletserver/schema/historian.go index 4c868e1de6b..c107f7a735d 100644 --- a/go/vt/vttablet/tabletserver/schema/historian.go +++ b/go/vt/vttablet/tabletserver/schema/historian.go @@ -29,7 +29,6 @@ import ( "vitess.io/vitess/go/vt/log" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" "vitess.io/vitess/go/vt/vtgate/evalengine" - "vitess.io/vitess/go/vt/vttablet/tabletserver/connpool" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/sqlparser" @@ -37,6 +36,9 @@ import ( const getSchemaVersions = "select id, pos, ddl, time_updated, schemax from _vt.schema_version where id > %d order by id asc" +// vl defines the glog verbosity level for the package +const vl = 10 + // Historian defines the interface to reload a db schema or get the schema of a table for a given position type Historian interface { RegisterVersionEvent() error @@ -47,20 +49,6 @@ type Historian interface { SetTrackSchemaVersions(val bool) } -// HistoryEngine exposes only those schema.Engine APIs that are needed by the HistorianSvc -// We define an interface to facilitate a mock for testing -var _ HistoryEngine = (*Engine)(nil) - -// HistoryEngine defines the interface which is implemented by schema.Engine for the historian to get current schema -// information from the database -type HistoryEngine interface { - Open() error - Close() - Reload(ctx context.Context) error - GetConnection(ctx context.Context) (*connpool.DBConn, error) - GetSchema() map[string]*Table -} - // TrackedSchema has the snapshot of the table at a given pos (reached by ddl) type TrackedSchema struct { schema map[string]*binlogdatapb.MinimalTable @@ -74,7 +62,7 @@ var _ Historian = (*HistorianSvc)(nil) // and supplying a schema for a specific version by loading the cached values from the schema_version table // The schema version table is populated by the Tracker type HistorianSvc struct { - he HistoryEngine + se *Engine lastID int64 schemas []*TrackedSchema mu sync.Mutex @@ -91,8 +79,8 @@ func (h *HistorianSvc) Reload(ctx context.Context) error { } // NewHistorian creates a new historian. It expects a schema.Engine instance -func NewHistorian(he HistoryEngine) *HistorianSvc { - sh := HistorianSvc{he: he, lastID: 0, trackSchemaVersions: true} +func NewHistorian(se *Engine) *HistorianSvc { + sh := HistorianSvc{se: se, lastID: 0, trackSchemaVersions: true} return &sh } @@ -112,12 +100,15 @@ func (h *HistorianSvc) readRow(row []sqltypes.Value) (*TrackedSchema, int64, err return nil, 0, err } ddl := string(row[2].ToBytes()) - timeUpdated, _ := evalengine.ToInt64(row[3]) + timeUpdated, err := evalengine.ToInt64(row[3]) + if err != nil { + return nil, 0, err + } sch := &binlogdatapb.MinimalSchema{} if err := proto.Unmarshal(row[4].ToBytes(), sch); err != nil { return nil, 0, err } - log.Infof("Read tracked schema from db: id %d, pos %v, ddl %s, schema len %d, time_updated %d \n", + log.V(vl).Infof("Read tracked schema from db: id %d, pos %v, ddl %s, schema len %d, time_updated %d \n", id, mysql.EncodePosition(pos), ddl, len(sch.Tables), timeUpdated) tables := map[string]*binlogdatapb.MinimalTable{} @@ -133,30 +124,27 @@ func (h *HistorianSvc) readRow(row []sqltypes.Value) (*TrackedSchema, int64, err } // loadFromDB loads all rows from the schema_version table that the historian does not have as yet +// caller should have locked h.mu func (h *HistorianSvc) loadFromDB(ctx context.Context) error { - conn, err := h.he.GetConnection(ctx) + conn, err := h.se.GetConnection(ctx) if err != nil { return err } defer conn.Recycle() - log.Infof("In loadFromDb for lastID %d", h.lastID) tableData, err := conn.Exec(ctx, fmt.Sprintf(getSchemaVersions, h.lastID), 10000, true) if err != nil { - log.Infof("Error reading schema_tracking table %v", err) + log.Infof("Error reading schema_tracking table %v, will operate with the latest available schema", err) return nil } - log.Infof("Received rows from schema_version: %v", len(tableData.Rows)) - if len(tableData.Rows) > 0 { - for _, row := range tableData.Rows { - trackedSchema, id, err := h.readRow(row) - if err != nil { - return err - } - h.schemas = append(h.schemas, trackedSchema) - h.lastID = id + for _, row := range tableData.Rows { + trackedSchema, id, err := h.readRow(row) + if err != nil { + return err } - h.sortSchemas() + h.schemas = append(h.schemas, trackedSchema) + h.lastID = id } + h.sortSchemas() return nil } @@ -167,7 +155,7 @@ func (h *HistorianSvc) Open() error { if h.isOpen { return nil } - h.he.Open() + h.se.Open() ctx := tabletenv.LocalContext() h.reload(ctx) if err := h.loadFromDB(ctx); err != nil { @@ -186,19 +174,17 @@ func (h *HistorianSvc) Close() { return } h.schemas = nil - h.he.Close() + h.se.Close() h.isOpen = false } // reload gets the latest schema and replaces the latest copy of the schema maintained by the historian func (h *HistorianSvc) reload(ctx context.Context) error { - log.Info("In historian reload") - if err := h.he.Reload(ctx); err != nil { + if err := h.se.Reload(ctx); err != nil { return err } tables := map[string]*binlogdatapb.MinimalTable{} - log.Infof("Schema returned by engine has %d tables", len(h.he.GetSchema())) - for _, t := range h.he.GetSchema() { + for _, t := range h.se.GetSchema() { table := &binlogdatapb.MinimalTable{ Name: t.Name.String(), Fields: t.Fields, @@ -218,7 +204,7 @@ func (h *HistorianSvc) reload(ctx context.Context) error { func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid string) *binlogdatapb.MinimalTable { h.mu.Lock() defer h.mu.Unlock() - log.Infof("GetTableForPos called for %s with pos %s", tableName, gtid) + log.V(vl).Infof("GetTableForPos called for %s with pos %s", tableName, gtid) if gtid != "" { pos, err := mysql.DecodePosition(gtid) if err != nil { @@ -230,7 +216,7 @@ func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid strin t = h.getTableFromHistoryForPos(tableName, pos) } if t != nil { - log.Infof("Returning table %s from history for pos %s, schema %s", tableName, gtid, t) + log.V(vl).Infof("Returning table %s from history for pos %s, schema %s", tableName, gtid, t) return t } } @@ -238,23 +224,20 @@ func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid strin if h.lastSchema == nil { return nil } - log.Infof("Returning table %s from latest schema for pos %s, schema %s", tableName, gtid, h.lastSchema[tableName.String()]) + log.V(vl).Infof("Returning table %s from latest schema for pos %s, schema %s", tableName, gtid, h.lastSchema[tableName.String()]) return h.lastSchema[tableName.String()] } // sortSchemas sorts entries in ascending order of gtid, ex: 40,44,48 func (h *HistorianSvc) sortSchemas() { - log.Infof("In sortSchemas with len %d", len(h.schemas)) sort.Slice(h.schemas, func(i int, j int) bool { return h.schemas[j].pos.AtLeast(h.schemas[i].pos) }) - log.Infof("Ending sortSchemas with len %d", len(h.schemas)) } // getTableFromHistoryForPos looks in the cache for a schema for a specific gtid func (h *HistorianSvc) getTableFromHistoryForPos(tableName sqlparser.TableIdent, pos mysql.Position) *binlogdatapb.MinimalTable { idx := sort.Search(len(h.schemas), func(i int) bool { - // log.Infof("idx %d pos %s checking pos %s equality %t less %t", i, mysql.EncodePosition(pos), mysql.EncodePosition(h.schemas[i].pos), pos.Equal(h.schemas[i].pos), !pos.AtLeast(h.schemas[i].pos)) return pos.Equal(h.schemas[i].pos) || !pos.AtLeast(h.schemas[i].pos) }) if idx >= len(h.schemas) || idx == 0 && !pos.Equal(h.schemas[idx].pos) { // beyond the range of the cache @@ -262,11 +245,9 @@ func (h *HistorianSvc) getTableFromHistoryForPos(tableName sqlparser.TableIdent, return nil } if pos.Equal(h.schemas[idx].pos) { //exact match to a cache entry - log.Infof("Found tracked schema for pos %s, ddl %s", pos, h.schemas[idx].ddl) return h.schemas[idx].schema[tableName.String()] } //not an exact match, so based on our sort algo idx is one less than found: from 40,44,48 : 43 < 44 but we want 40 - log.Infof("Found tracked schema for pos %s, ddl %s", pos, h.schemas[idx-1].ddl) return h.schemas[idx-1].schema[tableName.String()] } diff --git a/go/vt/vttablet/tabletserver/schema/tracker.go b/go/vt/vttablet/tabletserver/schema/tracker.go index 2e7a082460b..08362bcdaaa 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker.go +++ b/go/vt/vttablet/tabletserver/schema/tracker.go @@ -56,13 +56,11 @@ func NewTracker(engine *Engine) *Tracker { // Open enables the tracker functionality func (t *Tracker) Open() { - log.Info("Enabling tracker") t.enabled = true } // Close disables the tracker functionality func (t *Tracker) Close() { - log.Info("Disabling tracker") t.enabled = false } @@ -72,7 +70,7 @@ func (t *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) error log.Infof("Tracker not enabled, ignoring SchemaUpdated event") return nil } - log.Infof("Processing SchemaUpdated event for gtid %s, ddl %s", gtid, ddl) + log.V(vl).Infof("Processing SchemaUpdated event for gtid %s, ddl %s", gtid, ddl) if gtid == "" || ddl == "" { return fmt.Errorf("got invalid gtid or ddl in SchemaUpdated") } @@ -105,7 +103,6 @@ func (t *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) error if err != nil { return err } - log.Infof("Inserting version for position %s: %s : %+v", gtid, ddl, len(dbSchema.Tables)) 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) diff --git a/go/vt/vttablet/tabletserver/tabletenv/config.go b/go/vt/vttablet/tabletserver/tabletenv/config.go index 1c3eb81c5a6..359eada15c9 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/config.go +++ b/go/vt/vttablet/tabletserver/tabletenv/config.go @@ -109,7 +109,7 @@ func init() { flag.BoolVar(¤tConfig.TerseErrors, "queryserver-config-terse-errors", defaultConfig.TerseErrors, "prevent bind vars from escaping in returned errors") flag.StringVar(&deprecatedPoolNamePrefix, "pool-name-prefix", "", "Deprecated") flag.BoolVar(¤tConfig.WatchReplication, "watch_replication_stream", false, "When enabled, vttablet will stream the MySQL replication stream from the local server, and use it to support the include_event_token ExecuteOptions.") - flag.BoolVar(¤tConfig.TrackSchemaVersions, "track_schema_versions", false, "When enabled, vttablet will store versions of schemas at each position that a DDL is applied and allow retrieval of the schema corresponding to a position") + flag.BoolVar(¤tConfig.TrackSchemaVersions, "track_schema_versions", true, "When enabled, vttablet will store versions of schemas at each position that a DDL is applied and allow retrieval of the schema corresponding to a position") flag.BoolVar(&deprecatedAutocommit, "enable-autocommit", true, "This flag is deprecated. Autocommit is always allowed.") flag.BoolVar(¤tConfig.TwoPCEnable, "twopc_enable", defaultConfig.TwoPCEnable, "if the flag is on, 2pc is enabled. Other 2pc flags must be supplied.") flag.StringVar(¤tConfig.TwoPCCoordinatorAddress, "twopc_coordinator_address", defaultConfig.TwoPCCoordinatorAddress, "address of the (VTGate) process(es) that will be used to notify of abandoned transactions.") diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 35c8580b563..beaaff08889 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -540,6 +540,7 @@ func (tsv *TabletServer) fullStart() (err error) { } c.Close() + // sh opens and closes se if err := tsv.sh.Open(); err != nil { log.Errorf("Could not load historian, but starting the query service anyways: %v", err) } diff --git a/go/vt/vttablet/tabletserver/vstreamer/main_test.go b/go/vt/vttablet/tabletserver/vstreamer/main_test.go index 6496e32e650..642ee1fbc08 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/main_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/main_test.go @@ -84,7 +84,7 @@ func customEngine(t *testing.T, modifier func(mysql.ConnParams) mysql.ConnParams } type mockHistorian struct { - he schema.HistoryEngine + se *schema.Engine } func (h *mockHistorian) SetTrackSchemaVersions(val bool) {} @@ -100,8 +100,8 @@ func (h *mockHistorian) Reload(ctx context.Context) error { return nil } -func newMockHistorian(he schema.HistoryEngine) *mockHistorian { - sh := mockHistorian{he: he} +func newMockHistorian(se *schema.Engine) *mockHistorian { + sh := mockHistorian{se: se} return &sh } From 81512fae8518507f3c4132360a1552aa6f33e9a8 Mon Sep 17 00:00:00 2001 From: Rohit Nayak Date: Sun, 31 May 2020 00:09:09 +0200 Subject: [PATCH 17/17] Historian now subscribes to schema engine. Added ReloadAt in SE/VS to optimize schema loads. Related tests. Fixed TestMessage race Signed-off-by: Rohit Nayak --- go.mod | 1 + go.sum | 2 + go/test/endtoend/messaging/message_test.go | 2 + go/vt/vttablet/endtoend/main_test.go | 7 ++ go/vt/vttablet/endtoend/vstreamer_test.go | 56 ++++++++++++++- .../vreplication/external_connector.go | 2 +- .../vreplication/framework_test.go | 2 +- go/vt/vttablet/tabletserver/schema/engine.go | 23 +++++- .../tabletserver/schema/engine_test.go | 45 +++++++++++- .../vttablet/tabletserver/schema/historian.go | 71 +++++++++++-------- go/vt/vttablet/tabletserver/schema/tracker.go | 5 +- go/vt/vttablet/tabletserver/tabletserver.go | 2 +- .../vttablet/tabletserver/vstreamer/engine.go | 8 ++- .../tabletserver/vstreamer/main_test.go | 6 +- .../vstreamer/resultstreamer_test.go | 2 +- .../vstreamer/rowstreamer_test.go | 26 ++++--- .../tabletserver/vstreamer/vstreamer.go | 8 ++- .../tabletserver/vstreamer/vstreamer_test.go | 46 ++++++------ 18 files changed, 234 insertions(+), 80 deletions(-) diff --git a/go.mod b/go.mod index 954a5c27d1f..3b69f9eb8d1 100644 --- a/go.mod +++ b/go.mod @@ -47,6 +47,7 @@ require ( github.com/krishicks/yaml-patch v0.0.10 github.com/magiconair/properties v1.8.1 github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1 + github.com/mitchellh/go-ps v1.0.0 // indirect github.com/mitchellh/go-testing-interface v1.14.0 // indirect github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5 github.com/onsi/ginkgo v1.10.3 // indirect diff --git a/go.sum b/go.sum index 1c489ca43dc..3915dd2de43 100644 --- a/go.sum +++ b/go.sum @@ -378,6 +378,8 @@ github.com/minio/minio-go v0.0.0-20190131015406-c8a261de75c1/go.mod h1:vuvdOZLJu github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/go-testing-interface v1.14.0 h1:/x0XQ6h+3U3nAyk1yx+bHPURrKa9sVVvYbuqZ7pIAtI= github.com/mitchellh/go-testing-interface v1.14.0/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= diff --git a/go/test/endtoend/messaging/message_test.go b/go/test/endtoend/messaging/message_test.go index 8de8c808186..0a2aa46e381 100644 --- a/go/test/endtoend/messaging/message_test.go +++ b/go/test/endtoend/messaging/message_test.go @@ -69,6 +69,8 @@ func TestMessage(t *testing.T) { exec(t, conn, fmt.Sprintf("use %s", lookupKeyspace)) exec(t, conn, createMessage) + clusterInstance.VtctlProcess.ExecuteCommand(fmt.Sprintf("ReloadSchemaKeyspace %s", lookupKeyspace)) + defer exec(t, conn, "drop table vitess_message") exec(t, streamConn, "set workload = 'olap'") diff --git a/go/vt/vttablet/endtoend/main_test.go b/go/vt/vttablet/endtoend/main_test.go index db494bbfa80..43de9592f12 100644 --- a/go/vt/vttablet/endtoend/main_test.go +++ b/go/vt/vttablet/endtoend/main_test.go @@ -282,6 +282,13 @@ var tableACLConfig = `{ "readers": ["dev"], "writers": ["dev"], "admins": ["dev"] + }, + { + "name": "historian_test1", + "table_names_or_prefixes": ["historian_test1"], + "readers": ["dev"], + "writers": ["dev"], + "admins": ["dev"] } ] }` diff --git a/go/vt/vttablet/endtoend/vstreamer_test.go b/go/vt/vttablet/endtoend/vstreamer_test.go index 3f2e85f7443..9528a4018de 100644 --- a/go/vt/vttablet/endtoend/vstreamer_test.go +++ b/go/vt/vttablet/endtoend/vstreamer_test.go @@ -22,9 +22,13 @@ import ( "errors" "fmt" "strings" + "sync" "testing" "time" + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/log" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" @@ -41,6 +45,56 @@ type test struct { output []string } +func TestHistorianSchemaUpdate(t *testing.T) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + tsv := framework.Server + historian := tsv.Historian() + srvTopo := srvtopo.NewResilientServer(framework.TopoServer, "SchemaVersionE2ETestTopo") + + vstreamer.NewEngine(tabletenv.NewEnv(tsv.Config(), "SchemaVersionE2ETest"), srvTopo, tsv.SchemaEngine(), historian) + target := &querypb.Target{ + Keyspace: "vttest", + Shard: "0", + TabletType: tabletpb.TabletType_MASTER, + Cell: "", + } + filter := &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "/.*/", + }}, + } + var createTableSQL = "create table historian_test1(id1 int)" + var mu sync.Mutex + mu.Lock() + send := func(events []*binlogdatapb.VEvent) error { + for _, ev := range events { + if ev.Type == binlogdatapb.VEventType_DDL && ev.Ddl == createTableSQL { + log.Info("Found DDL for table historian_test1") + mu.Unlock() + } + } + return nil + } + go func() { + if err := tsv.VStream(ctx, target, "current", filter, send); err != nil { + fmt.Printf("Error in tsv.VStream: %v", err) + t.Error(err) + } + }() + + require.Nil(t, historian.GetTableForPos(sqlparser.NewTableIdent("historian_test1"), "")) + require.NotNil(t, historian.GetTableForPos(sqlparser.NewTableIdent("vitess_test"), "")) + client := framework.NewClient() + client.Execute(createTableSQL, nil) + + mu.Lock() + minSchema := historian.GetTableForPos(sqlparser.NewTableIdent("historian_test1"), "") + want := `name:"historian_test1" fields: ` + require.Equal(t, fmt.Sprintf("%v", minSchema), want) + +} + func TestSchemaVersioning(t *testing.T) { ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -49,7 +103,7 @@ func TestSchemaVersioning(t *testing.T) { tsv.StartTracker() srvTopo := srvtopo.NewResilientServer(framework.TopoServer, "SchemaVersionE2ETestTopo") - vstreamer.NewEngine(tabletenv.NewEnv(tsv.Config(), "SchemaVersionE2ETest"), srvTopo, tsv.Historian()) + vstreamer.NewEngine(tabletenv.NewEnv(tsv.Config(), "SchemaVersionE2ETest"), srvTopo, tsv.SchemaEngine(), tsv.Historian()) target := &querypb.Target{ Keyspace: "vttest", Shard: "0", diff --git a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go index 8597254b05b..bbbca25688a 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go +++ b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go @@ -89,7 +89,7 @@ func (ec *externalConnector) Get(name string) (*mysqlConnector, error) { c.env = tabletenv.NewEnv(config, name) c.se = schema.NewEngine(c.env) sh := schema.NewHistorian(c.se) - c.vstreamer = vstreamer.NewEngine(c.env, nil, sh) + c.vstreamer = vstreamer.NewEngine(c.env, nil, c.se, sh) c.se.InitDBConfig(c.env.Config().DB.DbaWithDB()) // Open diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index 6ec25853ca5..e35481e7f75 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -90,7 +90,7 @@ func TestMain(m *testing.M) { // engines cannot be initialized in testenv because it introduces // circular dependencies. - streamerEngine = vstreamer.NewEngine(env.TabletEnv, env.SrvTopo, schema.NewHistorian(env.SchemaEngine)) + streamerEngine = vstreamer.NewEngine(env.TabletEnv, env.SrvTopo, env.SchemaEngine, schema.NewHistorian(env.SchemaEngine)) streamerEngine.Open(env.KeyspaceName, env.Cells[0]) defer streamerEngine.Close() diff --git a/go/vt/vttablet/tabletserver/schema/engine.go b/go/vt/vttablet/tabletserver/schema/engine.go index fc089c3b175..ec99ab48293 100644 --- a/go/vt/vttablet/tabletserver/schema/engine.go +++ b/go/vt/vttablet/tabletserver/schema/engine.go @@ -57,7 +57,9 @@ type Engine struct { tables map[string]*Table lastChange int64 reloadTime time.Duration - notifiers map[string]notifier + //the position at which the schema was last loaded. it is only used in conjunction with ReloadAt + reloadAtPos mysql.Position + notifiers map[string]notifier // The following fields have their own synchronization // and do not require locking mu. @@ -177,13 +179,29 @@ func (se *Engine) MakeNonMaster() { // Reload reloads the schema info from the db. // Any tables that have changed since the last load are updated. func (se *Engine) Reload(ctx context.Context) error { + return se.ReloadAt(ctx, mysql.Position{}) +} + +// ReloadAt reloads the schema info from the db. +// Any tables that have changed since the last load are updated. +// It maintains the position at which the schema was reloaded and if the same position is provided +// (say by multiple vstreams) it returns the cached schema. In case of a newer or empty pos it always reloads the schema +func (se *Engine) ReloadAt(ctx context.Context, pos mysql.Position) error { se.mu.Lock() defer se.mu.Unlock() if !se.isOpen { log.Warning("Schema reload called for an engine that is not yet open") return nil } - return se.reload(ctx) + if !pos.IsZero() && se.reloadAtPos.AtLeast(pos) { + log.V(2).Infof("ReloadAt: found cached schema at %s", mysql.EncodePosition(pos)) + return nil + } + if err := se.reload(ctx); err != nil { + return err + } + se.reloadAtPos = pos + return nil } // reload reloads the schema. It can also be used to initialize it. @@ -260,7 +278,6 @@ func (se *Engine) reload(ctx context.Context) error { se.tables[k] = t } se.lastChange = curTime - se.broadcast(created, altered, dropped) return nil } diff --git a/go/vt/vttablet/tabletserver/schema/engine_test.go b/go/vt/vttablet/tabletserver/schema/engine_test.go index 0d4a9caf297..eb3b95f17e1 100644 --- a/go/vt/vttablet/tabletserver/schema/engine_test.go +++ b/go/vt/vttablet/tabletserver/schema/engine_test.go @@ -132,7 +132,6 @@ func TestOpenAndReload(t *testing.T) { } } se.RegisterNotifier("test", notifier) - err := se.Reload(context.Background()) require.NoError(t, err) @@ -160,6 +159,50 @@ func TestOpenAndReload(t *testing.T) { } delete(want, "msg") assert.Equal(t, want, se.GetSchema()) + + //ReloadAt tests + pos1, err := mysql.DecodePosition("MariaDB/0-41983-20") + require.NoError(t, err) + pos2, err := mysql.DecodePosition("MariaDB/0-41983-40") + require.NoError(t, err) + se.UnregisterNotifier("test") + + err = se.ReloadAt(context.Background(), mysql.Position{}) + require.NoError(t, err) + assert.Equal(t, want, se.GetSchema()) + + err = se.ReloadAt(context.Background(), pos1) + require.NoError(t, err) + assert.Equal(t, want, se.GetSchema()) + + // delete table test_table_03 + db.AddQuery(mysql.BaseShowTables, &sqltypes.Result{ + Fields: mysql.BaseShowTablesFields, + Rows: [][]sqltypes.Value{ + mysql.BaseShowTablesRow("test_table_01", false, ""), + mysql.BaseShowTablesRow("test_table_02", false, ""), + // test_table_04 will in spite of older timestamp because it doesn't exist yet. + mysql.BaseShowTablesRow("test_table_04", false, ""), + mysql.BaseShowTablesRow("seq", false, "vitess_sequence"), + }, + }) + db.AddQuery(mysql.BaseShowPrimary, &sqltypes.Result{ + Fields: mysql.ShowPrimaryFields, + Rows: [][]sqltypes.Value{ + mysql.ShowPrimaryRow("test_table_01", "pk"), + mysql.ShowPrimaryRow("test_table_02", "pk"), + mysql.ShowPrimaryRow("test_table_04", "pk"), + mysql.ShowPrimaryRow("seq", "id"), + }, + }) + err = se.ReloadAt(context.Background(), pos1) + require.NoError(t, err) + assert.Equal(t, want, se.GetSchema()) + + delete(want, "test_table_03") + err = se.ReloadAt(context.Background(), pos2) + require.NoError(t, err) + assert.Equal(t, want, se.GetSchema()) } func TestOpenFailedDueToMissMySQLTime(t *testing.T) { diff --git a/go/vt/vttablet/tabletserver/schema/historian.go b/go/vt/vttablet/tabletserver/schema/historian.go index c107f7a735d..812b37a99a7 100644 --- a/go/vt/vttablet/tabletserver/schema/historian.go +++ b/go/vt/vttablet/tabletserver/schema/historian.go @@ -23,7 +23,6 @@ import ( "sync" "github.com/gogo/protobuf/proto" - "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/log" @@ -42,7 +41,6 @@ const vl = 10 // Historian defines the interface to reload a db schema or get the schema of a table for a given position type Historian interface { RegisterVersionEvent() error - Reload(ctx context.Context) error GetTableForPos(tableName sqlparser.TableIdent, pos string) *binlogdatapb.MinimalTable Open() error Close() @@ -67,17 +65,10 @@ type HistorianSvc struct { schemas []*TrackedSchema mu sync.Mutex trackSchemaVersions bool - lastSchema map[string]*binlogdatapb.MinimalTable + latestSchema map[string]*binlogdatapb.MinimalTable isOpen bool } -// Reload gets the latest schema from the database -func (h *HistorianSvc) Reload(ctx context.Context) error { - h.mu.Lock() - defer h.mu.Unlock() - return h.reload(ctx) -} - // NewHistorian creates a new historian. It expects a schema.Engine instance func NewHistorian(se *Engine) *HistorianSvc { sh := HistorianSvc{se: se, lastID: 0, trackSchemaVersions: true} @@ -155,12 +146,16 @@ func (h *HistorianSvc) Open() error { if h.isOpen { return nil } - h.se.Open() + if err := h.se.Open(); err != nil { + return err + } ctx := tabletenv.LocalContext() - h.reload(ctx) + h.reload() if err := h.loadFromDB(ctx); err != nil { return err } + h.se.RegisterNotifier("historian", h.schemaChanged) + h.isOpen = true return nil } @@ -174,17 +169,15 @@ func (h *HistorianSvc) Close() { return } h.schemas = nil + h.se.UnregisterNotifier("historian") h.se.Close() h.isOpen = false } -// reload gets the latest schema and replaces the latest copy of the schema maintained by the historian -func (h *HistorianSvc) reload(ctx context.Context) error { - if err := h.se.Reload(ctx); err != nil { - return err - } - tables := map[string]*binlogdatapb.MinimalTable{} - for _, t := range h.se.GetSchema() { +// convert from schema table representation to minimal tables and store as latestSchema +func (h *HistorianSvc) storeLatest(tables map[string]*Table) { + minTables := make(map[string]*binlogdatapb.MinimalTable) + for _, t := range tables { table := &binlogdatapb.MinimalTable{ Name: t.Name.String(), Fields: t.Fields, @@ -194,17 +187,33 @@ func (h *HistorianSvc) reload(ctx context.Context) error { pkc = append(pkc, int64(pk)) } table.PKColumns = pkc - tables[table.Name] = table + minTables[table.Name] = table } - h.lastSchema = tables - return nil + h.latestSchema = minTables +} + +// reload gets the latest schema and replaces the latest copy of the schema maintained by the historian +// caller should lock h.mu +func (h *HistorianSvc) reload() { + h.storeLatest(h.se.tables) +} + +// schema notifier callback +func (h *HistorianSvc) schemaChanged(tables map[string]*Table, _, _, _ []string) { + if !h.isOpen { + return + } + h.mu.Lock() + defer h.mu.Unlock() + h.storeLatest(tables) } // GetTableForPos returns a best-effort schema for a specific gtid func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid string) *binlogdatapb.MinimalTable { h.mu.Lock() defer h.mu.Unlock() - log.V(vl).Infof("GetTableForPos called for %s with pos %s", tableName, gtid) + + log.V(2).Infof("GetTableForPos called for %s with pos %s", tableName, gtid) if gtid != "" { pos, err := mysql.DecodePosition(gtid) if err != nil { @@ -216,16 +225,18 @@ func (h *HistorianSvc) GetTableForPos(tableName sqlparser.TableIdent, gtid strin t = h.getTableFromHistoryForPos(tableName, pos) } if t != nil { - log.V(vl).Infof("Returning table %s from history for pos %s, schema %s", tableName, gtid, t) + log.V(2).Infof("Returning table %s from history for pos %s, schema %s", tableName, gtid, t) return t } } - h.reload(context.Background()) - if h.lastSchema == nil { - return nil + if h.latestSchema == nil || h.latestSchema[tableName.String()] == nil { + h.reload() + if h.latestSchema == nil { + return nil + } } - log.V(vl).Infof("Returning table %s from latest schema for pos %s, schema %s", tableName, gtid, h.lastSchema[tableName.String()]) - return h.lastSchema[tableName.String()] + log.V(2).Infof("Returning table %s from latest schema for pos %s, schema %s", tableName, gtid, h.latestSchema[tableName.String()]) + return h.latestSchema[tableName.String()] } // sortSchemas sorts entries in ascending order of gtid, ex: 40,44,48 @@ -241,7 +252,7 @@ func (h *HistorianSvc) getTableFromHistoryForPos(tableName sqlparser.TableIdent, return pos.Equal(h.schemas[i].pos) || !pos.AtLeast(h.schemas[i].pos) }) if idx >= len(h.schemas) || idx == 0 && !pos.Equal(h.schemas[idx].pos) { // beyond the range of the cache - log.Infof("Not found schema in cache for %s with pos %s", tableName, pos) + log.Infof("Schema not found in cache for %s with pos %s", tableName, pos) return nil } if pos.Equal(h.schemas[idx].pos) { //exact match to a cache entry diff --git a/go/vt/vttablet/tabletserver/schema/tracker.go b/go/vt/vttablet/tabletserver/schema/tracker.go index 08362bcdaaa..918ce42421e 100644 --- a/go/vt/vttablet/tabletserver/schema/tracker.go +++ b/go/vt/vttablet/tabletserver/schema/tracker.go @@ -70,12 +70,13 @@ func (t *Tracker) SchemaUpdated(gtid string, ddl string, timestamp int64) error log.Infof("Tracker not enabled, ignoring SchemaUpdated event") return nil } - log.V(vl).Infof("Processing SchemaUpdated event for gtid %s, ddl %s", gtid, ddl) + log.Infof("Processing SchemaUpdated event for gtid %s, ddl %s", gtid, ddl) if gtid == "" || ddl == "" { return fmt.Errorf("got invalid gtid or ddl in SchemaUpdated") } ctx := context.Background() - t.engine.Reload(ctx) + + // Engine will have reloaded the schema because vstream will reload it on a DDL tables := t.engine.GetSchema() dbSchema := &binlogdatapb.MinimalSchema{ Tables: []*binlogdatapb.MinimalTable{}, diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index beaaff08889..a786376194a 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -239,7 +239,7 @@ func NewTabletServer(name string, config *tabletenv.TabletConfig, topoServer *to tsv.hr = heartbeat.NewReader(tsv) tsv.txThrottler = txthrottler.NewTxThrottler(tsv.config, topoServer) tsOnce.Do(func() { srvTopoServer = srvtopo.NewResilientServer(topoServer, "TabletSrvTopo") }) - tsv.vstreamer = vstreamer.NewEngine(tsv, srvTopoServer, tsv.sh) + tsv.vstreamer = vstreamer.NewEngine(tsv, srvTopoServer, tsv.se, tsv.sh) tsv.tracker = schema.NewTracker(tsv.se) tsv.watcher = NewReplicationWatcher(tsv, tsv.vstreamer, tsv.config, tsv.tracker) tsv.messager = messager.NewEngine(tsv, tsv.se, tsv.vstreamer) diff --git a/go/vt/vttablet/tabletserver/vstreamer/engine.go b/go/vt/vttablet/tabletserver/vstreamer/engine.go index 9f107aa8970..ce362fd181a 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/engine.go +++ b/go/vt/vttablet/tabletserver/vstreamer/engine.go @@ -63,6 +63,7 @@ type Engine struct { // The following members are initialized once at the beginning. ts srvtopo.Server + se *schema.Engine sh schema.Historian keyspace string cell string @@ -74,7 +75,7 @@ type Engine struct { // NewEngine creates a new Engine. // Initialization sequence is: NewEngine->InitDBConfig->Open. // Open and Close can be called multiple times and are idempotent. -func NewEngine(env tabletenv.Env, ts srvtopo.Server, sh schema.Historian) *Engine { +func NewEngine(env tabletenv.Env, ts srvtopo.Server, se *schema.Engine, sh schema.Historian) *Engine { vse := &Engine{ env: env, streamers: make(map[int]*vstreamer), @@ -82,6 +83,7 @@ func NewEngine(env tabletenv.Env, ts srvtopo.Server, sh schema.Historian) *Engin resultStreamers: make(map[int]*resultStreamer), lvschema: &localVSchema{vschema: &vindexes.VSchema{}}, ts: ts, + se: se, sh: sh, vschemaErrors: env.Exporter().NewCounter("VSchemaErrors", "Count of VSchema errors"), vschemaUpdates: env.Exporter().NewCounter("VSchemaUpdates", "Count of VSchema updates. Does not include errors"), @@ -156,7 +158,7 @@ func (vse *Engine) Stream(ctx context.Context, startPos string, filter *binlogda if !vse.isOpen { return nil, 0, errors.New("VStreamer is not open") } - streamer := newVStreamer(ctx, vse.env.Config().DB.AppWithDB(), vse.sh, startPos, filter, vse.lvschema, send) + streamer := newVStreamer(ctx, vse.env.Config().DB.AppWithDB(), vse.se, vse.sh, startPos, filter, vse.lvschema, send) idx := vse.streamIdx vse.streamers[idx] = streamer vse.streamIdx++ @@ -318,7 +320,7 @@ func (vse *Engine) setWatch() { vschema: vschema, } b, _ := json.MarshalIndent(vschema, "", " ") - log.Infof("Updated vschema: %s", b) + log.V(2).Infof("Updated vschema: %s", b) for _, s := range vse.streamers { s.SetVSchema(vse.lvschema) } diff --git a/go/vt/vttablet/tabletserver/vstreamer/main_test.go b/go/vt/vttablet/tabletserver/vstreamer/main_test.go index 642ee1fbc08..7dfbdea99a3 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/main_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/main_test.go @@ -60,7 +60,7 @@ func TestMain(m *testing.M) { // circular dependencies historian = schema.NewHistorian(env.SchemaEngine) historian.Open() - engine = NewEngine(env.TabletEnv, env.SrvTopo, historian) + engine = NewEngine(env.TabletEnv, env.SrvTopo, env.SchemaEngine, historian) engine.Open(env.KeyspaceName, env.Cells[0]) defer engine.Close() @@ -75,10 +75,8 @@ func customEngine(t *testing.T, modifier func(mysql.ConnParams) mysql.ConnParams modified := modifier(*original) config := env.TabletEnv.Config().Clone() config.DB = dbconfigs.NewTestDBConfigs(modified, modified, modified.DbName) - historian = schema.NewHistorian(env.SchemaEngine) - historian.Open() - engine := NewEngine(tabletenv.NewEnv(config, "VStreamerTest"), env.SrvTopo, historian) + engine := NewEngine(tabletenv.NewEnv(config, "VStreamerTest"), env.SrvTopo, env.SchemaEngine, historian) engine.Open(env.KeyspaceName, env.Cells[0]) return engine } diff --git a/go/vt/vttablet/tabletserver/vstreamer/resultstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/resultstreamer_test.go index 92a1a277b34..b84915a08f6 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/resultstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/resultstreamer_test.go @@ -36,7 +36,7 @@ func TestStreamResults(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) query := "select id, val from t1 order by id" wantStream := []string{ diff --git a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go index 4ae7c55ac88..3157500b2e1 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/rowstreamer_test.go @@ -48,13 +48,15 @@ func TestStreamRowsScan(t *testing.T) { "create table t4(id1 int, id2 int, id3 int, val varbinary(128), primary key(id1, id2, id3))", "insert into t4 values (1, 2, 3, 'aaa'), (2, 3, 4, 'bbb')", }) + defer execStatements(t, []string{ "drop table t1", "drop table t2", "drop table t3", "drop table t4", }) - engine.sh.Reload(context.Background()) + + engine.se.Reload(context.Background()) // t1: all rows wantStream := []string{ @@ -137,19 +139,22 @@ func TestStreamRowsUnicode(t *testing.T) { execStatements(t, []string{ "create table t1(id int, val varchar(128) COLLATE utf8_unicode_ci, primary key(id))", }) + defer execStatements(t, []string{ "drop table t1", }) // Use an engine with latin1 charset. savedEngine := engine - defer func() { engine = savedEngine }() + defer func() { + engine = savedEngine + }() engine = customEngine(t, func(in mysql.ConnParams) mysql.ConnParams { in.Charset = "latin1" return in }) defer engine.Close() - + engine.se.Reload(context.Background()) // We need a latin1 connection. conn, err := env.Mysqld.GetDbaConnection() if err != nil { @@ -195,10 +200,11 @@ func TestStreamRowsKeyRange(t *testing.T) { "create table t1(id1 int, val varbinary(128), primary key(id1))", "insert into t1 values (1, 'aaa'), (6, 'bbb')", }) + defer execStatements(t, []string{ "drop table t1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) time.Sleep(1 * time.Second) @@ -225,10 +231,11 @@ func TestStreamRowsFilterInt(t *testing.T) { "create table t1(id1 int, id2 int, val varbinary(128), primary key(id1))", "insert into t1 values (1, 100, 'aaa'), (2, 200, 'bbb'), (3, 200, 'ccc'), (4, 100, 'ddd'), (5, 200, 'eee')", }) + defer execStatements(t, []string{ "drop table t1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) time.Sleep(1 * time.Second) @@ -254,10 +261,11 @@ func TestStreamRowsFilterVarBinary(t *testing.T) { "create table t1(id1 int, val varbinary(128), primary key(id1))", "insert into t1 values (1,'kepler'), (2, 'newton'), (3, 'newton'), (4, 'kepler'), (5, 'newton'), (6, 'kepler')", }) + defer execStatements(t, []string{ "drop table t1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) time.Sleep(1 * time.Second) @@ -282,10 +290,11 @@ func TestStreamRowsMultiPacket(t *testing.T) { "create table t1(id int, val varbinary(128), primary key(id))", "insert into t1 values (1, '234'), (2, '6789'), (3, '1'), (4, '2345678901'), (5, '2')", }) + defer execStatements(t, []string{ "drop table t1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) wantStream := []string{ `fields: fields: pkfields: `, @@ -310,10 +319,11 @@ func TestStreamRowsCancel(t *testing.T) { "create table t1(id int, val varbinary(128), primary key(id))", "insert into t1 values (1, '234567890'), (2, '234')", }) + defer execStatements(t, []string{ "drop table t1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) ctx, cancel := context.WithCancel(context.Background()) defer cancel() diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index 5ea61ff0431..2373f79cc46 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -60,6 +60,7 @@ type vstreamer struct { cancel func() cp dbconfigs.Connector + se *schema.Engine sh schema.Historian startPos string filter *binlogdatapb.Filter @@ -102,13 +103,14 @@ type streamerPlan struct { // Other constructs like joins, group by, etc. are not supported. // vschema: the current vschema. This value can later be changed through the SetVSchema method. // send: callback function to send events. -func newVStreamer(ctx context.Context, cp dbconfigs.Connector, sh schema.Historian, startPos string, +func newVStreamer(ctx context.Context, cp dbconfigs.Connector, se *schema.Engine, sh schema.Historian, startPos string, filter *binlogdatapb.Filter, vschema *localVSchema, send func([]*binlogdatapb.VEvent) error) *vstreamer { ctx, cancel := context.WithCancel(ctx) return &vstreamer{ ctx: ctx, cancel: cancel, cp: cp, + se: se, sh: sh, startPos: startPos, filter: filter, @@ -454,6 +456,7 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e Type: binlogdatapb.VEventType_OTHER, }) } + vs.se.ReloadAt(context.Background(), vs.pos) case sqlparser.StmtOther, sqlparser.StmtPriv: // These are either: // 1) DBA statements like REPAIR that can be ignored. @@ -476,9 +479,11 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e // A schema change will result in a change in table id, which // will generate a new plan and FIELD event. id := ev.TableID(vs.format) + if _, ok := vs.plans[id]; ok { return nil, nil } + tm, err := ev.TableMap(vs.format) if err != nil { return nil, err @@ -494,6 +499,7 @@ func (vs *vstreamer) parseEvent(ev mysql.BinlogEvent) ([]*binlogdatapb.VEvent, e vs.plans[id] = nil return nil, nil } + vevent, err := vs.buildTablePlan(id, tm) if err != nil { return nil, err diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index 8f99835e7f8..a318aca681e 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -51,7 +51,7 @@ func TestVersion(t *testing.T) { }() mh := newMockHistorian(env.SchemaEngine) - engine = NewEngine(engine.env, env.SrvTopo, mh) + engine = NewEngine(engine.env, env.SrvTopo, env.SchemaEngine, mh) engine.Open(env.KeyspaceName, env.Cells[0]) defer engine.Close() @@ -61,7 +61,7 @@ func TestVersion(t *testing.T) { defer execStatements(t, []string{ "drop table _vt.schema_version", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) testcases := []testcase{{ input: []string{ fmt.Sprintf("insert into _vt.schema_version values(1, 'MariaDB/0-41983-20', 123, 'create table t1', 'abc')"), @@ -88,7 +88,7 @@ func TestFilteredVarBinary(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) filter := &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{{ @@ -141,7 +141,7 @@ func TestFilteredInt(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) filter := &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{{ @@ -196,7 +196,7 @@ func TestStatements(t *testing.T) { "drop table stream1", "drop table stream2", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) testcases := []testcase{{ input: []string{ @@ -290,7 +290,7 @@ func TestOther(t *testing.T) { "drop table stream1", "drop table stream2", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) testcases := []string{ "repair table stream2", @@ -357,7 +357,7 @@ func TestRegexp(t *testing.T) { "drop table yes_stream", "drop table no_stream", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) filter := &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{{ @@ -397,7 +397,7 @@ func TestREKeyRange(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) setVSchema(t, shardedVSchema) defer env.SetVSchema("{}") @@ -487,7 +487,7 @@ func TestInKeyRangeMultiColumn(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) setVSchema(t, multicolumnVSchema) defer env.SetVSchema("{}") @@ -542,7 +542,7 @@ func TestREMultiColumnVindex(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) setVSchema(t, multicolumnVSchema) defer env.SetVSchema("{}") @@ -596,7 +596,7 @@ func TestSelectFilter(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) filter := &binlogdatapb.Filter{ Rules: []*binlogdatapb.Rule{{ @@ -652,7 +652,7 @@ func TestDDLAddColumn(t *testing.T) { "insert into ddl_test2 values(2, 'bbb', 'ccc')", "commit", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -718,7 +718,7 @@ func TestDDLDropColumn(t *testing.T) { "alter table ddl_test2 drop column val2", "insert into ddl_test2 values(2, 'bbb')", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -773,7 +773,7 @@ func TestBuffering(t *testing.T) { execStatement(t, "create table packet_test(id int, val varbinary(128), primary key(id))") defer execStatement(t, "drop table packet_test") - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) testcases := []testcase{{ // All rows in one packet. @@ -885,7 +885,7 @@ func TestBestEffortNameInFieldEvent(t *testing.T) { defer execStatements(t, []string{ "drop table vitess_test_new", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) testcases := []testcase{{ input: []string{ "insert into vitess_test_new values(2, 'abc')", @@ -932,7 +932,7 @@ func TestTypes(t *testing.T) { "drop table vitess_misc", "drop table vitess_null", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) testcases := []testcase{{ input: []string{ @@ -1065,7 +1065,7 @@ func TestJSON(t *testing.T) { t.Fatal(err) } defer execStatement(t, "drop table vitess_json") - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) testcases := []testcase{{ input: []string{ @@ -1094,7 +1094,7 @@ func TestExternalTable(t *testing.T) { defer execStatements(t, []string{ "drop database external", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) testcases := []testcase{{ input: []string{ @@ -1123,7 +1123,7 @@ func TestJournal(t *testing.T) { defer execStatements(t, []string{ "drop table _vt.resharding_journal", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) journal1 := &binlogdatapb.Journal{ Id: 1, @@ -1163,7 +1163,7 @@ func TestMinimalMode(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) // Record position before the next few statements. pos := masterPosition(t) @@ -1199,7 +1199,7 @@ func TestStatementMode(t *testing.T) { "create table stream2(id int, val varbinary(128), primary key(id))", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) defer execStatements(t, []string{ "drop table stream1", @@ -1254,7 +1254,7 @@ func TestNoFutureGTID(t *testing.T) { defer execStatements(t, []string{ "drop table stream1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) pos := masterPosition(t) t.Logf("current position: %v", pos) @@ -1293,7 +1293,7 @@ func TestFilteredMultipleWhere(t *testing.T) { defer execStatements(t, []string{ "drop table t1", }) - engine.sh.Reload(context.Background()) + engine.se.Reload(context.Background()) setVSchema(t, shardedVSchema) defer env.SetVSchema("{}")