From bf7f7c9e2aac60fec614c5509fcdcb082974698f Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 18 Apr 2020 21:24:07 -0700 Subject: [PATCH 01/16] exporter: allow named exports after unnamed There are upcoming use cases where we'll create new named subcomponents of tabletserver along with the global unnamed one. Specifically for vreplication external mysql. This change will tolerate such usage and we'll just reuse the vars created by the unnamed tabletserver. Signed-off-by: Sugu Sougoumarane --- go/vt/servenv/exporter.go | 114 ++++++++++++++++++++++++++++----- go/vt/servenv/exporter_test.go | 74 +++++++++++++++++++++ 2 files changed, 172 insertions(+), 16 deletions(-) diff --git a/go/vt/servenv/exporter.go b/go/vt/servenv/exporter.go index 61a12b70ea6..969c12a4ebc 100644 --- a/go/vt/servenv/exporter.go +++ b/go/vt/servenv/exporter.go @@ -50,6 +50,11 @@ var ( // name causes that Exporter to be reused. exporters = make(map[string]*Exporter) + // unnamedExports contain variables that were exported using + // an unnamed exporter. If there is a name collision here, we + // just reuse the unnamed variable. + unnamedExports = make(map[string]expvar.Var) + // exportedMultiCountVars contains the merged stats vars created for the vars that support Counts. exportedMultiCountVars = make(map[string]*multiCountVars) @@ -183,7 +188,9 @@ func (e *Exporter) NewCountersFuncWithMultiLabels(name, help string, labels []st // If e.name is empty, it's a pass-through. // If name is empty, it's an unexported var. if e.name == "" || name == "" { - return stats.NewCountersFuncWithMultiLabels(name, help, labels, f) + v := stats.NewCountersFuncWithMultiLabels(name, help, labels, f) + addUnnamedExport(name, v) + return v } lvar := stats.NewCountersFuncWithMultiLabels("", help, labels, f) _ = e.createCountsTracker(name, help, labels, lvar, replaceOnDup, typeCounter) @@ -194,6 +201,13 @@ func (e *Exporter) createCountsTracker(name, help string, labels []string, lvar exporterMu.Lock() defer exporterMu.Unlock() + if c, ok := unnamedExports[name]; ok { + if typ == typeCounter { + return c.(multiCountVar) + } + return nil + } + if evar, ok := exportedMultiCountVars[name]; ok { evar.mu.Lock() defer evar.mu.Unlock() @@ -221,7 +235,9 @@ func (e *Exporter) createCountsTracker(name, help string, labels []string, lvar // NewGaugesFuncWithMultiLabels creates a name-spaced equivalent for stats.NewGaugesFuncWithMultiLabels. func (e *Exporter) NewGaugesFuncWithMultiLabels(name, help string, labels []string, f func() map[string]int64) *stats.GaugesFuncWithMultiLabels { if e.name == "" || name == "" { - return stats.NewGaugesFuncWithMultiLabels(name, help, labels, f) + v := stats.NewGaugesFuncWithMultiLabels(name, help, labels, f) + addUnnamedExport(name, v) + return v } lvar := stats.NewGaugesFuncWithMultiLabels("", help, labels, f) _ = e.createCountsTracker(name, help, labels, lvar, replaceOnDup, typeGauge) @@ -231,7 +247,9 @@ func (e *Exporter) NewGaugesFuncWithMultiLabels(name, help string, labels []stri // NewCounter creates a name-spaced equivalent for stats.NewCounter. func (e *Exporter) NewCounter(name string, help string) *stats.Counter { if e.name == "" || name == "" { - return stats.NewCounter(name, help) + v := stats.NewCounter(name, help) + addUnnamedExport(name, v) + return v } lvar := stats.NewCounter("", help) if exists := e.createCountTracker(name, help, lvar, reuseOnDup, typeCounter); exists != nil { @@ -244,6 +262,13 @@ func (e *Exporter) createCountTracker(name, help string, lvar singleCountVar, on exporterMu.Lock() defer exporterMu.Unlock() + if c, ok := unnamedExports[name]; ok { + if typ == typeCounter { + return c.(singleCountVar) + } + return nil + } + if evar, ok := exportedSingleCountVars[name]; ok { evar.mu.Lock() defer evar.mu.Unlock() @@ -271,7 +296,9 @@ func (e *Exporter) createCountTracker(name, help string, lvar singleCountVar, on // NewGauge creates a name-spaced equivalent for stats.NewGauge. func (e *Exporter) NewGauge(name string, help string) *stats.Gauge { if e.name == "" || name == "" { - return stats.NewGauge(name, help) + v := stats.NewGauge(name, help) + addUnnamedExport(name, v) + return v } lvar := stats.NewGauge("", help) if exists := e.createCountTracker(name, help, lvar, reuseOnDup, typeCounter); exists != nil { @@ -283,7 +310,9 @@ func (e *Exporter) NewGauge(name string, help string) *stats.Gauge { // NewCounterFunc creates a name-spaced equivalent for stats.NewCounterFunc. func (e *Exporter) NewCounterFunc(name string, help string, f func() int64) *stats.CounterFunc { if e.name == "" || name == "" { - return stats.NewCounterFunc(name, help, f) + v := stats.NewCounterFunc(name, help, f) + addUnnamedExport(name, v) + return v } lvar := stats.NewCounterFunc("", help, f) _ = e.createCountTracker(name, help, lvar, replaceOnDup, typeCounter) @@ -293,7 +322,9 @@ func (e *Exporter) NewCounterFunc(name string, help string, f func() int64) *sta // NewGaugeFunc creates a name-spaced equivalent for stats.NewGaugeFunc. func (e *Exporter) NewGaugeFunc(name string, help string, f func() int64) *stats.GaugeFunc { if e.name == "" || name == "" { - return stats.NewGaugeFunc(name, help, f) + v := stats.NewGaugeFunc(name, help, f) + addUnnamedExport(name, v) + return v } lvar := stats.NewGaugeFunc("", help, f) _ = e.createCountTracker(name, help, lvar, replaceOnDup, typeGauge) @@ -303,7 +334,9 @@ func (e *Exporter) NewGaugeFunc(name string, help string, f func() int64) *stats // NewCounterDurationFunc creates a name-spaced equivalent for stats.NewCounterDurationFunc. func (e *Exporter) NewCounterDurationFunc(name string, help string, f func() time.Duration) *stats.CounterDurationFunc { if e.name == "" || name == "" { - return stats.NewCounterDurationFunc(name, help, f) + v := stats.NewCounterDurationFunc(name, help, f) + addUnnamedExport(name, v) + return v } lvar := stats.NewCounterDurationFunc("", help, f) _ = e.createCountTracker(name, help, lvar, replaceOnDup, typeCounter) @@ -313,7 +346,9 @@ func (e *Exporter) NewCounterDurationFunc(name string, help string, f func() tim // NewGaugeDurationFunc creates a name-spaced equivalent for stats.NewGaugeDurationFunc. func (e *Exporter) NewGaugeDurationFunc(name string, help string, f func() time.Duration) *stats.GaugeDurationFunc { if e.name == "" || name == "" { - return stats.NewGaugeDurationFunc(name, help, f) + v := stats.NewGaugeDurationFunc(name, help, f) + addUnnamedExport(name, v) + return v } lvar := stats.NewGaugeDurationFunc("", help, f) _ = e.createCountTracker(name, help, lvar, replaceOnDup, typeGauge) @@ -324,7 +359,9 @@ func (e *Exporter) NewGaugeDurationFunc(name string, help string, f func() time. // Tags are ignored if the exporter is named. func (e *Exporter) NewCountersWithSingleLabel(name, help string, label string, tags ...string) *stats.CountersWithSingleLabel { if e.name == "" || name == "" { - return stats.NewCountersWithSingleLabel(name, help, label, tags...) + v := stats.NewCountersWithSingleLabel(name, help, label, tags...) + addUnnamedExport(name, v) + return v } lvar := stats.NewCountersWithSingleLabel("", help, label) if exists := e.createCountsTracker(name, help, []string{label}, lvar, reuseOnDup, typeCounter); exists != nil { @@ -337,7 +374,9 @@ func (e *Exporter) NewCountersWithSingleLabel(name, help string, label string, t // Tags are ignored if the exporter is named. func (e *Exporter) NewGaugesWithSingleLabel(name, help string, label string, tags ...string) *stats.GaugesWithSingleLabel { if e.name == "" || name == "" { - return stats.NewGaugesWithSingleLabel(name, help, label, tags...) + v := stats.NewGaugesWithSingleLabel(name, help, label, tags...) + addUnnamedExport(name, v) + return v } lvar := stats.NewGaugesWithSingleLabel("", help, label) @@ -350,7 +389,9 @@ func (e *Exporter) NewGaugesWithSingleLabel(name, help string, label string, tag // NewCountersWithMultiLabels creates a name-spaced equivalent for stats.NewCountersWithMultiLabels. func (e *Exporter) NewCountersWithMultiLabels(name, help string, labels []string) *stats.CountersWithMultiLabels { if e.name == "" || name == "" { - return stats.NewCountersWithMultiLabels(name, help, labels) + v := stats.NewCountersWithMultiLabels(name, help, labels) + addUnnamedExport(name, v) + return v } lvar := stats.NewCountersWithMultiLabels("", help, labels) @@ -363,7 +404,9 @@ func (e *Exporter) NewCountersWithMultiLabels(name, help string, labels []string // NewGaugesWithMultiLabels creates a name-spaced equivalent for stats.NewGaugesWithMultiLabels. func (e *Exporter) NewGaugesWithMultiLabels(name, help string, labels []string) *stats.GaugesWithMultiLabels { if e.name == "" || name == "" { - return stats.NewGaugesWithMultiLabels(name, help, labels) + v := stats.NewGaugesWithMultiLabels(name, help, labels) + addUnnamedExport(name, v) + return v } lvar := stats.NewGaugesWithMultiLabels("", help, labels) @@ -377,14 +420,22 @@ func (e *Exporter) NewGaugesWithMultiLabels(name, help string, labels []string) // The function currently just returns an unexported variable. func (e *Exporter) NewTimings(name string, help string, label string) *TimingsWrapper { if e.name == "" || name == "" { - return &TimingsWrapper{ + v := &TimingsWrapper{ timings: stats.NewMultiTimings(name, help, []string{label}), } + addUnnamedExport(name, v.timings) + return v } exporterMu.Lock() defer exporterMu.Unlock() + if v, ok := unnamedExports[name]; ok { + return &TimingsWrapper{ + timings: v.(*stats.MultiTimings), + } + } + if tv, ok := exportedTimingsVars[name]; ok { return &TimingsWrapper{ name: e.name, @@ -403,14 +454,22 @@ func (e *Exporter) NewTimings(name string, help string, label string) *TimingsWr // The function currently just returns an unexported variable. func (e *Exporter) NewMultiTimings(name string, help string, labels []string) *MultiTimingsWrapper { if e.name == "" || name == "" { - return &MultiTimingsWrapper{ + v := &MultiTimingsWrapper{ timings: stats.NewMultiTimings(name, help, labels), } + addUnnamedExport(name, v.timings) + return v } exporterMu.Lock() defer exporterMu.Unlock() + if v, ok := unnamedExports[name]; ok { + return &MultiTimingsWrapper{ + timings: v.(*stats.MultiTimings), + } + } + if tv, ok := exportedTimingsVars[name]; ok { return &MultiTimingsWrapper{ name: e.name, @@ -429,11 +488,18 @@ func (e *Exporter) NewMultiTimings(name string, help string, labels []string) *M // The function currently just returns an unexported variable. func (e *Exporter) NewRates(name string, singleCountVar multiCountVar, samples int, interval time.Duration) *stats.Rates { if e.name == "" || name == "" { - return stats.NewRates(name, singleCountVar, samples, interval) + v := stats.NewRates(name, singleCountVar, samples, interval) + addUnnamedExport(name, v) + return v } exporterMu.Lock() defer exporterMu.Unlock() + + if v, ok := unnamedExports[name]; ok { + return v.(*stats.Rates) + } + ov, ok := exportedOtherStatsVars[name] if !ok { ov = expvar.NewMap(name) @@ -452,7 +518,9 @@ func (e *Exporter) NewRates(name string, singleCountVar multiCountVar, samples i // The function currently just returns an unexported variable. func (e *Exporter) NewHistogram(name, help string, cutoffs []int64) *stats.Histogram { if e.name == "" || name == "" { - return stats.NewHistogram(name, help, cutoffs) + v := stats.NewHistogram(name, help, cutoffs) + addUnnamedExport(name, v) + return v } hist := stats.NewHistogram("", help, cutoffs) e.addToOtherVars(name, hist) @@ -463,6 +531,7 @@ func (e *Exporter) NewHistogram(name, help string, cutoffs []int64) *stats.Histo // The function just passes through if the Exporter name is empty. func (e *Exporter) Publish(name string, v expvar.Var) { if e.name == "" || name == "" { + addUnnamedExport(name, v) stats.Publish(name, v) return } @@ -473,6 +542,10 @@ func (e *Exporter) addToOtherVars(name string, v expvar.Var) { exporterMu.Lock() defer exporterMu.Unlock() + if _, ok := unnamedExports[name]; ok { + return + } + ov, ok := exportedOtherStatsVars[name] if !ok { ov = expvar.NewMap(name) @@ -620,6 +693,15 @@ func (hf *handleFunc) Get() func(w http.ResponseWriter, r *http.Request) { //----------------------------------------------------------------- +func addUnnamedExport(name string, v expvar.Var) { + if name == "" { + return + } + exporterMu.Lock() + unnamedExports[name] = v + exporterMu.Unlock() +} + func combineLabels(label string, labels []string) []string { return append(append(make([]string, 0, len(labels)+1), label), labels...) } diff --git a/go/vt/servenv/exporter_test.go b/go/vt/servenv/exporter_test.go index 123e7f3bab0..aa86c2679ae 100644 --- a/go/vt/servenv/exporter_test.go +++ b/go/vt/servenv/exporter_test.go @@ -108,6 +108,10 @@ func TestCountersFuncWithMultiLabels(t *testing.T) { ebd.NewCountersFuncWithMultiLabels("", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 2} }) ebd.NewCountersFuncWithMultiLabels("", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 3} }) + // Ensure reuse of global var is ignored. + ebd.NewCountersFuncWithMultiLabels("gcfwml", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 2} }) + assert.Equal(t, `{"a": 1}`, expvar.Get("gcfwml").String()) + ebd.NewCountersFuncWithMultiLabels("lcfwml", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 4} }) assert.Equal(t, `{"i1.a": 4}`, expvar.Get("lcfwml").String()) @@ -132,6 +136,10 @@ func TestGaugesFuncWithMultiLabels(t *testing.T) { ebd.NewGaugesFuncWithMultiLabels("", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 2} }) ebd.NewGaugesFuncWithMultiLabels("", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 3} }) + // Ensure reuse of global var is ignored. + ebd.NewGaugesFuncWithMultiLabels("ggfwml", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 1} }) + assert.Equal(t, `{"a": 1}`, expvar.Get("ggfwml").String()) + ebd.NewGaugesFuncWithMultiLabels("lgfwml", "", []string{"l"}, func() map[string]int64 { return map[string]int64{"a": 4} }) assert.Equal(t, `{"i1.a": 4}`, expvar.Get("lgfwml").String()) @@ -157,6 +165,11 @@ func TestCounter(t *testing.T) { ebd.NewCounter("", "") ebd.NewCounter("", "") + // Ensure global var gets reused. + c = ebd.NewCounter("gcounter", "") + c.Add(1) + assert.Equal(t, "2", expvar.Get("gcounter").String()) + c = ebd.NewCounter("lcounter", "") c.Add(4) assert.Equal(t, `{"i1": 4}`, expvar.Get("lcounter").String()) @@ -185,6 +198,11 @@ func TestGauge(t *testing.T) { ebd.NewGauge("", "") ebd.NewGauge("", "") + // Ensure global var gets reused. + c = ebd.NewGauge("ggauge", "") + c.Set(2) + assert.Equal(t, "2", expvar.Get("ggauge").String()) + c = ebd.NewGauge("lgauge", "") c.Set(4) assert.Equal(t, `{"i1": 4}`, expvar.Get("lgauge").String()) @@ -213,6 +231,10 @@ func TestCounterFunc(t *testing.T) { ebd.NewCounterFunc("", "", func() int64 { return 2 }) ebd.NewCounterFunc("", "", func() int64 { return 3 }) + // Ensure reuse of global var is ignored. + ebd.NewCounterFunc("gcf", "", func() int64 { return 2 }) + assert.Equal(t, "1", expvar.Get("gcf").String()) + ebd.NewCounterFunc("lcf", "", func() int64 { return 4 }) assert.Equal(t, `{"i1": 4}`, expvar.Get("lcf").String()) @@ -237,6 +259,10 @@ func TestGaugeFunc(t *testing.T) { ebd.NewGaugeFunc("", "", func() int64 { return 2 }) ebd.NewGaugeFunc("", "", func() int64 { return 3 }) + // Ensure reuse of global var is ignored. + ebd.NewGaugeFunc("ggf", "", func() int64 { return 2 }) + assert.Equal(t, "1", expvar.Get("ggf").String()) + ebd.NewGaugeFunc("lgf", "", func() int64 { return 4 }) assert.Equal(t, `{"i1": 4}`, expvar.Get("lgf").String()) @@ -261,6 +287,10 @@ func TestCounterDurationFunc(t *testing.T) { ebd.NewCounterDurationFunc("", "", func() time.Duration { return 2 }) ebd.NewCounterDurationFunc("", "", func() time.Duration { return 3 }) + // Ensure reuse of global var is ignored. + ebd.NewCounterDurationFunc("gcduration", "", func() time.Duration { return 2 }) + assert.Equal(t, "1", expvar.Get("gcduration").String()) + ebd.NewCounterDurationFunc("lcduration", "", func() time.Duration { return 4 }) assert.Equal(t, `{"i1": 4}`, expvar.Get("lcduration").String()) @@ -285,6 +315,10 @@ func TestGaugeDurationFunc(t *testing.T) { ebd.NewGaugeDurationFunc("", "", func() time.Duration { return 2 }) ebd.NewGaugeDurationFunc("", "", func() time.Duration { return 3 }) + // Ensure reuse of global var is ignored. + ebd.NewGaugeDurationFunc("ggduration", "", func() time.Duration { return 2 }) + assert.Equal(t, "1", expvar.Get("ggduration").String()) + ebd.NewGaugeDurationFunc("lgduration", "", func() time.Duration { return 4 }) assert.Equal(t, `{"i1": 4}`, expvar.Get("lgduration").String()) @@ -310,6 +344,11 @@ func TestCountersWithSingleLabel(t *testing.T) { ebd.NewCountersWithSingleLabel("", "", "l") ebd.NewCountersWithSingleLabel("", "", "l") + // Ensure global var gets reused. + g = ebd.NewCountersWithSingleLabel("gcwsl", "", "l") + g.Add("a", 1) + assert.Equal(t, `{"a": 2}`, expvar.Get("gcwsl").String()) + g = ebd.NewCountersWithSingleLabel("lcwsl", "", "l") g.Add("a", 4) assert.Equal(t, `{"i1.a": 4}`, expvar.Get("lcwsl").String()) @@ -338,6 +377,11 @@ func TestGaugesWithSingleLabel(t *testing.T) { ebd.NewGaugesWithSingleLabel("", "", "l") ebd.NewGaugesWithSingleLabel("", "", "l") + // Ensure reuse of global var is ignored. + g = ebd.NewGaugesWithSingleLabel("ggwsl", "", "l") + g.Set("a", 2) + assert.Equal(t, `{"a": 1}`, expvar.Get("ggwsl").String()) + g = ebd.NewGaugesWithSingleLabel("lgwsl", "", "l") g.Set("a", 4) assert.Equal(t, `{"i1.a": 4}`, expvar.Get("lgwsl").String()) @@ -367,6 +411,11 @@ func TestCountersWithMultiLabels(t *testing.T) { ebd.NewCountersWithMultiLabels("", "", []string{"l"}) ebd.NewCountersWithMultiLabels("", "", []string{"l"}) + // Ensure global var gets reused. + g = ebd.NewCountersWithMultiLabels("gcwml", "", []string{"l"}) + g.Add([]string{"a"}, 1) + assert.Equal(t, `{"a": 2}`, expvar.Get("gcwml").String()) + g = ebd.NewCountersWithMultiLabels("lcwml", "", []string{"l"}) g.Add([]string{"a"}, 4) assert.Equal(t, `{"i1.a": 4}`, expvar.Get("lcwml").String()) @@ -395,6 +444,11 @@ func TestGaugesWithMultiLabels(t *testing.T) { ebd.NewGaugesWithMultiLabels("", "", []string{"l"}) ebd.NewGaugesWithMultiLabels("", "", []string{"l"}) + // Ensure reuse of global var is ignored. + g = ebd.NewGaugesWithMultiLabels("ggwml", "", []string{"l"}) + g.Set([]string{"a"}, 2) + assert.Equal(t, `{"a": 1}`, expvar.Get("ggwml").String()) + g = ebd.NewGaugesWithMultiLabels("lgwml", "", []string{"l"}) g.Set([]string{"a"}, 4) assert.Equal(t, `{"i1.a": 4}`, expvar.Get("lgwml").String()) @@ -426,6 +480,11 @@ func TestTimings(t *testing.T) { ebd.NewTimings("", "", "l") ebd.NewTimings("", "", "l") + // Ensure global var gets reused. + g = ebd.NewTimings("gtimings", "", "l") + g.Add("a", 1) + assert.Contains(t, expvar.Get("gtimings").String(), `"TotalCount":3`) + g = ebd.NewTimings("ltimings", "", "l") g.Add("a", 1) g.Add("a", 1) @@ -469,6 +528,11 @@ func TestMultiTimings(t *testing.T) { ebd.NewMultiTimings("", "", []string{"l"}) ebd.NewMultiTimings("", "", []string{"l"}) + // Ensure global var gets reused. + g = ebd.NewMultiTimings("gmtimings", "", []string{"l"}) + g.Add([]string{"a"}, 1) + assert.Contains(t, expvar.Get("gmtimings").String(), `"TotalCount":3`) + g = ebd.NewMultiTimings("lmtimings", "", []string{"l"}) g.Add([]string{"a"}, 1) g.Add([]string{"a"}, 1) @@ -510,6 +574,10 @@ func TestRates(t *testing.T) { ebd.NewRates("", tm, 15*60/5, 5*time.Second) ebd.NewRates("", tm, 15*60/5, 5*time.Second) + // Ensure global var gets reused. + ebd.NewRates("grates", tm, 15*60/5, 5*time.Second) + assert.Equal(t, "{}", expvar.Get("grates").String()) + // Ensure var gets reused. rates1 := ebd.NewRates("lrates", tm, 15*60/5, 5*time.Second) rates2 := ebd.NewRates("lrates", tm, 15*60/5, 5*time.Second) @@ -532,6 +600,9 @@ func TestHistogram(t *testing.T) { ebd.NewHistogram("", "", []int64{10}) ebd.NewHistogram("", "", []int64{10}) + // Ensure reuse of global var doesn't panic. + _ = ebd.NewHistogram("ghistogram", "", []int64{10}) + g = ebd.NewHistogram("lhistogram", "", []int64{10}) g.Add(1) g.Add(1) @@ -556,6 +627,9 @@ func TestPublish(t *testing.T) { ebd.Publish("lpub", s) assert.Equal(t, `{"i1": "1"}`, expvar.Get("lpub").String()) + // Ensure reuse of global var doesn't panic. + ebd.Publish("gpub", s) + ebd = NewExporter("i2", "label") ebd.Publish("lpub", s) assert.Contains(t, expvar.Get("lpub").String(), `"i1": "1"`) From e12cd1c9b44ee80ccbc33967274f92779f647e1b Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 18 Apr 2020 21:51:31 -0700 Subject: [PATCH 02/16] vstreamer: change to use vt_app always The binlog dump was using dba privileges, but it's better to use app, which is also expected to have privileges to request binlogs. Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletserver/vstreamer/engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/vt/vttablet/tabletserver/vstreamer/engine.go b/go/vt/vttablet/tabletserver/vstreamer/engine.go index 305b2530f75..e3f313c4b32 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/engine.go +++ b/go/vt/vttablet/tabletserver/vstreamer/engine.go @@ -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.DBConfigs().DbaWithDB(), vse.se, startPos, filter, vse.lvschema, send) + streamer := newVStreamer(ctx, vse.env.DBConfigs().AppWithDB(), vse.se, startPos, filter, vse.lvschema, send) idx := vse.streamIdx vse.streamers[idx] = streamer vse.streamIdx++ From 7f2845870b248942a8fd02a6e9fd09bbba8461f7 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sat, 18 Apr 2020 22:04:04 -0700 Subject: [PATCH 03/16] vstream: fix broken test Signed-off-by: Sugu Sougoumarane --- go/vt/vtcombo/tablet_map.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/go/vt/vtcombo/tablet_map.go b/go/vt/vtcombo/tablet_map.go index 337472e4e10..e54a2058174 100644 --- a/go/vt/vtcombo/tablet_map.go +++ b/go/vt/vtcombo/tablet_map.go @@ -179,8 +179,6 @@ func InitTabletMap(ts *topo.Server, tpb *vttestpb.VTTestTopology, mysqld mysqlct if dbname == "" { dbname = fmt.Sprintf("vt_%v_%v", keyspace, shard) } - // Clone dbcfgs and override SidecarDBName because there will be one for each db. - copydbcfgs := dbcfgs.Clone() replicas := int(kpb.ReplicaCount) if replicas == 0 { @@ -196,7 +194,7 @@ func InitTabletMap(ts *topo.Server, tpb *vttestpb.VTTestTopology, mysqld mysqlct replicas-- // create the master - if err := CreateTablet(ctx, ts, cell, uid, keyspace, shard, dbname, topodatapb.TabletType_MASTER, mysqld, copydbcfgs); err != nil { + if err := CreateTablet(ctx, ts, cell, uid, keyspace, shard, dbname, topodatapb.TabletType_MASTER, mysqld, dbcfgs.Clone()); err != nil { return err } uid++ @@ -204,7 +202,7 @@ func InitTabletMap(ts *topo.Server, tpb *vttestpb.VTTestTopology, mysqld mysqlct for i := 0; i < replicas; i++ { // create a replica slave - if err := CreateTablet(ctx, ts, cell, uid, keyspace, shard, dbname, topodatapb.TabletType_REPLICA, mysqld, copydbcfgs); err != nil { + if err := CreateTablet(ctx, ts, cell, uid, keyspace, shard, dbname, topodatapb.TabletType_REPLICA, mysqld, dbcfgs.Clone()); err != nil { return err } uid++ @@ -212,7 +210,7 @@ func InitTabletMap(ts *topo.Server, tpb *vttestpb.VTTestTopology, mysqld mysqlct for i := 0; i < rdonlys; i++ { // create a rdonly slave - if err := CreateTablet(ctx, ts, cell, uid, keyspace, shard, dbname, topodatapb.TabletType_RDONLY, mysqld, copydbcfgs); err != nil { + if err := CreateTablet(ctx, ts, cell, uid, keyspace, shard, dbname, topodatapb.TabletType_RDONLY, mysqld, dbcfgs.Clone()); err != nil { return err } uid++ From f5f28724549db93ea6857757dceecce470d42687 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 19 Apr 2020 11:57:42 -0700 Subject: [PATCH 04/16] yaml: ExternalConnections for vreplication Signed-off-by: Sugu Sougoumarane --- go/cmd/vttablet/vttablet.go | 2 +- go/vt/dbconfigs/dbconfigs.go | 18 ++-------- go/vt/dbconfigs/dbconfigs_test.go | 2 +- go/vt/vttablet/tabletmanager/action_agent.go | 24 ++++++------- .../tabletmanager/init_tablet_test.go | 2 +- .../vttablet/tabletmanager/rpc_lock_tables.go | 2 +- .../tabletmanager/vreplication/engine.go | 22 ++++++++++-- .../tabletmanager/vreplication/engine_test.go | 16 ++++----- .../vreplication/framework_test.go | 2 +- .../vttablet/tabletserver/tabletenv/config.go | 36 ++++++++++--------- .../testlib/migrate_served_from_test.go | 2 +- .../testlib/migrate_served_types_test.go | 12 +++---- go/vt/wrangler/traffic_switcher_env_test.go | 4 +-- 13 files changed, 75 insertions(+), 69 deletions(-) diff --git a/go/cmd/vttablet/vttablet.go b/go/cmd/vttablet/vttablet.go index 958d00aadd3..331d08500cd 100644 --- a/go/cmd/vttablet/vttablet.go +++ b/go/cmd/vttablet/vttablet.go @@ -138,7 +138,7 @@ func main() { if servenv.GRPCPort != nil { gRPCPort = int32(*servenv.GRPCPort) } - agent, err = tabletmanager.NewActionAgent(context.Background(), ts, mysqld, qsc, tabletAlias, config.DB, mycnf, int32(*servenv.Port), gRPCPort) + agent, err = tabletmanager.NewActionAgent(context.Background(), ts, mysqld, qsc, tabletAlias, config, mycnf, int32(*servenv.Port), gRPCPort) if err != nil { log.Exitf("NewActionAgent() failed: %v", err) } diff --git a/go/vt/dbconfigs/dbconfigs.go b/go/vt/dbconfigs/dbconfigs.go index 9941ec442a8..dea2269fa2f 100644 --- a/go/vt/dbconfigs/dbconfigs.go +++ b/go/vt/dbconfigs/dbconfigs.go @@ -96,7 +96,7 @@ type DBConfigs struct { allprivsParams mysql.ConnParams externalReplParams mysql.ConnParams - dbname string + DBName string `json:"-"` } // UserConfig contains user-specific configs. @@ -208,18 +208,6 @@ func (c Connector) Host() string { return c.connParams.Host } -// WithDBName returns a new DBConfigs with the dbname set. -func (dbcfgs *DBConfigs) WithDBName(dbname string) *DBConfigs { - dbcfgs = dbcfgs.Clone() - dbcfgs.dbname = dbname - return dbcfgs -} - -// DBName returns the db name. -func (dbcfgs *DBConfigs) DBName() string { - return dbcfgs.dbname -} - // AppWithDB returns connection parameters for app with dbname set. func (dbcfgs *DBConfigs) AppWithDB() Connector { return dbcfgs.makeParams(&dbcfgs.appParams, true) @@ -276,7 +264,7 @@ func (dbcfgs *DBConfigs) ExternalReplWithDB() Connector { func (dbcfgs *DBConfigs) makeParams(cp *mysql.ConnParams, withDB bool) Connector { result := *cp if withDB { - result.DbName = dbcfgs.dbname + result.DbName = dbcfgs.DBName } return Connector{ connParams: &result, @@ -419,6 +407,6 @@ func NewTestDBConfigs(genParams, appDebugParams mysql.ConnParams, dbname string) filteredParams: genParams, replParams: genParams, externalReplParams: genParams, - dbname: dbname, + DBName: dbname, } } diff --git a/go/vt/dbconfigs/dbconfigs_test.go b/go/vt/dbconfigs/dbconfigs_test.go index 23855b6c5ac..3d8d9366546 100644 --- a/go/vt/dbconfigs/dbconfigs_test.go +++ b/go/vt/dbconfigs/dbconfigs_test.go @@ -222,8 +222,8 @@ func TestAccessors(t *testing.T) { dbaParams: mysql.ConnParams{}, filteredParams: mysql.ConnParams{}, replParams: mysql.ConnParams{}, + DBName: "db", } - dbc = dbc.WithDBName("db") if got, want := dbc.AppWithDB().connParams.DbName, "db"; got != want { t.Errorf("dbc.AppWithDB().DbName: %v, want %v", got, want) } diff --git a/go/vt/vttablet/tabletmanager/action_agent.go b/go/vt/vttablet/tabletmanager/action_agent.go index 427eb9b0f58..d5a9707d95e 100644 --- a/go/vt/vttablet/tabletmanager/action_agent.go +++ b/go/vt/vttablet/tabletmanager/action_agent.go @@ -44,6 +44,7 @@ import ( "sync" "time" + "vitess.io/vitess/go/vt/binlog/binlogplayer" "vitess.io/vitess/go/vt/vterrors" "golang.org/x/net/context" @@ -54,7 +55,6 @@ import ( "vitess.io/vitess/go/netutil" "vitess.io/vitess/go/stats" "vitess.io/vitess/go/vt/binlog" - "vitess.io/vitess/go/vt/binlog/binlogplayer" "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/health" "vitess.io/vitess/go/vt/key" @@ -67,6 +67,7 @@ import ( "vitess.io/vitess/go/vt/topotools" "vitess.io/vitess/go/vt/vttablet/tabletmanager/vreplication" "vitess.io/vitess/go/vt/vttablet/tabletserver" + "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "vitess.io/vitess/go/vt/vttablet/tabletservermock" querypb "vitess.io/vitess/go/vt/proto/query" @@ -250,7 +251,7 @@ func NewActionAgent( mysqld mysqlctl.MysqlDaemon, queryServiceControl tabletserver.Controller, tabletAlias *topodatapb.TabletAlias, - dbcfgs *dbconfigs.DBConfigs, + config *tabletenv.TabletConfig, mycnf *mysqlctl.Mycnf, port, gRPCPort int32, ) (agent *ActionAgent, err error) { @@ -297,7 +298,7 @@ func NewActionAgent( var mysqlHost string var mysqlPort int32 - if appConfig, _ := dbcfgs.AppWithDB().MysqlParams(); appConfig.Host != "" { + if appConfig, _ := config.DB.AppWithDB().MysqlParams(); appConfig.Host != "" { mysqlHost = appConfig.Host mysqlPort = int32(appConfig.Port) @@ -316,19 +317,14 @@ func NewActionAgent( } // Start will get the tablet info, and update our state from it - if err := agent.Start(batchCtx, dbcfgs, mysqlHost, int32(mysqlPort), port, gRPCPort, true); err != nil { + if err := agent.Start(batchCtx, config.DB, mysqlHost, int32(mysqlPort), port, gRPCPort, true); err != nil { return nil, err } vreplication.InitVStreamerClient(agent.DBConfigs) // The db name is set by the Start function called above - filteredWithDBParams, _ := agent.DBConfigs.FilteredWithDB().MysqlParams() - agent.VREngine = vreplication.NewEngine(ts, tabletAlias.Cell, mysqld, func() binlogplayer.DBClient { - return binlogplayer.NewDBClient(agent.DBConfigs.FilteredWithDB()) - }, - filteredWithDBParams.DbName, - ) + agent.VREngine = vreplication.NewEngine(config, ts, tabletAlias.Cell, mysqld) servenv.OnTerm(agent.VREngine.Close) // Run a background task to rebuild the SrvKeyspace in our cell/keyspace @@ -418,7 +414,7 @@ func NewTestActionAgent(batchCtx context.Context, ts *topo.Server, tabletAlias * TabletAlias: tabletAlias, Cnf: nil, MysqlDaemon: mysqlDaemon, - VREngine: vreplication.NewEngine(ts, tabletAlias.Cell, mysqlDaemon, binlogplayer.NewFakeDBClient, ti.DbName()), + VREngine: vreplication.NewTestEngine(ts, tabletAlias.Cell, mysqlDaemon, binlogplayer.NewFakeDBClient, ti.DbName()), History: history.New(historyLength), DemoteMasterType: demoteMasterTabletType, _healthy: fmt.Errorf("healthcheck not run yet"), @@ -462,7 +458,7 @@ func NewComboActionAgent(batchCtx context.Context, ts *topo.Server, tabletAlias TabletAlias: tabletAlias, Cnf: nil, MysqlDaemon: mysqlDaemon, - VREngine: vreplication.NewEngine(nil, "", nil, nil, ""), + VREngine: vreplication.NewTestEngine(nil, "", nil, nil, ""), gotMysqlPort: true, History: history.New(historyLength), DemoteMasterType: demoteMasterType, @@ -683,8 +679,8 @@ func (agent *ActionAgent) Start(ctx context.Context, dbcfgs *dbconfigs.DBConfigs // Verify the topology is correct. agent.verifyTopology(ctx) - dbname := topoproto.TabletDbName(agent.initialTablet) - agent.DBConfigs = dbcfgs.WithDBName(dbname) + dbcfgs.DBName = topoproto.TabletDbName(agent.initialTablet) + agent.DBConfigs = dbcfgs // Create and register the RPC services from UpdateStream. // (it needs the dbname, so it has to be delayed up to here, diff --git a/go/vt/vttablet/tabletmanager/init_tablet_test.go b/go/vt/vttablet/tabletmanager/init_tablet_test.go index 409fb6d77c9..150122e9b0e 100644 --- a/go/vt/vttablet/tabletmanager/init_tablet_test.go +++ b/go/vt/vttablet/tabletmanager/init_tablet_test.go @@ -182,7 +182,7 @@ func TestInitTablet(t *testing.T) { TabletAlias: tabletAlias, MysqlDaemon: mysqlDaemon, DBConfigs: &dbconfigs.DBConfigs{}, - VREngine: vreplication.NewEngine(nil, "", nil, nil, ""), + VREngine: vreplication.NewTestEngine(nil, "", nil, nil, ""), batchCtx: ctx, History: history.New(historyLength), _healthy: fmt.Errorf("healthcheck not run yet"), diff --git a/go/vt/vttablet/tabletmanager/rpc_lock_tables.go b/go/vt/vttablet/tabletmanager/rpc_lock_tables.go index b81d78cd7c1..05306615d7a 100644 --- a/go/vt/vttablet/tabletmanager/rpc_lock_tables.go +++ b/go/vt/vttablet/tabletmanager/rpc_lock_tables.go @@ -102,7 +102,7 @@ func (agent *ActionAgent) lockTablesUsingLockTables(conn *dbconnpool.DBConnectio tableNames = append(tableNames, fmt.Sprintf("%s READ", sqlescape.EscapeID(name))) } lockStatement := fmt.Sprintf("LOCK TABLES %v", strings.Join(tableNames, ", ")) - _, err := conn.ExecuteFetch(fmt.Sprintf("USE %s", agent.DBConfigs.DBName()), 0, false) + _, err := conn.ExecuteFetch(fmt.Sprintf("USE %s", agent.DBConfigs.DBName), 0, false) if err != nil { return err } diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine.go b/go/vt/vttablet/tabletmanager/vreplication/engine.go index 1c77300455c..0fdbf92d431 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine.go @@ -25,6 +25,7 @@ import ( "time" "vitess.io/vitess/go/vt/vtgate/evalengine" + "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" "golang.org/x/net/context" "vitess.io/vitess/go/mysql" @@ -98,14 +99,31 @@ type journalEvent struct { // NewEngine creates a new Engine. // A nil ts means that the Engine is disabled. -func NewEngine(ts *topo.Server, cell string, mysqld mysqlctl.MysqlDaemon, dbClientFactory func() binlogplayer.DBClient, dbName string) *Engine { +func NewEngine(config *tabletenv.TabletConfig, ts *topo.Server, cell string, mysqld mysqlctl.MysqlDaemon) *Engine { + dbClientFactory := func() binlogplayer.DBClient { + return binlogplayer.NewDBClient(config.DB.FilteredWithDB()) + } + vre := &Engine{ + controllers: make(map[int]*controller), + ts: ts, + cell: cell, + mysqld: mysqld, + dbClientFactory: dbClientFactory, + dbName: config.DB.DBName, + journaler: make(map[string]*journalEvent), + } + return vre +} + +// NewTestEngine creates a new Engine for testing. +func NewTestEngine(ts *topo.Server, cell string, mysqld mysqlctl.MysqlDaemon, dbClientFactory func() binlogplayer.DBClient, dbname string) *Engine { vre := &Engine{ controllers: make(map[int]*controller), ts: ts, cell: cell, mysqld: mysqld, dbClientFactory: dbClientFactory, - dbName: dbName, + dbName: dbname, journaler: make(map[string]*journalEvent), } return vre diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine_test.go b/go/vt/vttablet/tabletmanager/vreplication/engine_test.go index 0d1981f8246..3374916c39c 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine_test.go @@ -41,7 +41,7 @@ func TestEngineOpen(t *testing.T) { // Test Insert - vre := NewEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) if vre.IsOpen() { t.Errorf("IsOpen: %v, want false", vre.IsOpen()) } @@ -89,7 +89,7 @@ func TestEngineExec(t *testing.T) { // Test Insert - vre := NewEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := vre.Open(context.Background()); err != nil { @@ -249,7 +249,7 @@ func TestEngineBadInsert(t *testing.T) { dbClientFactory := func() binlogplayer.DBClient { return dbClient } mysqld := &fakemysqldaemon.FakeMysqlDaemon{MysqlPort: 3306} - vre := NewEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := vre.Open(context.Background()); err != nil { @@ -279,7 +279,7 @@ func TestEngineSelect(t *testing.T) { dbClientFactory := func() binlogplayer.DBClient { return dbClient } mysqld := &fakemysqldaemon.FakeMysqlDaemon{MysqlPort: 3306} - vre := NewEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := vre.Open(context.Background()); err != nil { @@ -314,7 +314,7 @@ func TestWaitForPos(t *testing.T) { dbClient := binlogplayer.NewMockDBClient(t) mysqld := &fakemysqldaemon.FakeMysqlDaemon{MysqlPort: 3306} dbClientFactory := func() binlogplayer.DBClient { return dbClient } - vre := NewEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := vre.Open(context.Background()); err != nil { @@ -344,7 +344,7 @@ func TestWaitForPosError(t *testing.T) { dbClient := binlogplayer.NewMockDBClient(t) mysqld := &fakemysqldaemon.FakeMysqlDaemon{MysqlPort: 3306} dbClientFactory := func() binlogplayer.DBClient { return dbClient } - vre := NewEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) err := vre.WaitForPos(context.Background(), 1, "MariaDB/0-1-1084") want := `vreplication engine is closed` @@ -386,7 +386,7 @@ func TestWaitForPosCancel(t *testing.T) { dbClient := binlogplayer.NewMockDBClient(t) mysqld := &fakemysqldaemon.FakeMysqlDaemon{MysqlPort: 3306} dbClientFactory := func() binlogplayer.DBClient { return dbClient } - vre := NewEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := vre.Open(context.Background()); err != nil { @@ -434,7 +434,7 @@ func TestCreateDBAndTable(t *testing.T) { // Test Insert - vre := NewEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) tableNotFound := mysql.SQLError{Num: 1146, Message: "table not found"} dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", nil, &tableNotFound) diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index 40072569e30..9cb7be471d4 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -103,7 +103,7 @@ func TestMain(m *testing.M) { InitVStreamerClient(env.Dbcfgs) - playerEngine = NewEngine(env.TopoServ, env.Cells[0], env.Mysqld, realDBClientFactory, vrepldb) + playerEngine = NewTestEngine(env.TopoServ, env.Cells[0], env.Mysqld, realDBClientFactory, vrepldb) if err := playerEngine.Open(context.Background()); err != nil { fmt.Fprintf(os.Stderr, "%v", err) return 1 diff --git a/go/vt/vttablet/tabletserver/tabletenv/config.go b/go/vt/vttablet/tabletserver/tabletenv/config.go index 189b795715a..d8611ff1746 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/config.go +++ b/go/vt/vttablet/tabletserver/tabletenv/config.go @@ -190,22 +190,26 @@ func Init() { type TabletConfig struct { DB *dbconfigs.DBConfigs `json:"db,omitempty"` - OltpReadPool ConnPoolConfig `json:"oltpReadPool,omitempty"` - OlapReadPool ConnPoolConfig `json:"olapReadPool,omitempty"` - TxPool ConnPoolConfig `json:"txPool,omitempty"` - Oltp OltpConfig `json:"oltp,omitempty"` - HotRowProtection HotRowProtectionConfig `json:"hotRowProtection,omitempty"` - Consolidator string `json:"consolidator,omitempty"` - HeartbeatIntervalMilliseconds int `json:"heartbeatIntervalMilliseconds,omitempty"` - ShutdownGracePeriodSeconds int `json:"shutdownGracePeriodSeconds,omitempty"` - PassthroughDML bool `json:"passthroughDML,omitempty"` - StreamBufferSize int `json:"streamBufferSize,omitempty"` - QueryCacheSize int `json:"queryCacheSize,omitempty"` - SchemaReloadIntervalSeconds int `json:"schemaReloadIntervalSeconds,omitempty"` - WatchReplication bool `json:"watchReplication,omitempty"` - TerseErrors bool `json:"terseErrors,omitempty"` - MessagePostponeParallelism int `json:"messagePostponeParallelism,omitempty"` - CacheResultFields bool `json:"cacheResultFields,omitempty"` + OltpReadPool ConnPoolConfig `json:"oltpReadPool,omitempty"` + OlapReadPool ConnPoolConfig `json:"olapReadPool,omitempty"` + TxPool ConnPoolConfig `json:"txPool,omitempty"` + + Oltp OltpConfig `json:"oltp,omitempty"` + HotRowProtection HotRowProtectionConfig `json:"hotRowProtection,omitempty"` + + Consolidator string `json:"consolidator,omitempty"` + HeartbeatIntervalMilliseconds int `json:"heartbeatIntervalMilliseconds,omitempty"` + ShutdownGracePeriodSeconds int `json:"shutdownGracePeriodSeconds,omitempty"` + PassthroughDML bool `json:"passthroughDML,omitempty"` + StreamBufferSize int `json:"streamBufferSize,omitempty"` + QueryCacheSize int `json:"queryCacheSize,omitempty"` + SchemaReloadIntervalSeconds int `json:"schemaReloadIntervalSeconds,omitempty"` + WatchReplication bool `json:"watchReplication,omitempty"` + TerseErrors bool `json:"terseErrors,omitempty"` + MessagePostponeParallelism int `json:"messagePostponeParallelism,omitempty"` + CacheResultFields bool `json:"cacheResultFields,omitempty"` + + ExternalConnections map[string]*dbconfigs.DBConfigs `json:"externalConnections,omitempty"` StrictTableACL bool `json:"-"` EnableTableACLDryRun bool `json:"-"` diff --git a/go/vt/wrangler/testlib/migrate_served_from_test.go b/go/vt/wrangler/testlib/migrate_served_from_test.go index 22912050631..7ac80937876 100644 --- a/go/vt/wrangler/testlib/migrate_served_from_test.go +++ b/go/vt/wrangler/testlib/migrate_served_from_test.go @@ -106,7 +106,7 @@ func TestMigrateServedFrom(t *testing.T) { // Override with a fake VREngine after Agent is initialized in action loop. dbClient := binlogplayer.NewMockDBClient(t) dbClientFactory := func() binlogplayer.DBClient { return dbClient } - destMaster.Agent.VREngine = vreplication.NewEngine(ts, "", destMaster.FakeMysqlDaemon, dbClientFactory, dbClient.DBName()) + destMaster.Agent.VREngine = vreplication.NewTestEngine(ts, "", destMaster.FakeMysqlDaemon, dbClientFactory, dbClient.DBName()) dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := destMaster.Agent.VREngine.Open(context.Background()); err != nil { t.Fatal(err) diff --git a/go/vt/wrangler/testlib/migrate_served_types_test.go b/go/vt/wrangler/testlib/migrate_served_types_test.go index c7debf1cc6d..53b8a88a041 100644 --- a/go/vt/wrangler/testlib/migrate_served_types_test.go +++ b/go/vt/wrangler/testlib/migrate_served_types_test.go @@ -153,7 +153,7 @@ func TestMigrateServedTypes(t *testing.T) { // Override with a fake VREngine after Agent is initialized in action loop. dbClient1 := binlogplayer.NewMockDBClient(t) dbClientFactory1 := func() binlogplayer.DBClient { return dbClient1 } - dest1Master.Agent.VREngine = vreplication.NewEngine(ts, "", dest1Master.FakeMysqlDaemon, dbClientFactory1, dbClient1.DBName()) + dest1Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest1Master.FakeMysqlDaemon, dbClientFactory1, dbClient1.DBName()) // select * from _vt.vreplication during Open dbClient1.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := dest1Master.Agent.VREngine.Open(context.Background()); err != nil { @@ -181,7 +181,7 @@ func TestMigrateServedTypes(t *testing.T) { // Override with a fake VREngine after Agent is initialized in action loop. dbClient2 := binlogplayer.NewMockDBClient(t) dbClientFactory2 := func() binlogplayer.DBClient { return dbClient2 } - dest2Master.Agent.VREngine = vreplication.NewEngine(ts, "", dest2Master.FakeMysqlDaemon, dbClientFactory2, dbClient2.DBName()) + dest2Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest2Master.FakeMysqlDaemon, dbClientFactory2, dbClient2.DBName()) // select * from _vt.vreplication during Open dbClient2.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := dest2Master.Agent.VREngine.Open(context.Background()); err != nil { @@ -417,7 +417,7 @@ func TestMultiShardMigrateServedTypes(t *testing.T) { // Override with a fake VREngine after Agent is initialized in action loop. dbClient1 := binlogplayer.NewMockDBClient(t) dbClientFactory1 := func() binlogplayer.DBClient { return dbClient1 } - dest1Master.Agent.VREngine = vreplication.NewEngine(ts, "", dest1Master.FakeMysqlDaemon, dbClientFactory1, "db") + dest1Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest1Master.FakeMysqlDaemon, dbClientFactory1, "db") // select * from _vt.vreplication during Open dbClient1.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := dest1Master.Agent.VREngine.Open(context.Background()); err != nil { @@ -434,7 +434,7 @@ func TestMultiShardMigrateServedTypes(t *testing.T) { // Override with a fake VREngine after Agent is initialized in action loop. dbClient2 := binlogplayer.NewMockDBClient(t) dbClientFactory2 := func() binlogplayer.DBClient { return dbClient2 } - dest2Master.Agent.VREngine = vreplication.NewEngine(ts, "", dest2Master.FakeMysqlDaemon, dbClientFactory2, "db") + dest2Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest2Master.FakeMysqlDaemon, dbClientFactory2, "db") // select * from _vt.vreplication during Open dbClient2.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := dest2Master.Agent.VREngine.Open(context.Background()); err != nil { @@ -505,7 +505,7 @@ func TestMultiShardMigrateServedTypes(t *testing.T) { // Override with a fake VREngine after Agent is initialized in action loop. dbClient1 = binlogplayer.NewMockDBClient(t) dbClientFactory1 = func() binlogplayer.DBClient { return dbClient1 } - dest3Master.Agent.VREngine = vreplication.NewEngine(ts, "", dest3Master.FakeMysqlDaemon, dbClientFactory1, "db") + dest3Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest3Master.FakeMysqlDaemon, dbClientFactory1, "db") // select * from _vt.vreplication during Open dbClient1.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := dest3Master.Agent.VREngine.Open(context.Background()); err != nil { @@ -522,7 +522,7 @@ func TestMultiShardMigrateServedTypes(t *testing.T) { // Override with a fake VREngine after Agent is initialized in action loop. dbClient2 = binlogplayer.NewMockDBClient(t) dbClientFactory2 = func() binlogplayer.DBClient { return dbClient2 } - dest4Master.Agent.VREngine = vreplication.NewEngine(ts, "", dest4Master.FakeMysqlDaemon, dbClientFactory2, "db") + dest4Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest4Master.FakeMysqlDaemon, dbClientFactory2, "db") // select * from _vt.vreplication during Open dbClient2.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := dest4Master.Agent.VREngine.Open(context.Background()); err != nil { diff --git a/go/vt/wrangler/traffic_switcher_env_test.go b/go/vt/wrangler/traffic_switcher_env_test.go index 5a1a2c47756..653c8c13810 100644 --- a/go/vt/wrangler/traffic_switcher_env_test.go +++ b/go/vt/wrangler/traffic_switcher_env_test.go @@ -336,7 +336,7 @@ func (tme *testMigraterEnv) createDBClients(ctx context.Context, t *testing.T) { dbClientFactory := func() binlogplayer.DBClient { return dbclient } // Replace existing engine with a new one master.Agent.VREngine.Close() - master.Agent.VREngine = vreplication.NewEngine(tme.ts, "", master.FakeMysqlDaemon, dbClientFactory, dbclient.DBName()) + master.Agent.VREngine = vreplication.NewTestEngine(tme.ts, "", master.FakeMysqlDaemon, dbClientFactory, dbclient.DBName()) if err := master.Agent.VREngine.Open(ctx); err != nil { t.Fatal(err) } @@ -347,7 +347,7 @@ func (tme *testMigraterEnv) createDBClients(ctx context.Context, t *testing.T) { dbClientFactory := func() binlogplayer.DBClient { return dbclient } // Replace existing engine with a new one master.Agent.VREngine.Close() - master.Agent.VREngine = vreplication.NewEngine(tme.ts, "", master.FakeMysqlDaemon, dbClientFactory, dbclient.DBName()) + master.Agent.VREngine = vreplication.NewTestEngine(tme.ts, "", master.FakeMysqlDaemon, dbClientFactory, dbclient.DBName()) if err := master.Agent.VREngine.Open(ctx); err != nil { t.Fatal(err) } From c6239ca78e7db9d9b9fbe808a212ae727f5d9c3a Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 19 Apr 2020 19:05:37 -0700 Subject: [PATCH 05/16] vrepl: externalConnector: cache Signed-off-by: Sugu Sougoumarane --- go/cmd/vtcombo/main.go | 6 +- go/cmd/vttablet/vttablet.go | 8 +- go/vt/dbconfigs/dbconfigs.go | 7 +- go/vt/dbconfigs/dbconfigs_test.go | 30 ++-- go/vt/mysqlctl/cmd.go | 8 +- go/vt/mysqlctl/mycnf_test.go | 4 +- .../tabletmanager/vreplication/controller.go | 11 +- .../tabletmanager/vreplication/engine.go | 4 + .../vreplication/external_connector.go | 155 ++++++++++++++++++ .../tabletmanager/vreplication/vcopier.go | 6 - .../tabletmanager/vreplication/vplayer.go | 6 - .../vreplication/vstreamer_client.go | 8 +- .../vttablet/tabletserver/vstreamer/engine.go | 9 + 13 files changed, 210 insertions(+), 52 deletions(-) create mode 100644 go/vt/vttablet/tabletmanager/vreplication/external_connector.go diff --git a/go/cmd/vtcombo/main.go b/go/cmd/vtcombo/main.go index 232865bf7bf..518ff88cf4b 100644 --- a/go/cmd/vtcombo/main.go +++ b/go/cmd/vtcombo/main.go @@ -97,13 +97,13 @@ func main() { servenv.Init() tabletenv.Init() - dbcfgs := dbconfigs.GlobalDBConfigs.Init("") - mysqld := mysqlctl.NewMysqld(dbcfgs) + dbconfigs.GlobalDBConfigs.Init("") + mysqld := mysqlctl.NewMysqld(&dbconfigs.GlobalDBConfigs) servenv.OnClose(mysqld.Close) // tablets configuration and init. // Send mycnf as nil because vtcombo won't do backups and restores. - if err := vtcombo.InitTabletMap(ts, tpb, mysqld, dbcfgs, *schemaDir, nil); err != nil { + if err := vtcombo.InitTabletMap(ts, tpb, mysqld, &dbconfigs.GlobalDBConfigs, *schemaDir, nil); err != nil { log.Errorf("initTabletMapProto failed: %v", err) exit.Return(1) } diff --git a/go/cmd/vttablet/vttablet.go b/go/cmd/vttablet/vttablet.go index 331d08500cd..577e6c833b9 100644 --- a/go/cmd/vttablet/vttablet.go +++ b/go/cmd/vttablet/vttablet.go @@ -56,12 +56,13 @@ func main() { servenv.ParseFlags("vttablet") + tabletenv.Init() + // Load current config after tabletenv.Init, because it changes it. config := tabletenv.NewCurrentConfig() if err := config.Verify(); err != nil { log.Exitf("invalid config: %v", err) } - tabletenv.Init() if *tabletConfig != "" { bytes, err := ioutil.ReadFile(*tabletConfig) if err != nil { @@ -103,7 +104,10 @@ func main() { // If connection parameters were specified, socketFile will be empty. // Otherwise, the socketFile (read from mycnf) will be used to initialize // dbconfigs. - config.DB = config.DB.Init(socketFile) + config.DB.Init(socketFile) + for _, cfg := range config.ExternalConnections { + cfg.Init("") + } if *tableACLConfig != "" { // To override default simpleacl, other ACL plugins must set themselves to be default ACL factory diff --git a/go/vt/dbconfigs/dbconfigs.go b/go/vt/dbconfigs/dbconfigs.go index dea2269fa2f..f42d4b47ebf 100644 --- a/go/vt/dbconfigs/dbconfigs.go +++ b/go/vt/dbconfigs/dbconfigs.go @@ -79,6 +79,7 @@ type DBConfigs struct { SslKey string `json:"sslKey,omitempty"` ServerName string `json:"serverName,omitempty"` ConnectTimeoutMilliseconds int `json:"connectTimeoutMilliseconds,omitempty"` + DBName string `json:"dbName,omitempty"` App UserConfig `json:"app,omitempty"` Dba UserConfig `json:"dba,omitempty"` @@ -95,8 +96,6 @@ type DBConfigs struct { appdebugParams mysql.ConnParams allprivsParams mysql.ConnParams externalReplParams mysql.ConnParams - - DBName string `json:"-"` } // UserConfig contains user-specific configs. @@ -321,8 +320,7 @@ func (dbcfgs *DBConfigs) Clone() *DBConfigs { // parameters. This is only for legacy support. // If no per-user parameters are supplied, then the defaultSocketFile // is used to initialize the per-user conn params. -func (dbcfgs *DBConfigs) Init(defaultSocketFile string) *DBConfigs { - dbcfgs = dbcfgs.Clone() +func (dbcfgs *DBConfigs) Init(defaultSocketFile string) { for _, userKey := range All { uc, cp := dbcfgs.getParams(userKey, dbcfgs) // TODO @rafael: For ExternalRepl we need to respect the provided host / port @@ -363,7 +361,6 @@ func (dbcfgs *DBConfigs) Init(defaultSocketFile string) *DBConfigs { } log.Infof("DBConfigs: %v\n", dbcfgs.String()) - return dbcfgs } func (dbcfgs *DBConfigs) getParams(userKey string, dbc *DBConfigs) (*UserConfig, *mysql.ConnParams) { diff --git a/go/vt/dbconfigs/dbconfigs_test.go b/go/vt/dbconfigs/dbconfigs_test.go index 3d8d9366546..bb61246a6c9 100644 --- a/go/vt/dbconfigs/dbconfigs_test.go +++ b/go/vt/dbconfigs/dbconfigs_test.go @@ -35,10 +35,10 @@ func TestInit(t *testing.T) { appParams: mysql.ConnParams{UnixSocket: "socket"}, dbaParams: mysql.ConnParams{Host: "host"}, } - dbc := dbConfigs.Init("default") - assert.Equal(t, mysql.ConnParams{UnixSocket: "socket"}, dbc.appParams) - assert.Equal(t, mysql.ConnParams{Host: "host"}, dbc.dbaParams) - assert.Equal(t, mysql.ConnParams{UnixSocket: "default"}, dbc.appdebugParams) + dbConfigs.Init("default") + assert.Equal(t, mysql.ConnParams{UnixSocket: "socket"}, dbConfigs.appParams) + assert.Equal(t, mysql.ConnParams{Host: "host"}, dbConfigs.dbaParams) + assert.Equal(t, mysql.ConnParams{UnixSocket: "default"}, dbConfigs.appdebugParams) dbConfigs = DBConfigs{ Host: "a", @@ -71,7 +71,7 @@ func TestInit(t *testing.T) { Host: "host", }, } - dbc = dbConfigs.Init("default") + dbConfigs.Init("default") want := mysql.ConnParams{ Host: "a", @@ -84,7 +84,7 @@ func TestInit(t *testing.T) { Flavor: "flavor", ConnectTimeoutMs: 250, } - assert.Equal(t, want, dbc.appParams) + assert.Equal(t, want, dbConfigs.appParams) want = mysql.ConnParams{ Host: "a", @@ -99,7 +99,7 @@ func TestInit(t *testing.T) { SslKey: "g", ConnectTimeoutMs: 250, } - assert.Equal(t, want, dbc.appdebugParams) + assert.Equal(t, want, dbConfigs.appdebugParams) want = mysql.ConnParams{ Host: "a", Port: 1, @@ -115,7 +115,7 @@ func TestInit(t *testing.T) { SslKey: "g", ConnectTimeoutMs: 250, } - assert.Equal(t, want, dbc.dbaParams) + assert.Equal(t, want, dbConfigs.dbaParams) // Test that baseConfig does not override Charset and Flag if they're // not specified. @@ -148,7 +148,7 @@ func TestInit(t *testing.T) { Flags: 2, }, } - dbc = dbConfigs.Init("default") + dbConfigs.Init("default") want = mysql.ConnParams{ Host: "a", Port: 1, @@ -157,7 +157,7 @@ func TestInit(t *testing.T) { UnixSocket: "b", Charset: "f", } - assert.Equal(t, want, dbc.appParams) + assert.Equal(t, want, dbConfigs.appParams) want = mysql.ConnParams{ Host: "a", Port: 1, @@ -167,7 +167,7 @@ func TestInit(t *testing.T) { SslCert: "f", SslKey: "g", } - assert.Equal(t, want, dbc.appdebugParams) + assert.Equal(t, want, dbConfigs.appdebugParams) want = mysql.ConnParams{ Host: "a", Port: 1, @@ -180,7 +180,7 @@ func TestInit(t *testing.T) { SslCert: "f", SslKey: "g", } - assert.Equal(t, want, dbc.dbaParams) + assert.Equal(t, want, dbConfigs.dbaParams) } func TestUseTCP(t *testing.T) { @@ -196,14 +196,14 @@ func TestUseTCP(t *testing.T) { User: "dba", }, } - dbc := dbConfigs.Init("default") + dbConfigs.Init("default") want := mysql.ConnParams{ Host: "a", Port: 1, Uname: "app", } - assert.Equal(t, want, dbc.appParams) + assert.Equal(t, want, dbConfigs.appParams) want = mysql.ConnParams{ Host: "a", @@ -211,7 +211,7 @@ func TestUseTCP(t *testing.T) { Uname: "dba", UnixSocket: "b", } - assert.Equal(t, want, dbc.dbaParams) + assert.Equal(t, want, dbConfigs.dbaParams) } func TestAccessors(t *testing.T) { diff --git a/go/vt/mysqlctl/cmd.go b/go/vt/mysqlctl/cmd.go index 971f704bc63..ac6b0415164 100644 --- a/go/vt/mysqlctl/cmd.go +++ b/go/vt/mysqlctl/cmd.go @@ -46,8 +46,8 @@ func CreateMysqldAndMycnf(tabletUID uint32, mysqlSocket string, mysqlPort int32) mycnf.SocketFile = mysqlSocket } - dbcfgs := dbconfigs.GlobalDBConfigs.Init(mycnf.SocketFile) - return NewMysqld(dbcfgs), mycnf, nil + dbconfigs.GlobalDBConfigs.Init(mycnf.SocketFile) + return NewMysqld(&dbconfigs.GlobalDBConfigs), mycnf, nil } // OpenMysqldAndMycnf returns a Mysqld and a Mycnf object to use for working with a MySQL @@ -60,6 +60,6 @@ func OpenMysqldAndMycnf(tabletUID uint32) (*Mysqld, *Mycnf, error) { return nil, nil, fmt.Errorf("couldn't read my.cnf file: %v", err) } - dbcfgs := dbconfigs.GlobalDBConfigs.Init(mycnf.SocketFile) - return NewMysqld(dbcfgs), mycnf, nil + dbconfigs.GlobalDBConfigs.Init(mycnf.SocketFile) + return NewMysqld(&dbconfigs.GlobalDBConfigs), mycnf, nil } diff --git a/go/vt/mysqlctl/mycnf_test.go b/go/vt/mysqlctl/mycnf_test.go index e2ee0fcc4d2..2911c0b5b61 100644 --- a/go/vt/mysqlctl/mycnf_test.go +++ b/go/vt/mysqlctl/mycnf_test.go @@ -96,8 +96,8 @@ func NoTestMycnfHook(t *testing.T) { // this is not being passed, so it should be nil os.Setenv("MY_VAR", "myvalue") - dbcfgs := dbconfigs.GlobalDBConfigs.Init(cnf.SocketFile) - mysqld := NewMysqld(dbcfgs) + dbconfigs.GlobalDBConfigs.Init(cnf.SocketFile) + mysqld := NewMysqld(&dbconfigs.GlobalDBConfigs) servenv.OnClose(mysqld.Close) err := mysqld.InitConfig(cnf) diff --git a/go/vt/vttablet/tabletmanager/vreplication/controller.go b/go/vt/vttablet/tabletmanager/vreplication/controller.go index b215edd2771..edd034ef0e2 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/controller.go +++ b/go/vt/vttablet/tabletmanager/vreplication/controller.go @@ -222,11 +222,16 @@ func (ct *controller) runBlp(ctx context.Context) (err error) { } var vsClient VStreamerClient - if ct.source.GetExternalMysql() == "" { - vsClient = NewTabletVStreamerClient(tablet) + var err error + if name := ct.source.GetExternalMysql(); name != "" { + vsClient, err = ct.vre.ec.Get(name) } else { - vsClient = NewMySQLVStreamerClient() + vsClient, err = newTabletConnector(tablet) } + if err != nil { + return err + } + defer vsClient.Close(ctx) vr := newVReplicator(ct.id, &ct.source, vsClient, ct.blpStats, dbClient, ct.mysqld, ct.vre) return vr.Replicate(ctx) diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine.go b/go/vt/vttablet/tabletmanager/vreplication/engine.go index 0fdbf92d431..e7a1907d046 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine.go @@ -89,6 +89,7 @@ type Engine struct { dbName string journaler map[string]*journalEvent + ec *externalConnector } type journalEvent struct { @@ -111,6 +112,7 @@ func NewEngine(config *tabletenv.TabletConfig, ts *topo.Server, cell string, mys dbClientFactory: dbClientFactory, dbName: config.DB.DBName, journaler: make(map[string]*journalEvent), + ec: newExternalConnector(config.ExternalConnections), } return vre } @@ -125,6 +127,7 @@ func NewTestEngine(ts *topo.Server, cell string, mysqld mysqlctl.MysqlDaemon, db dbClientFactory: dbClientFactory, dbName: dbname, journaler: make(map[string]*journalEvent), + ec: newExternalConnector(nil), } return vre } @@ -244,6 +247,7 @@ func (vre *Engine) Close() { } log.Infof("Shutting down VReplication engine") + vre.ec.Close() vre.cancel() // We still have to wait for all controllers to stop. for _, ct := range vre.controllers { diff --git a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go new file mode 100644 index 00000000000..86a8c0ba76b --- /dev/null +++ b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go @@ -0,0 +1,155 @@ +/* +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 vreplication + +import ( + "sync" + + "golang.org/x/net/context" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/dbconfigs" + "vitess.io/vitess/go/vt/grpcclient" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" + querypb "vitess.io/vitess/go/vt/proto/query" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/vterrors" + "vitess.io/vitess/go/vt/vttablet/queryservice" + "vitess.io/vitess/go/vt/vttablet/tabletconn" + "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 externalConnector struct { + mu sync.Mutex + dbconfigs map[string]*dbconfigs.DBConfigs + connectors map[string]*mysqlConnector +} + +func newExternalConnector(dbcfgs map[string]*dbconfigs.DBConfigs) *externalConnector { + return &externalConnector{ + dbconfigs: dbcfgs, + connectors: make(map[string]*mysqlConnector), + } +} + +func (ec *externalConnector) Close() { + for _, c := range ec.connectors { + c.shutdown() + } + ec.connectors = make(map[string]*mysqlConnector) +} + +func (ec *externalConnector) Get(name string) (*mysqlConnector, error) { + ec.mu.Lock() + defer ec.mu.Unlock() + if c, ok := ec.connectors[name]; ok { + return c, nil + } + + // Construct + config := tabletenv.NewDefaultConfig() + config.DB = ec.dbconfigs[name] + if config.DB == nil { + return nil, vterrors.Errorf(vtrpcpb.Code_NOT_FOUND, "external mysqlConnector %v not found", name) + } + c := &mysqlConnector{} + c.env = tabletenv.NewTestEnv(config, nil, name) + c.se = schema.NewEngine(c.env) + c.vstreamer = vstreamer.NewEngine(c.env, nil, c.se) + c.se.InitDBConfig(c.env.DBConfigs().DbaWithDB()) + + // Open + if err := c.se.Open(); err != nil { + return nil, vterrors.Wrapf(err, "external mysqlConnector: %v", name) + } + c.vstreamer.Open("", "") + + // Register + ec.connectors[name] = c + return c, nil +} + +//----------------------------------------------------------- + +type mysqlConnector struct { + env tabletenv.Env + se *schema.Engine + vstreamer *vstreamer.Engine +} + +func (c *mysqlConnector) shutdown() { + c.vstreamer.Close() + c.se.Close() +} + +func (c *mysqlConnector) Close(ctx context.Context) { +} + +func (c *mysqlConnector) VStream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { + return c.vstreamer.Stream(ctx, startPos, filter, send) +} + +func (c *mysqlConnector) VStreamRows(ctx context.Context, query string, lastpk *querypb.QueryResult, send func(*binlogdatapb.VStreamRowsResponse) error) error { + var row []sqltypes.Value + if lastpk != nil { + r := sqltypes.Proto3ToResult(lastpk) + if len(r.Rows) != 1 { + return vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "unexpected lastpk input: %v", lastpk) + } + row = r.Rows[0] + } + return c.vstreamer.StreamRows(ctx, query, row, send) +} + +//----------------------------------------------------------- + +type tabletConnector struct { + tablet *topodatapb.Tablet + target *querypb.Target + qs queryservice.QueryService +} + +func newTabletConnector(tablet *topodatapb.Tablet) (*tabletConnector, error) { + tc := &tabletConnector{ + tablet: tablet, + target: &querypb.Target{ + Keyspace: tablet.Keyspace, + Shard: tablet.Shard, + TabletType: tablet.Type, + }, + } + qs, err := tabletconn.GetDialer()(tc.tablet, grpcclient.FailFast(true)) + if err != nil { + return nil, err + } + tc.qs = qs + return tc, nil +} + +func (tc *tabletConnector) Close(ctx context.Context) { + tc.qs.Close(ctx) +} + +func (tc *tabletConnector) VStream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { + return tc.qs.VStream(ctx, tc.target, startPos, filter, send) +} + +func (tc *tabletConnector) VStreamRows(ctx context.Context, query string, lastpk *querypb.QueryResult, send func(*binlogdatapb.VStreamRowsResponse) error) error { + return tc.qs.VStreamRows(ctx, tc.target, query, lastpk, send) +} diff --git a/go/vt/vttablet/tabletmanager/vreplication/vcopier.go b/go/vt/vttablet/tabletmanager/vreplication/vcopier.go index c0f9cc693b6..088b0160f55 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vcopier.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vcopier.go @@ -201,12 +201,6 @@ func (vc *vcopier) copyTable(ctx context.Context, tableName string, copyState ma return fmt.Errorf("plan not found for table: %s, current plans are: %#v", tableName, plan.TargetTables) } - err = vc.vr.sourceVStreamer.Open(ctx) - if err != nil { - return fmt.Errorf("error opening vsclient: %v", err) - } - defer vc.vr.sourceVStreamer.Close(ctx) - ctx, cancel := context.WithTimeout(ctx, copyTimeout) defer cancel() diff --git a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go index 536c71aa89a..3ed3c0f7ee8 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vplayer.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vplayer.go @@ -129,12 +129,6 @@ func (vp *vplayer) play(ctx context.Context) error { func (vp *vplayer) fetchAndApply(ctx context.Context) (err error) { log.Infof("Starting VReplication player id: %v, startPos: %v, stop: %v, filter: %v", vp.vr.id, vp.startPos, vp.stopPos, vp.vr.source) - err = vp.vr.sourceVStreamer.Open(ctx) - if err != nil { - return fmt.Errorf("error creating vstreamer client: %v", err) - } - defer vp.vr.sourceVStreamer.Close(ctx) - ctx, cancel := context.WithCancel(ctx) defer cancel() diff --git a/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go b/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go index 309d901ef55..7bbbe7a6409 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go @@ -38,17 +38,13 @@ import ( ) var ( - _ VStreamerClient = (*TabletVStreamerClient)(nil) - _ VStreamerClient = (*MySQLVStreamerClient)(nil) + //_ VStreamerClient = (*TabletVStreamerClient)(nil) dbcfgs *dbconfigs.DBConfigs ) // VStreamerClient exposes the core interface of a vstreamer type VStreamerClient interface { - // Open sets up all the environment for a vstream - Open(ctx context.Context) error - // Close closes a vstream - Close(ctx context.Context) error + Close(context.Context) // VStream streams VReplication events based on the specified filter. VStream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error diff --git a/go/vt/vttablet/tabletserver/vstreamer/engine.go b/go/vt/vttablet/tabletserver/vstreamer/engine.go index e3f313c4b32..7b62717a162 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/engine.go +++ b/go/vt/vttablet/tabletserver/vstreamer/engine.go @@ -277,6 +277,15 @@ func (vse *Engine) ServeHTTP(response http.ResponseWriter, request *http.Request } func (vse *Engine) setWatch() { + // If there's no toposerver, create an empty vschema. + if vse.ts == nil { + vse.lvschema = &localVSchema{ + keyspace: vse.keyspace, + vschema: &vindexes.VSchema{}, + } + return + } + // WatchSrvVSchema does not return until the inner func has been called at least once. vse.ts.WatchSrvVSchema(context.TODO(), vse.cell, func(v *vschemapb.SrvVSchema, err error) { switch { From 929e0e6eef6add4d983f3be0b682eed2cafa1ca0 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 19 Apr 2020 20:24:39 -0700 Subject: [PATCH 06/16] vrepl: tests for externalConnector Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletmanager/action_agent.go | 4 +- .../tabletmanager/init_tablet_test.go | 2 +- .../tabletmanager/vreplication/engine.go | 5 +- .../tabletmanager/vreplication/engine_test.go | 16 +- .../vreplication/external_connector.go | 2 +- .../vreplication/external_connector_test.go | 157 ++++++++++++++++++ .../vreplication/framework_test.go | 7 +- .../tabletmanager/vreplication/vreplicator.go | 5 + .../testlib/migrate_served_from_test.go | 2 +- .../testlib/migrate_served_types_test.go | 12 +- go/vt/wrangler/traffic_switcher_env_test.go | 4 +- 11 files changed, 192 insertions(+), 24 deletions(-) create mode 100644 go/vt/vttablet/tabletmanager/vreplication/external_connector_test.go diff --git a/go/vt/vttablet/tabletmanager/action_agent.go b/go/vt/vttablet/tabletmanager/action_agent.go index d5a9707d95e..cc1a4145c87 100644 --- a/go/vt/vttablet/tabletmanager/action_agent.go +++ b/go/vt/vttablet/tabletmanager/action_agent.go @@ -414,7 +414,7 @@ func NewTestActionAgent(batchCtx context.Context, ts *topo.Server, tabletAlias * TabletAlias: tabletAlias, Cnf: nil, MysqlDaemon: mysqlDaemon, - VREngine: vreplication.NewTestEngine(ts, tabletAlias.Cell, mysqlDaemon, binlogplayer.NewFakeDBClient, ti.DbName()), + VREngine: vreplication.NewTestEngine(ts, tabletAlias.Cell, mysqlDaemon, binlogplayer.NewFakeDBClient, ti.DbName(), nil), History: history.New(historyLength), DemoteMasterType: demoteMasterTabletType, _healthy: fmt.Errorf("healthcheck not run yet"), @@ -458,7 +458,7 @@ func NewComboActionAgent(batchCtx context.Context, ts *topo.Server, tabletAlias TabletAlias: tabletAlias, Cnf: nil, MysqlDaemon: mysqlDaemon, - VREngine: vreplication.NewTestEngine(nil, "", nil, nil, ""), + VREngine: vreplication.NewTestEngine(nil, "", nil, nil, "", nil), gotMysqlPort: true, History: history.New(historyLength), DemoteMasterType: demoteMasterType, diff --git a/go/vt/vttablet/tabletmanager/init_tablet_test.go b/go/vt/vttablet/tabletmanager/init_tablet_test.go index 150122e9b0e..9936d402133 100644 --- a/go/vt/vttablet/tabletmanager/init_tablet_test.go +++ b/go/vt/vttablet/tabletmanager/init_tablet_test.go @@ -182,7 +182,7 @@ func TestInitTablet(t *testing.T) { TabletAlias: tabletAlias, MysqlDaemon: mysqlDaemon, DBConfigs: &dbconfigs.DBConfigs{}, - VREngine: vreplication.NewTestEngine(nil, "", nil, nil, ""), + VREngine: vreplication.NewTestEngine(nil, "", nil, nil, "", nil), batchCtx: ctx, History: history.New(historyLength), _healthy: fmt.Errorf("healthcheck not run yet"), diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine.go b/go/vt/vttablet/tabletmanager/vreplication/engine.go index e7a1907d046..e5ca360ed4d 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine.go @@ -24,6 +24,7 @@ import ( "sync" "time" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" @@ -118,7 +119,7 @@ func NewEngine(config *tabletenv.TabletConfig, ts *topo.Server, cell string, mys } // NewTestEngine creates a new Engine for testing. -func NewTestEngine(ts *topo.Server, cell string, mysqld mysqlctl.MysqlDaemon, dbClientFactory func() binlogplayer.DBClient, dbname string) *Engine { +func NewTestEngine(ts *topo.Server, cell string, mysqld mysqlctl.MysqlDaemon, dbClientFactory func() binlogplayer.DBClient, dbname string, externalConfig map[string]*dbconfigs.DBConfigs) *Engine { vre := &Engine{ controllers: make(map[int]*controller), ts: ts, @@ -127,7 +128,7 @@ func NewTestEngine(ts *topo.Server, cell string, mysqld mysqlctl.MysqlDaemon, db dbClientFactory: dbClientFactory, dbName: dbname, journaler: make(map[string]*journalEvent), - ec: newExternalConnector(nil), + ec: newExternalConnector(externalConfig), } return vre } diff --git a/go/vt/vttablet/tabletmanager/vreplication/engine_test.go b/go/vt/vttablet/tabletmanager/vreplication/engine_test.go index 3374916c39c..feccaa71b3f 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/engine_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/engine_test.go @@ -41,7 +41,7 @@ func TestEngineOpen(t *testing.T) { // Test Insert - vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName(), nil) if vre.IsOpen() { t.Errorf("IsOpen: %v, want false", vre.IsOpen()) } @@ -89,7 +89,7 @@ func TestEngineExec(t *testing.T) { // Test Insert - vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName(), nil) dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := vre.Open(context.Background()); err != nil { @@ -249,7 +249,7 @@ func TestEngineBadInsert(t *testing.T) { dbClientFactory := func() binlogplayer.DBClient { return dbClient } mysqld := &fakemysqldaemon.FakeMysqlDaemon{MysqlPort: 3306} - vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName(), nil) dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := vre.Open(context.Background()); err != nil { @@ -279,7 +279,7 @@ func TestEngineSelect(t *testing.T) { dbClientFactory := func() binlogplayer.DBClient { return dbClient } mysqld := &fakemysqldaemon.FakeMysqlDaemon{MysqlPort: 3306} - vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName(), nil) dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := vre.Open(context.Background()); err != nil { @@ -314,7 +314,7 @@ func TestWaitForPos(t *testing.T) { dbClient := binlogplayer.NewMockDBClient(t) mysqld := &fakemysqldaemon.FakeMysqlDaemon{MysqlPort: 3306} dbClientFactory := func() binlogplayer.DBClient { return dbClient } - vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName(), nil) dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := vre.Open(context.Background()); err != nil { @@ -344,7 +344,7 @@ func TestWaitForPosError(t *testing.T) { dbClient := binlogplayer.NewMockDBClient(t) mysqld := &fakemysqldaemon.FakeMysqlDaemon{MysqlPort: 3306} dbClientFactory := func() binlogplayer.DBClient { return dbClient } - vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName(), nil) err := vre.WaitForPos(context.Background(), 1, "MariaDB/0-1-1084") want := `vreplication engine is closed` @@ -386,7 +386,7 @@ func TestWaitForPosCancel(t *testing.T) { dbClient := binlogplayer.NewMockDBClient(t) mysqld := &fakemysqldaemon.FakeMysqlDaemon{MysqlPort: 3306} dbClientFactory := func() binlogplayer.DBClient { return dbClient } - vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName(), nil) dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := vre.Open(context.Background()); err != nil { @@ -434,7 +434,7 @@ func TestCreateDBAndTable(t *testing.T) { // Test Insert - vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName()) + vre := NewTestEngine(env.TopoServ, env.Cells[0], mysqld, dbClientFactory, dbClient.DBName(), nil) tableNotFound := mysql.SQLError{Num: 1146, Message: "table not found"} dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", nil, &tableNotFound) diff --git a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go index 86a8c0ba76b..f98addc4c5b 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go +++ b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go @@ -69,7 +69,7 @@ func (ec *externalConnector) Get(name string) (*mysqlConnector, error) { return nil, vterrors.Errorf(vtrpcpb.Code_NOT_FOUND, "external mysqlConnector %v not found", name) } c := &mysqlConnector{} - c.env = tabletenv.NewTestEnv(config, nil, name) + c.env = tabletenv.NewTestEnv(config, config.DB, name) c.se = schema.NewEngine(c.env) c.vstreamer = vstreamer.NewEngine(c.env, nil, c.se) c.se.InitDBConfig(c.env.DBConfigs().DbaWithDB()) diff --git a/go/vt/vttablet/tabletmanager/vreplication/external_connector_test.go b/go/vt/vttablet/tabletmanager/vreplication/external_connector_test.go new file mode 100644 index 00000000000..ec9bf0fed50 --- /dev/null +++ b/go/vt/vttablet/tabletmanager/vreplication/external_connector_test.go @@ -0,0 +1,157 @@ +/* +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 vreplication + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "vitess.io/vitess/go/vt/binlog/binlogplayer" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" +) + +func TestExternalConnector(t *testing.T) { + execStatements(t, []string{ + "create table tab1(id int, val varbinary(128), primary key(id))", + fmt.Sprintf("create table %s.tab1(id int, val varbinary(128), primary key(id))", vrepldb), + "insert into tab1 values(1, 'a'), (2, 'b')", + + "create table tab2(id int, val varbinary(128), primary key(id))", + fmt.Sprintf("create table %s.tab2(id int, val varbinary(128), primary key(id))", vrepldb), + "insert into tab2 values(1, 'a'), (2, 'b')", + + "create table tab3(id int, val varbinary(128), primary key(id))", + fmt.Sprintf("create table %s.tab3(id int, val varbinary(128), primary key(id))", vrepldb), + "insert into tab3 values(1, 'a'), (2, 'b')", + }) + defer execStatements(t, []string{ + "drop table tab1", + fmt.Sprintf("drop table %s.tab1", vrepldb), + "drop table tab2", + fmt.Sprintf("drop table %s.tab2", vrepldb), + "drop table tab3", + fmt.Sprintf("drop table %s.tab3", vrepldb), + }) + + filter1 := &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "tab1", + Filter: "", + }}, + } + bls1 := &binlogdatapb.BinlogSource{ + ExternalMysql: "exta", + Filter: filter1, + } + cancel1 := startExternalVReplication(t, bls1) + + expectDBClientQueries(t, []string{ + "begin", + "insert into tab1(id,val) values (1,'a'), (2,'b')", + "/update _vt.copy_state", + "commit", + "/delete from _vt.copy_state", + "/update _vt.vreplication set state='Running'", + }) + execStatements(t, []string{"insert into tab1 values(3, 'c')"}) + expectDBClientQueries(t, []string{ + "begin", + "insert into tab1(id,val) values (3,'c')", + "/update _vt.vreplication set pos=", + "commit", + }) + // Cancel immediately so we don't deal with spurious updates. + cancel1() + + // Check that only one connector was created. + assert.Equal(t, 1, len(playerEngine.ec.connectors)) + + filter2 := &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "tab2", + Filter: "", + }}, + } + bls2 := &binlogdatapb.BinlogSource{ + ExternalMysql: "exta", + Filter: filter2, + } + cancel2 := startExternalVReplication(t, bls2) + + expectDBClientQueries(t, []string{ + "begin", + "insert into tab2(id,val) values (1,'a'), (2,'b')", + "/update _vt.copy_state", + "commit", + "/delete from _vt.copy_state", + "/update _vt.vreplication set state='Running'", + }) + cancel2() + + // Check that only one connector was created. + assert.Equal(t, 1, len(playerEngine.ec.connectors)) + + filter3 := &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "tab3", + Filter: "", + }}, + } + bls3 := &binlogdatapb.BinlogSource{ + ExternalMysql: "extb", + Filter: filter3, + } + cancel3 := startExternalVReplication(t, bls3) + + expectDBClientQueries(t, []string{ + "begin", + "insert into tab3(id,val) values (1,'a'), (2,'b')", + "/update _vt.copy_state", + "commit", + "/delete from _vt.copy_state", + "/update _vt.vreplication set state='Running'", + }) + cancel3() + + // Check that there now two connectors. + assert.Equal(t, 2, len(playerEngine.ec.connectors)) +} + +func startExternalVReplication(t *testing.T, bls *binlogdatapb.BinlogSource) (cancelr func()) { + query := binlogplayer.CreateVReplication("test", bls, "", 9223372036854775807, 9223372036854775807, 0, vrepldb) + qr, err := playerEngine.Exec(query) + if err != nil { + t.Fatal(err) + } + expectDBClientQueries(t, []string{ + "/insert into _vt.vreplication", + "begin", + "/insert into _vt.copy_state", + "/update _vt.vreplication set state='Copying'", + "commit", + "/update _vt.vreplication set pos=", + }) + return func() { + t.Helper() + query := fmt.Sprintf("delete from _vt.vreplication where id = %d", qr.InsertID) + if _, err := playerEngine.Exec(query); err != nil { + t.Fatal(err) + } + expectDeleteQueries(t) + } +} diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index 9cb7be471d4..52f3aee060f 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -33,6 +33,7 @@ import ( "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/binlog/binlogplayer" + "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/grpcclient" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/vttablet/queryservice" @@ -103,7 +104,11 @@ func TestMain(m *testing.M) { InitVStreamerClient(env.Dbcfgs) - playerEngine = NewTestEngine(env.TopoServ, env.Cells[0], env.Mysqld, realDBClientFactory, vrepldb) + externalConfig := map[string]*dbconfigs.DBConfigs{ + "exta": env.Dbcfgs, + "extb": env.Dbcfgs, + } + playerEngine = NewTestEngine(env.TopoServ, env.Cells[0], env.Mysqld, realDBClientFactory, vrepldb, externalConfig) if err := playerEngine.Open(context.Background()); err != nil { fmt.Fprintf(os.Stderr, "%v", err) return 1 diff --git a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go index c16cbde24dd..1302b3fd786 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go @@ -132,6 +132,11 @@ func (vr *vreplicator) replicate(ctx context.Context) error { vr.tableKeys = tableKeys for { + select { + case <-ctx.Done(): + return nil + default: + } // This rollback is a no-op. It's here for safety // in case the functions below leave transactions open. vr.dbClient.Rollback() diff --git a/go/vt/wrangler/testlib/migrate_served_from_test.go b/go/vt/wrangler/testlib/migrate_served_from_test.go index 7ac80937876..42aeaf868d3 100644 --- a/go/vt/wrangler/testlib/migrate_served_from_test.go +++ b/go/vt/wrangler/testlib/migrate_served_from_test.go @@ -106,7 +106,7 @@ func TestMigrateServedFrom(t *testing.T) { // Override with a fake VREngine after Agent is initialized in action loop. dbClient := binlogplayer.NewMockDBClient(t) dbClientFactory := func() binlogplayer.DBClient { return dbClient } - destMaster.Agent.VREngine = vreplication.NewTestEngine(ts, "", destMaster.FakeMysqlDaemon, dbClientFactory, dbClient.DBName()) + destMaster.Agent.VREngine = vreplication.NewTestEngine(ts, "", destMaster.FakeMysqlDaemon, dbClientFactory, dbClient.DBName(), nil) dbClient.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := destMaster.Agent.VREngine.Open(context.Background()); err != nil { t.Fatal(err) diff --git a/go/vt/wrangler/testlib/migrate_served_types_test.go b/go/vt/wrangler/testlib/migrate_served_types_test.go index 53b8a88a041..2b643c7f97e 100644 --- a/go/vt/wrangler/testlib/migrate_served_types_test.go +++ b/go/vt/wrangler/testlib/migrate_served_types_test.go @@ -153,7 +153,7 @@ func TestMigrateServedTypes(t *testing.T) { // Override with a fake VREngine after Agent is initialized in action loop. dbClient1 := binlogplayer.NewMockDBClient(t) dbClientFactory1 := func() binlogplayer.DBClient { return dbClient1 } - dest1Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest1Master.FakeMysqlDaemon, dbClientFactory1, dbClient1.DBName()) + dest1Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest1Master.FakeMysqlDaemon, dbClientFactory1, dbClient1.DBName(), nil) // select * from _vt.vreplication during Open dbClient1.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := dest1Master.Agent.VREngine.Open(context.Background()); err != nil { @@ -181,7 +181,7 @@ func TestMigrateServedTypes(t *testing.T) { // Override with a fake VREngine after Agent is initialized in action loop. dbClient2 := binlogplayer.NewMockDBClient(t) dbClientFactory2 := func() binlogplayer.DBClient { return dbClient2 } - dest2Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest2Master.FakeMysqlDaemon, dbClientFactory2, dbClient2.DBName()) + dest2Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest2Master.FakeMysqlDaemon, dbClientFactory2, dbClient2.DBName(), nil) // select * from _vt.vreplication during Open dbClient2.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := dest2Master.Agent.VREngine.Open(context.Background()); err != nil { @@ -417,7 +417,7 @@ func TestMultiShardMigrateServedTypes(t *testing.T) { // Override with a fake VREngine after Agent is initialized in action loop. dbClient1 := binlogplayer.NewMockDBClient(t) dbClientFactory1 := func() binlogplayer.DBClient { return dbClient1 } - dest1Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest1Master.FakeMysqlDaemon, dbClientFactory1, "db") + dest1Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest1Master.FakeMysqlDaemon, dbClientFactory1, "db", nil) // select * from _vt.vreplication during Open dbClient1.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := dest1Master.Agent.VREngine.Open(context.Background()); err != nil { @@ -434,7 +434,7 @@ func TestMultiShardMigrateServedTypes(t *testing.T) { // Override with a fake VREngine after Agent is initialized in action loop. dbClient2 := binlogplayer.NewMockDBClient(t) dbClientFactory2 := func() binlogplayer.DBClient { return dbClient2 } - dest2Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest2Master.FakeMysqlDaemon, dbClientFactory2, "db") + dest2Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest2Master.FakeMysqlDaemon, dbClientFactory2, "db", nil) // select * from _vt.vreplication during Open dbClient2.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := dest2Master.Agent.VREngine.Open(context.Background()); err != nil { @@ -505,7 +505,7 @@ func TestMultiShardMigrateServedTypes(t *testing.T) { // Override with a fake VREngine after Agent is initialized in action loop. dbClient1 = binlogplayer.NewMockDBClient(t) dbClientFactory1 = func() binlogplayer.DBClient { return dbClient1 } - dest3Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest3Master.FakeMysqlDaemon, dbClientFactory1, "db") + dest3Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest3Master.FakeMysqlDaemon, dbClientFactory1, "db", nil) // select * from _vt.vreplication during Open dbClient1.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := dest3Master.Agent.VREngine.Open(context.Background()); err != nil { @@ -522,7 +522,7 @@ func TestMultiShardMigrateServedTypes(t *testing.T) { // Override with a fake VREngine after Agent is initialized in action loop. dbClient2 = binlogplayer.NewMockDBClient(t) dbClientFactory2 = func() binlogplayer.DBClient { return dbClient2 } - dest4Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest4Master.FakeMysqlDaemon, dbClientFactory2, "db") + dest4Master.Agent.VREngine = vreplication.NewTestEngine(ts, "", dest4Master.FakeMysqlDaemon, dbClientFactory2, "db", nil) // select * from _vt.vreplication during Open dbClient2.ExpectRequest("select * from _vt.vreplication where db_name='db'", &sqltypes.Result{}, nil) if err := dest4Master.Agent.VREngine.Open(context.Background()); err != nil { diff --git a/go/vt/wrangler/traffic_switcher_env_test.go b/go/vt/wrangler/traffic_switcher_env_test.go index 653c8c13810..f12c1060da4 100644 --- a/go/vt/wrangler/traffic_switcher_env_test.go +++ b/go/vt/wrangler/traffic_switcher_env_test.go @@ -336,7 +336,7 @@ func (tme *testMigraterEnv) createDBClients(ctx context.Context, t *testing.T) { dbClientFactory := func() binlogplayer.DBClient { return dbclient } // Replace existing engine with a new one master.Agent.VREngine.Close() - master.Agent.VREngine = vreplication.NewTestEngine(tme.ts, "", master.FakeMysqlDaemon, dbClientFactory, dbclient.DBName()) + master.Agent.VREngine = vreplication.NewTestEngine(tme.ts, "", master.FakeMysqlDaemon, dbClientFactory, dbclient.DBName(), nil) if err := master.Agent.VREngine.Open(ctx); err != nil { t.Fatal(err) } @@ -347,7 +347,7 @@ func (tme *testMigraterEnv) createDBClients(ctx context.Context, t *testing.T) { dbClientFactory := func() binlogplayer.DBClient { return dbclient } // Replace existing engine with a new one master.Agent.VREngine.Close() - master.Agent.VREngine = vreplication.NewTestEngine(tme.ts, "", master.FakeMysqlDaemon, dbClientFactory, dbclient.DBName()) + master.Agent.VREngine = vreplication.NewTestEngine(tme.ts, "", master.FakeMysqlDaemon, dbClientFactory, dbclient.DBName(), nil) if err := master.Agent.VREngine.Open(ctx); err != nil { t.Fatal(err) } From 4c67c624c2ceb51b117859136c78be7a2c7eb57c Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 19 Apr 2020 21:15:19 -0700 Subject: [PATCH 07/16] vrepl: cleanup VStreamerClient Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletmanager/action_agent.go | 2 - .../tabletmanager/vreplication/controller.go | 2 +- .../vreplication/external_connector.go | 16 + .../vreplication/framework_test.go | 2 - .../tabletmanager/vreplication/vreplicator.go | 4 +- .../vreplication/vstreamer_client.go | 208 ------- .../vreplication/vstreamer_client_test.go | 524 ------------------ 7 files changed, 19 insertions(+), 739 deletions(-) delete mode 100644 go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go delete mode 100644 go/vt/vttablet/tabletmanager/vreplication/vstreamer_client_test.go diff --git a/go/vt/vttablet/tabletmanager/action_agent.go b/go/vt/vttablet/tabletmanager/action_agent.go index cc1a4145c87..dd28f0a4d34 100644 --- a/go/vt/vttablet/tabletmanager/action_agent.go +++ b/go/vt/vttablet/tabletmanager/action_agent.go @@ -321,8 +321,6 @@ func NewActionAgent( return nil, err } - vreplication.InitVStreamerClient(agent.DBConfigs) - // The db name is set by the Start function called above agent.VREngine = vreplication.NewEngine(config, ts, tabletAlias.Cell, mysqld) servenv.OnTerm(agent.VREngine.Close) diff --git a/go/vt/vttablet/tabletmanager/vreplication/controller.go b/go/vt/vttablet/tabletmanager/vreplication/controller.go index edd034ef0e2..a45f6b3b5c5 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/controller.go +++ b/go/vt/vttablet/tabletmanager/vreplication/controller.go @@ -221,7 +221,7 @@ func (ct *controller) runBlp(ctx context.Context) (err error) { return err } - var vsClient VStreamerClient + var vsClient vstreamerClient var err error if name := ct.source.GetExternalMysql(); name != "" { vsClient, err = ct.vre.ec.Get(name) diff --git a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go index f98addc4c5b..43e5f4eadc5 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go +++ b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go @@ -35,6 +35,22 @@ import ( "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer" ) +var ( + _ vstreamerClient = (*mysqlConnector)(nil) + _ vstreamerClient = (*tabletConnector)(nil) +) + +// vstreamerClient exposes the core interface of a vstreamer +type vstreamerClient interface { + Close(context.Context) + + // VStream streams VReplication events based on the specified filter. + VStream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error + + // VStreamRows streams rows of a table from the specified starting point. + VStreamRows(ctx context.Context, query string, lastpk *querypb.QueryResult, send func(*binlogdatapb.VStreamRowsResponse) error) error +} + type externalConnector struct { mu sync.Mutex dbconfigs map[string]*dbconfigs.DBConfigs diff --git a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go index 52f3aee060f..9ad110f2133 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/framework_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/framework_test.go @@ -102,8 +102,6 @@ func TestMain(m *testing.M) { return 1 } - InitVStreamerClient(env.Dbcfgs) - externalConfig := map[string]*dbconfigs.DBConfigs{ "exta": env.Dbcfgs, "extb": env.Dbcfgs, diff --git a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go index 1302b3fd786..104ff093be6 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go @@ -53,7 +53,7 @@ type vreplicator struct { dbClient *vdbClient // source source *binlogdatapb.BinlogSource - sourceVStreamer VStreamerClient + sourceVStreamer vstreamerClient stats *binlogplayer.Stats // mysqld is used to fetch the local schema. @@ -81,7 +81,7 @@ type vreplicator struct { // alias like "a+b as targetcol" must be used. // More advanced constructs can be used. Please see the table plan builder // documentation for more info. -func newVReplicator(id uint32, source *binlogdatapb.BinlogSource, sourceVStreamer VStreamerClient, stats *binlogplayer.Stats, dbClient binlogplayer.DBClient, mysqld mysqlctl.MysqlDaemon, vre *Engine) *vreplicator { +func newVReplicator(id uint32, source *binlogdatapb.BinlogSource, sourceVStreamer vstreamerClient, stats *binlogplayer.Stats, dbClient binlogplayer.DBClient, mysqld mysqlctl.MysqlDaemon, vre *Engine) *vreplicator { return &vreplicator{ vre: vre, id: id, diff --git a/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go b/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go deleted file mode 100644 index 7bbbe7a6409..00000000000 --- a/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client.go +++ /dev/null @@ -1,208 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package vreplication - -import ( - "errors" - "fmt" - "sync" - - "golang.org/x/net/context" - - "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/dbconfigs" - "vitess.io/vitess/go/vt/grpcclient" - "vitess.io/vitess/go/vt/vttablet/queryservice" - "vitess.io/vitess/go/vt/vttablet/tabletconn" - "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" - "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" - "vitess.io/vitess/go/vt/vttablet/tabletserver/vstreamer" - - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -var ( - //_ VStreamerClient = (*TabletVStreamerClient)(nil) - dbcfgs *dbconfigs.DBConfigs -) - -// VStreamerClient exposes the core interface of a vstreamer -type VStreamerClient interface { - Close(context.Context) - - // VStream streams VReplication events based on the specified filter. - VStream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error - - // VStreamRows streams rows of a table from the specified starting point. - VStreamRows(ctx context.Context, query string, lastpk *querypb.QueryResult, send func(*binlogdatapb.VStreamRowsResponse) error) error -} - -// TabletVStreamerClient a vstream client backed by vttablet -type TabletVStreamerClient struct { - // mu protects isOpen, streamers, streamIdx and kschema. - mu sync.Mutex - - isOpen bool - - tablet *topodatapb.Tablet - target *querypb.Target - tsQueryService queryservice.QueryService -} - -// MySQLVStreamerClient a vstream client backed by MySQL -type MySQLVStreamerClient struct { - // mu protects isOpen, streamers, streamIdx and kschema. - mu sync.Mutex - - isOpen bool - - sourceConnParams dbconfigs.Connector - sourceSe *schema.Engine -} - -// NewTabletVStreamerClient creates a new TabletVStreamerClient -func NewTabletVStreamerClient(tablet *topodatapb.Tablet) *TabletVStreamerClient { - return &TabletVStreamerClient{ - tablet: tablet, - target: &querypb.Target{ - Keyspace: tablet.Keyspace, - Shard: tablet.Shard, - TabletType: tablet.Type, - }, - } -} - -// Open part of the VStreamerClient interface -func (vsClient *TabletVStreamerClient) Open(ctx context.Context) (err error) { - vsClient.mu.Lock() - defer vsClient.mu.Unlock() - if vsClient.isOpen { - return nil - } - vsClient.isOpen = true - - vsClient.tsQueryService, err = tabletconn.GetDialer()(vsClient.tablet, grpcclient.FailFast(true)) - return err -} - -// Close part of the VStreamerClient interface -func (vsClient *TabletVStreamerClient) Close(ctx context.Context) (err error) { - vsClient.mu.Lock() - defer vsClient.mu.Unlock() - if !vsClient.isOpen { - return nil - } - vsClient.isOpen = false - return vsClient.tsQueryService.Close(ctx) -} - -// VStream part of the VStreamerClient interface -func (vsClient *TabletVStreamerClient) VStream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { - if !vsClient.isOpen { - return errors.New("can't VStream without opening client") - } - return vsClient.tsQueryService.VStream(ctx, vsClient.target, startPos, filter, send) -} - -// VStreamRows part of the VStreamerClient interface -func (vsClient *TabletVStreamerClient) VStreamRows(ctx context.Context, query string, lastpk *querypb.QueryResult, send func(*binlogdatapb.VStreamRowsResponse) error) error { - if !vsClient.isOpen { - return errors.New("can't VStreamRows without opening client") - } - return vsClient.tsQueryService.VStreamRows(ctx, vsClient.target, query, lastpk, send) -} - -// NewMySQLVStreamerClient is a vstream client that allows you to stream directly from MySQL. -// In order to achieve this, the following creates a vstreamer Engine with a dummy in memorytopo. -func NewMySQLVStreamerClient() *MySQLVStreamerClient { - if dbcfgs == nil { - panic("can't use MySQLVStreamerClient without calling InitVStreamerClient() ") - } - // TODO: For now external mysql streams can only be used with ExternalReplWithDB creds. - // In the future we will support multiple users. - vsClient := &MySQLVStreamerClient{ - sourceConnParams: dbcfgs.ExternalReplWithDB(), - } - return vsClient -} - -// Open part of the VStreamerClient interface -func (vsClient *MySQLVStreamerClient) Open(ctx context.Context) (err error) { - vsClient.mu.Lock() - defer vsClient.mu.Unlock() - if vsClient.isOpen { - return nil - } - vsClient.isOpen = true - - // Let's create all the required components by vstreamer - - config := tabletenv.NewDefaultConfig() - vsClient.sourceSe = schema.NewEngine(tabletenv.NewTestEnv(config, nil, "VStreamerClientTest")) - vsClient.sourceSe.InitDBConfig(vsClient.sourceConnParams) - err = vsClient.sourceSe.Open() - if err != nil { - return err - } - return nil -} - -// Close part of the VStreamerClient interface -func (vsClient *MySQLVStreamerClient) Close(ctx context.Context) (err error) { - vsClient.mu.Lock() - defer vsClient.mu.Unlock() - if !vsClient.isOpen { - return nil - } - - vsClient.isOpen = false - vsClient.sourceSe.Close() - return nil -} - -// VStream part of the VStreamerClient interface -func (vsClient *MySQLVStreamerClient) VStream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { - if !vsClient.isOpen { - return errors.New("can't VStream without opening client") - } - streamer := vstreamer.NewVStreamer(ctx, vsClient.sourceConnParams, vsClient.sourceSe, startPos, filter, send) - return streamer.Stream() -} - -// VStreamRows part of the VStreamerClient interface -func (vsClient *MySQLVStreamerClient) VStreamRows(ctx context.Context, query string, lastpk *querypb.QueryResult, send func(*binlogdatapb.VStreamRowsResponse) error) error { - if !vsClient.isOpen { - return errors.New("can't VStreamRows without opening client") - } - var row []sqltypes.Value - if lastpk != nil { - r := sqltypes.Proto3ToResult(lastpk) - if len(r.Rows) != 1 { - return fmt.Errorf("unexpected lastpk input: %v", lastpk) - } - row = r.Rows[0] - } - streamer := vstreamer.NewRowStreamer(ctx, vsClient.sourceConnParams, vsClient.sourceSe, query, row, send) - return streamer.Stream() -} - -// InitVStreamerClient initializes config for vstreamer client -func InitVStreamerClient(cfg *dbconfigs.DBConfigs) { - dbcfgs = cfg -} diff --git a/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client_test.go b/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client_test.go deleted file mode 100644 index af3e762383b..00000000000 --- a/go/vt/vttablet/tabletmanager/vreplication/vstreamer_client_test.go +++ /dev/null @@ -1,524 +0,0 @@ -/* -Copyright 2019 The Vitess Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package vreplication - -import ( - "fmt" - "reflect" - "strings" - "testing" - "time" - - "golang.org/x/net/context" - - "vitess.io/vitess/go/mysql" - - "vitess.io/vitess/go/vt/dbconfigs" - binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" - querypb "vitess.io/vitess/go/vt/proto/query" - topodatapb "vitess.io/vitess/go/vt/proto/topodata" -) - -func TestTabletVStreamerClientOpen(t *testing.T) { - tablet := addTablet(100) - defer deleteTablet(tablet) - - type fields struct { - tablet *topodatapb.Tablet - } - type args struct { - ctx context.Context - } - tests := []struct { - name string - fields fields - args args - err string - }{ - { - name: "initializes streamer client", - fields: fields{ - tablet: tablet, - }, - args: args{ - ctx: context.Background(), - }, - }, - } - - for _, tcase := range tests { - t.Run(tcase.name, func(t *testing.T) { - vsClient := &TabletVStreamerClient{ - tablet: tcase.fields.tablet, - } - - err := vsClient.Open(tcase.args.ctx) - - if err != nil { - if !strings.Contains(err.Error(), tcase.err) { - t.Errorf("TabletVStreamerClient.Open() error:\n%v, want\n%v", err, tcase.err) - } - return - } - - if tcase.err != "" { - t.Errorf("TabletVStreamerClient.Open() error:\n%v, want\n%v", err, tcase.err) - } - - if !vsClient.isOpen { - t.Errorf("TabletVStreamerClient.Open() isOpen set to false, expected true") - } - - if vsClient.tablet == nil { - t.Errorf("TabletVStreamerClient.Open() expected sourceSe to be set") - } - }) - } -} - -func TestTabletVStreamerClientClose(t *testing.T) { - tablet := addTablet(100) - defer deleteTablet(tablet) - - type fields struct { - tablet *topodatapb.Tablet - } - type args struct { - ctx context.Context - } - tests := []struct { - name string - fields fields - args args - err string - }{ - { - name: "closes engine correctly", - fields: fields{ - tablet: tablet, - }, - args: args{ - ctx: context.Background(), - }, - }, - } - - for _, tcase := range tests { - t.Run(tcase.name, func(t *testing.T) { - vsClient := &TabletVStreamerClient{ - tablet: tcase.fields.tablet, - } - - err := vsClient.Open(tcase.args.ctx) - if err != nil { - t.Errorf("Failed to Open vsClient") - return - } - - err = vsClient.Close(tcase.args.ctx) - - if tcase.err != "" { - t.Errorf("MySQLVStreamerClient.Close() error:\n%v, want\n%v", err, tcase.err) - } - - if vsClient.isOpen { - t.Errorf("MySQLVStreamerClient.Close() isOpen set to true, expected false") - } - }) - } -} - -func TestTabletVStreamerClientVStream(t *testing.T) { - tablet := addTablet(100) - defer deleteTablet(tablet) - - vsClient := &TabletVStreamerClient{ - tablet: tablet, - target: &querypb.Target{ - Keyspace: tablet.Keyspace, - Shard: tablet.Shard, - TabletType: tablet.Type, - }, - } - - filter := &binlogdatapb.Filter{ - Rules: []*binlogdatapb.Rule{{ - Match: "/.*", - }}, - } - eventsChan := make(chan *binlogdatapb.VEvent, 1000) - send := func(events []*binlogdatapb.VEvent) error { - for _, e := range events { - eventsChan <- e - } - return nil - } - - execStatements(t, []string{ - "create table t1(id int, ts timestamp, dt datetime)", - fmt.Sprintf("create table %s.t1(id int, ts timestamp, dt datetime)", vrepldb), - }) - defer execStatements(t, []string{ - "drop table t1", - fmt.Sprintf("drop table %s.t1", vrepldb), - }) - env.SchemaEngine.Reload(context.Background()) - - ctx := context.Background() - err := vsClient.Open(ctx) - if err != nil { - t.Errorf("Failed to Open vsClient") - return - } - - defer vsClient.Close(ctx) - - pos := masterPosition(t) - // This asserts that events are flowing through the VStream when using mysql client - go vsClient.VStream(ctx, pos, filter, send) - - qr, err := env.Mysqld.FetchSuperQuery(context.Background(), "select now()") - if err != nil { - t.Fatal(err) - } - want := qr.Rows[0][0].ToString() - execStatements(t, []string{ - fmt.Sprintf("insert into t1 values(1, '%s', '%s')", want, want), - }) - - select { - case got := <-eventsChan: - if got.Type != binlogdatapb.VEventType_BEGIN { - t.Errorf("Did not get expected events: want: %v, got: %v", binlogdatapb.VEventType_BEGIN, got.Type) - } - case <-time.After(5 * time.Second): - t.Errorf("no events received") - } -} - -func TestTabletVStreamerClientVStreamRows(t *testing.T) { - tablet := addTablet(100) - defer deleteTablet(tablet) - - vsClient := &TabletVStreamerClient{ - tablet: tablet, - } - - eventsChan := make(chan *querypb.Row, 1000) - send := func(streamerResponse *binlogdatapb.VStreamRowsResponse) error { - for _, row := range streamerResponse.Rows { - eventsChan <- row - } - return nil - } - - execStatements(t, []string{ - "create table t1(id int, ts timestamp, dt datetime)", - fmt.Sprintf("create table %s.t1(id int, ts timestamp, dt datetime)", vrepldb), - }) - defer execStatements(t, []string{ - "drop table t1", - fmt.Sprintf("drop table %s.t1", vrepldb), - }) - env.SchemaEngine.Reload(context.Background()) - - qr, err := env.Mysqld.FetchSuperQuery(context.Background(), "select now()") - if err != nil { - t.Fatal(err) - } - want := qr.Rows[0][0].ToString() - ctx := context.Background() - err = vsClient.Open(ctx) - if err != nil { - t.Errorf("Failed to Open vsClient") - return - } - - defer vsClient.Close(ctx) - - execStatements(t, []string{ - fmt.Sprintf("insert into t1 values(1, '%s', '%s')", want, want), - }) - - // This asserts that events are flowing through VStreamRows when using mysql client - // This needs to happen after the insert above to avoid race where select precedes the insert - go vsClient.VStreamRows(ctx, "select * from t1", nil, send) - - select { - case <-eventsChan: - // Success got expected - case <-time.After(5 * time.Second): - t.Errorf("no events received") - } -} - -func TestNewMySQLVStreamerClient(t *testing.T) { - tests := []struct { - name string - want *MySQLVStreamerClient - }{ - { - name: "sets conn params for MySQLVStreamerClient ", - want: &MySQLVStreamerClient{ - sourceConnParams: dbcfgs.ExternalReplWithDB(), - }, - }, - } - for _, tcase := range tests { - t.Run(tcase.name, func(t *testing.T) { - if got := NewMySQLVStreamerClient(); !reflect.DeepEqual(got, tcase.want) { - t.Errorf("NewMySQLVStreamerClient() = %v, want %v", got, tcase.want) - } - }) - } -} - -func TestMySQLVStreamerClientOpen(t *testing.T) { - dbc := dbconfigs.New(&mysql.ConnParams{ - Host: "invalidhost", - Port: 3306, - }) - type fields struct { - sourceConnParams dbconfigs.Connector - } - type args struct { - ctx context.Context - } - tests := []struct { - name string - fields fields - args args - err string - }{ - { - name: "initializes streamer correctly", - fields: fields{ - sourceConnParams: dbcfgs.ExternalReplWithDB(), - }, - args: args{ - ctx: context.Background(), - }, - }, - { - name: "returns error when invalid conn params are provided", - fields: fields{ - sourceConnParams: dbc, - }, - args: args{ - ctx: context.Background(), - }, - err: "failed: dial tcp: lookup invalidhost", - }, - } - for _, tcase := range tests { - t.Run(tcase.name, func(t *testing.T) { - vsClient := &MySQLVStreamerClient{ - sourceConnParams: tcase.fields.sourceConnParams, - } - - err := vsClient.Open(tcase.args.ctx) - - if err != nil { - if !strings.Contains(err.Error(), tcase.err) { - t.Errorf("MySQLVStreamerClient.Open() error:\n%v, want\n%v", err, tcase.err) - } - return - } - - if tcase.err != "" { - t.Errorf("MySQLVStreamerClient.Open() error:\n%v, want\n%v", err, tcase.err) - } - - if !vsClient.isOpen { - t.Errorf("MySQLVStreamerClient.Open() isOpen set to false, expected true") - } - - if !vsClient.sourceSe.IsOpen() { - t.Errorf("MySQLVStreamerClient.Open() expected sourceSe to be opened") - } - }) - } -} - -func TestMySQLVStreamerClientClose(t *testing.T) { - type fields struct { - isOpen bool - sourceConnParams dbconfigs.Connector - } - type args struct { - ctx context.Context - } - - tests := []struct { - name string - fields fields - args args - err string - }{ - { - name: "closes engine correctly", - fields: fields{ - sourceConnParams: dbcfgs.ExternalReplWithDB(), - }, - args: args{ - ctx: context.Background(), - }, - }, - } - - for _, tcase := range tests { - t.Run(tcase.name, func(t *testing.T) { - vsClient := &MySQLVStreamerClient{ - isOpen: tcase.fields.isOpen, - sourceConnParams: tcase.fields.sourceConnParams, - } - - err := vsClient.Open(tcase.args.ctx) - if err != nil { - t.Errorf("Failed to Open vsClient") - return - } - - err = vsClient.Close(tcase.args.ctx) - - if tcase.err != "" { - t.Errorf("MySQLVStreamerClient.Close() error:\n%v, want\n%v", err, tcase.err) - } - - if vsClient.isOpen { - t.Errorf("MySQLVStreamerClient.Close() isOpen set to true, expected false") - } - - if vsClient.sourceSe.IsOpen() { - t.Errorf("MySQLVStreamerClient.Close() expected sourceSe to be closed") - } - }) - } -} - -func TestMySQLVStreamerClientVStream(t *testing.T) { - vsClient := &MySQLVStreamerClient{ - sourceConnParams: dbcfgs.ExternalReplWithDB(), - } - - filter := &binlogdatapb.Filter{ - Rules: []*binlogdatapb.Rule{{ - Match: "/.*", - }}, - } - eventsChan := make(chan *binlogdatapb.VEvent, 1000) - send := func(events []*binlogdatapb.VEvent) error { - for _, e := range events { - eventsChan <- e - } - return nil - } - - execStatements(t, []string{ - "create table t1(id int, ts timestamp, dt datetime)", - fmt.Sprintf("create table %s.t1(id int, ts timestamp, dt datetime)", vrepldb), - }) - defer execStatements(t, []string{ - "drop table t1", - fmt.Sprintf("drop table %s.t1", vrepldb), - }) - env.SchemaEngine.Reload(context.Background()) - - ctx := context.Background() - err := vsClient.Open(ctx) - if err != nil { - t.Errorf("Failed to Open vsClient") - return - } - - defer vsClient.Close(ctx) - - pos := masterPosition(t) - // This asserts that events are flowing through the VStream when using mysql client - go vsClient.VStream(ctx, pos, filter, send) - - qr, err := env.Mysqld.FetchSuperQuery(context.Background(), "select now()") - if err != nil { - t.Fatal(err) - } - want := qr.Rows[0][0].ToString() - execStatements(t, []string{ - fmt.Sprintf("insert into t1 values(1, '%s', '%s')", want, want), - }) - - select { - case got := <-eventsChan: - if got.Type != binlogdatapb.VEventType_BEGIN { - t.Errorf("Did not get expected events: want: %v, got: %v", binlogdatapb.VEventType_BEGIN, got.Type) - } - case <-time.After(5 * time.Second): - t.Errorf("no events received") - } -} - -func TestMySQLVStreamerClientVStreamRows(t *testing.T) { - vsClient := &MySQLVStreamerClient{ - sourceConnParams: dbcfgs.ExternalReplWithDB(), - } - - eventsChan := make(chan *querypb.Row, 1000) - send := func(streamerResponse *binlogdatapb.VStreamRowsResponse) error { - for _, row := range streamerResponse.Rows { - eventsChan <- row - } - return nil - } - - execStatements(t, []string{ - "create table t1(id int, ts timestamp, dt datetime)", - fmt.Sprintf("create table %s.t1(id int, ts timestamp, dt datetime)", vrepldb), - }) - defer execStatements(t, []string{ - "drop table t1", - fmt.Sprintf("drop table %s.t1", vrepldb), - }) - env.SchemaEngine.Reload(context.Background()) - - qr, err := env.Mysqld.FetchSuperQuery(context.Background(), "select now()") - if err != nil { - t.Fatal(err) - } - want := qr.Rows[0][0].ToString() - - ctx := context.Background() - err = vsClient.Open(ctx) - if err != nil { - t.Errorf("Failed to Open vsClient") - return - } - - defer vsClient.Close(ctx) - - // This asserts that events are flowing through the VStream when using mysql client - go vsClient.VStreamRows(ctx, "select * from t1", nil, send) - - execStatements(t, []string{ - fmt.Sprintf("insert into t1 values(1, '%s', '%s')", want, want), - }) - - select { - case <-eventsChan: - // Success got expected - case <-time.After(5 * time.Second): - t.Errorf("no events received") - } -} From aeabda5c03f34d452d18eb07b1bb564707627b87 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 19 Apr 2020 21:54:29 -0700 Subject: [PATCH 08/16] yaml: use TabletConfig.DB instead of dbcfgs Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/heartbeat/reader.go | 2 +- go/vt/vttablet/heartbeat/reader_test.go | 2 +- go/vt/vttablet/heartbeat/writer.go | 4 +-- go/vt/vttablet/heartbeat/writer_test.go | 2 +- .../vreplication/external_connector.go | 4 +-- .../tabletserver/connpool/pool_test.go | 6 ++--- .../tabletserver/messager/engine_test.go | 2 +- .../messager/message_manager_test.go | 2 +- go/vt/vttablet/tabletserver/query_engine.go | 6 ++--- .../tabletserver/query_engine_test.go | 6 +++-- .../tabletserver/schema/engine_test.go | 2 +- .../tabletserver/schema/load_table_test.go | 2 +- go/vt/vttablet/tabletserver/tabletenv/env.go | 27 ++++++++----------- go/vt/vttablet/tabletserver/tabletserver.go | 15 +++-------- go/vt/vttablet/tabletserver/tx_engine.go | 6 ++--- go/vt/vttablet/tabletserver/tx_engine_test.go | 8 +++--- go/vt/vttablet/tabletserver/tx_pool_test.go | 2 +- .../tabletserver/txlimiter/tx_limiter_test.go | 6 ++--- .../txserializer/tx_serializer_test.go | 18 ++++++------- .../vttablet/tabletserver/vstreamer/engine.go | 6 ++--- .../tabletserver/vstreamer/main_test.go | 5 ++-- .../vstreamer/snapshot_conn_test.go | 2 +- .../tabletserver/vstreamer/testenv/testenv.go | 3 ++- .../tabletserver/vstreamer/vstreamer_test.go | 2 +- 24 files changed, 65 insertions(+), 75 deletions(-) diff --git a/go/vt/vttablet/heartbeat/reader.go b/go/vt/vttablet/heartbeat/reader.go index ec9522dd1b2..0df09195e60 100644 --- a/go/vt/vttablet/heartbeat/reader.go +++ b/go/vt/vttablet/heartbeat/reader.go @@ -110,7 +110,7 @@ func (r *Reader) Open() { } log.Info("Beginning heartbeat reads") - r.pool.Open(r.env.DBConfigs().AppWithDB(), r.env.DBConfigs().DbaWithDB(), r.env.DBConfigs().AppDebugWithDB()) + r.pool.Open(r.env.Config().DB.AppWithDB(), r.env.Config().DB.DbaWithDB(), r.env.Config().DB.AppDebugWithDB()) r.ticks.Start(func() { r.readHeartbeat() }) r.isOpen = true } diff --git a/go/vt/vttablet/heartbeat/reader_test.go b/go/vt/vttablet/heartbeat/reader_test.go index 446c3c4cee0..af1accf37ea 100644 --- a/go/vt/vttablet/heartbeat/reader_test.go +++ b/go/vt/vttablet/heartbeat/reader_test.go @@ -105,7 +105,7 @@ func newReader(db *fakesqldb.DB, nowFunc func() time.Time) *Reader { cp := *params dbc := dbconfigs.NewTestDBConfigs(cp, cp, "") - tr := NewReader(tabletenv.NewTestEnv(config, nil, "ReaderTest")) + tr := NewReader(tabletenv.NewTestEnv(config, "ReaderTest")) tr.keyspaceShard = "test:0" tr.now = nowFunc tr.pool.Open(dbc.AppWithDB(), dbc.DbaWithDB(), dbc.AppDebugWithDB()) diff --git a/go/vt/vttablet/heartbeat/writer.go b/go/vt/vttablet/heartbeat/writer.go index 54acc84dbaf..e1f649486f3 100644 --- a/go/vt/vttablet/heartbeat/writer.go +++ b/go/vt/vttablet/heartbeat/writer.go @@ -102,7 +102,7 @@ func (w *Writer) Init(target querypb.Target) error { w.keyspaceShard = fmt.Sprintf("%s:%s", target.Keyspace, target.Shard) if target.TabletType == topodatapb.TabletType_MASTER { - err := w.initializeTables(w.env.DBConfigs().AppWithDB()) + err := w.initializeTables(w.env.Config().DB.AppWithDB()) if err != nil { w.recordError(err) return err @@ -125,7 +125,7 @@ func (w *Writer) Open() { return } log.Info("Beginning heartbeat writes") - w.pool.Open(w.env.DBConfigs().AppWithDB(), w.env.DBConfigs().DbaWithDB(), w.env.DBConfigs().AppDebugWithDB()) + w.pool.Open(w.env.Config().DB.AppWithDB(), w.env.Config().DB.DbaWithDB(), w.env.Config().DB.AppDebugWithDB()) w.ticks.Start(func() { w.writeHeartbeat() }) w.isOpen = true } diff --git a/go/vt/vttablet/heartbeat/writer_test.go b/go/vt/vttablet/heartbeat/writer_test.go index a25d5d5e7e5..9c2c3213373 100644 --- a/go/vt/vttablet/heartbeat/writer_test.go +++ b/go/vt/vttablet/heartbeat/writer_test.go @@ -110,7 +110,7 @@ func newTestWriter(db *fakesqldb.DB, nowFunc func() time.Time) *Writer { cp := *params dbc := dbconfigs.NewTestDBConfigs(cp, cp, "") - tw := NewWriter(tabletenv.NewTestEnv(config, nil, "WriterTest"), topodatapb.TabletAlias{Cell: "test", Uid: 1111}) + tw := NewWriter(tabletenv.NewTestEnv(config, "WriterTest"), topodatapb.TabletAlias{Cell: "test", Uid: 1111}) tw.keyspaceShard = "test:0" tw.now = nowFunc tw.pool.Open(dbc.AppWithDB(), dbc.DbaWithDB(), dbc.AppDebugWithDB()) diff --git a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go index 43e5f4eadc5..16878f5b6b5 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go +++ b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go @@ -85,10 +85,10 @@ func (ec *externalConnector) Get(name string) (*mysqlConnector, error) { return nil, vterrors.Errorf(vtrpcpb.Code_NOT_FOUND, "external mysqlConnector %v not found", name) } c := &mysqlConnector{} - c.env = tabletenv.NewTestEnv(config, config.DB, name) + c.env = tabletenv.NewTestEnv(config, name) c.se = schema.NewEngine(c.env) c.vstreamer = vstreamer.NewEngine(c.env, nil, c.se) - c.se.InitDBConfig(c.env.DBConfigs().DbaWithDB()) + c.se.InitDBConfig(c.env.Config().DB.DbaWithDB()) // Open if err := c.se.Open(); err != nil { diff --git a/go/vt/vttablet/tabletserver/connpool/pool_test.go b/go/vt/vttablet/tabletserver/connpool/pool_test.go index 1f7bafa3977..6354e509687 100644 --- a/go/vt/vttablet/tabletserver/connpool/pool_test.go +++ b/go/vt/vttablet/tabletserver/connpool/pool_test.go @@ -54,7 +54,7 @@ func TestConnPoolGet(t *testing.T) { func TestConnPoolTimeout(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - connPool := NewPool(tabletenv.NewTestEnv(nil, nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ + connPool := NewPool(tabletenv.NewTestEnv(nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ Size: 1, TimeoutSeconds: 1, IdleTimeoutSeconds: 10, @@ -71,7 +71,7 @@ func TestConnPoolTimeout(t *testing.T) { func TestConnPoolMaxWaiters(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - connPool := NewPool(tabletenv.NewTestEnv(nil, nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ + connPool := NewPool(tabletenv.NewTestEnv(nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ Size: 1, MaxWaiters: 1, }) @@ -282,7 +282,7 @@ func TestConnPoolStateWhilePoolIsOpen(t *testing.T) { } func newPool() *Pool { - return NewPool(tabletenv.NewTestEnv(nil, nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ + return NewPool(tabletenv.NewTestEnv(nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ Size: 100, IdleTimeoutSeconds: 10, }) diff --git a/go/vt/vttablet/tabletserver/messager/engine_test.go b/go/vt/vttablet/tabletserver/messager/engine_test.go index 6982e191335..8e9650240b1 100644 --- a/go/vt/vttablet/tabletserver/messager/engine_test.go +++ b/go/vt/vttablet/tabletserver/messager/engine_test.go @@ -172,7 +172,7 @@ func TestEngineGenerate(t *testing.T) { func newTestEngine(db *fakesqldb.DB) *Engine { config := tabletenv.NewDefaultConfig() tsv := &fakeTabletServer{ - Env: tabletenv.NewTestEnv(config, nil, "MessagerTest"), + Env: tabletenv.NewTestEnv(config, "MessagerTest"), } se := schema.NewEngine(tsv) te := NewEngine(tsv, se, newFakeVStreamer()) diff --git a/go/vt/vttablet/tabletserver/messager/message_manager_test.go b/go/vt/vttablet/tabletserver/messager/message_manager_test.go index b3aec44e4ef..366694843fc 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager_test.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager_test.go @@ -836,7 +836,7 @@ type fakeTabletServer struct { func newFakeTabletServer() *fakeTabletServer { config := tabletenv.NewDefaultConfig() return &fakeTabletServer{ - Env: tabletenv.NewTestEnv(config, nil, "MessagerTest"), + Env: tabletenv.NewTestEnv(config, "MessagerTest"), } } diff --git a/go/vt/vttablet/tabletserver/query_engine.go b/go/vt/vttablet/tabletserver/query_engine.go index e2ddab5bec7..0c1842961ea 100644 --- a/go/vt/vttablet/tabletserver/query_engine.go +++ b/go/vt/vttablet/tabletserver/query_engine.go @@ -236,7 +236,7 @@ func NewQueryEngine(env tabletenv.Env, se *schema.Engine) *QueryEngine { // Open must be called before sending requests to QueryEngine. func (qe *QueryEngine) Open() error { - qe.conns.Open(qe.env.DBConfigs().AppWithDB(), qe.env.DBConfigs().DbaWithDB(), qe.env.DBConfigs().AppDebugWithDB()) + qe.conns.Open(qe.env.Config().DB.AppWithDB(), qe.env.Config().DB.DbaWithDB(), qe.env.Config().DB.AppDebugWithDB()) conn, err := qe.conns.Get(tabletenv.LocalContext()) if err != nil { @@ -253,7 +253,7 @@ func (qe *QueryEngine) Open() error { return err } - qe.streamConns.Open(qe.env.DBConfigs().AppWithDB(), qe.env.DBConfigs().DbaWithDB(), qe.env.DBConfigs().AppDebugWithDB()) + qe.streamConns.Open(qe.env.Config().DB.AppWithDB(), qe.env.Config().DB.DbaWithDB(), qe.env.Config().DB.AppDebugWithDB()) qe.se.RegisterNotifier("qe", qe.schemaChanged) return nil } @@ -360,7 +360,7 @@ func (qe *QueryEngine) ClearQueryPlanCache() { // IsMySQLReachable returns true if we can connect to MySQL. func (qe *QueryEngine) IsMySQLReachable() bool { - conn, err := dbconnpool.NewDBConnection(context.TODO(), qe.env.DBConfigs().DbaWithDB()) + conn, err := dbconnpool.NewDBConnection(context.TODO(), qe.env.Config().DB.DbaWithDB()) if err != nil { if mysql.IsConnErr(err) { return false diff --git a/go/vt/vttablet/tabletserver/query_engine_test.go b/go/vt/vttablet/tabletserver/query_engine_test.go index d6fce353947..2b85cfd0930 100644 --- a/go/vt/vttablet/tabletserver/query_engine_test.go +++ b/go/vt/vttablet/tabletserver/query_engine_test.go @@ -49,7 +49,8 @@ func TestStrictMode(t *testing.T) { // Test default behavior. config := tabletenv.NewDefaultConfig() - env := tabletenv.NewTestEnv(config, newDBConfigs(db), "TabletServerTest") + config.DB = newDBConfigs(db) + env := tabletenv.NewTestEnv(config, "TabletServerTest") se := schema.NewEngine(env) qe := NewQueryEngine(env, se) qe.se.InitDBConfig(newDBConfigs(db).DbaWithDB()) @@ -274,11 +275,12 @@ func TestStatsURL(t *testing.T) { func newTestQueryEngine(queryCacheSize int, idleTimeout time.Duration, strict bool, dbcfgs *dbconfigs.DBConfigs) *QueryEngine { config := tabletenv.NewDefaultConfig() + config.DB = dbcfgs config.QueryCacheSize = queryCacheSize config.OltpReadPool.IdleTimeoutSeconds = int(idleTimeout / 1e9) config.OlapReadPool.IdleTimeoutSeconds = int(idleTimeout / 1e9) config.TxPool.IdleTimeoutSeconds = int(idleTimeout / 1e9) - env := tabletenv.NewTestEnv(config, dbcfgs, "TabletServerTest") + env := tabletenv.NewTestEnv(config, "TabletServerTest") se := schema.NewEngine(env) qe := NewQueryEngine(env, se) se.InitDBConfig(dbcfgs.DbaWithDB()) diff --git a/go/vt/vttablet/tabletserver/schema/engine_test.go b/go/vt/vttablet/tabletserver/schema/engine_test.go index 9faced3730c..7af44a268b6 100644 --- a/go/vt/vttablet/tabletserver/schema/engine_test.go +++ b/go/vt/vttablet/tabletserver/schema/engine_test.go @@ -305,7 +305,7 @@ func newEngine(queryCacheSize int, reloadTime time.Duration, idleTimeout time.Du config.OltpReadPool.IdleTimeoutSeconds = int(idleTimeout / 1e9) config.OlapReadPool.IdleTimeoutSeconds = int(idleTimeout / 1e9) config.TxPool.IdleTimeoutSeconds = int(idleTimeout / 1e9) - se := NewEngine(tabletenv.NewTestEnv(config, nil, "SchemaTest")) + se := NewEngine(tabletenv.NewTestEnv(config, "SchemaTest")) se.InitDBConfig(newDBConfigs(db).DbaWithDB()) return se } diff --git a/go/vt/vttablet/tabletserver/schema/load_table_test.go b/go/vt/vttablet/tabletserver/schema/load_table_test.go index 05f371fda91..4862df0b620 100644 --- a/go/vt/vttablet/tabletserver/schema/load_table_test.go +++ b/go/vt/vttablet/tabletserver/schema/load_table_test.go @@ -161,7 +161,7 @@ func newTestLoadTable(tableType string, comment string, db *fakesqldb.DB) (*Tabl ctx := context.Background() appParams := db.ConnParams() dbaParams := db.ConnParams() - connPool := connpool.NewPool(tabletenv.NewTestEnv(nil, nil, "SchemaTest"), "", tabletenv.ConnPoolConfig{ + connPool := connpool.NewPool(tabletenv.NewTestEnv(nil, "SchemaTest"), "", tabletenv.ConnPoolConfig{ Size: 2, IdleTimeoutSeconds: 10, }) diff --git a/go/vt/vttablet/tabletserver/tabletenv/env.go b/go/vt/vttablet/tabletserver/tabletenv/env.go index 66f3db0bdc5..fe27acefbe4 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/env.go +++ b/go/vt/vttablet/tabletserver/tabletenv/env.go @@ -20,7 +20,6 @@ package tabletenv import ( "vitess.io/vitess/go/tb" - "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/servenv" ) @@ -30,36 +29,32 @@ import ( type Env interface { CheckMySQL() Config() *TabletConfig - DBConfigs() *dbconfigs.DBConfigs Exporter() *servenv.Exporter Stats() *Stats LogError() } type testEnv struct { - config *TabletConfig - dbconfigs *dbconfigs.DBConfigs - exporter *servenv.Exporter - stats *Stats + config *TabletConfig + exporter *servenv.Exporter + stats *Stats } // NewTestEnv creates an Env that can be used for tests. // CheckMySQL is a no-op. -func NewTestEnv(config *TabletConfig, dbconfigs *dbconfigs.DBConfigs, exporterName string) Env { +func NewTestEnv(config *TabletConfig, exporterName string) Env { exporter := servenv.NewExporter(exporterName, "Tablet") return &testEnv{ - config: config, - dbconfigs: dbconfigs, - exporter: exporter, - stats: NewStats(exporter), + config: config, + exporter: exporter, + stats: NewStats(exporter), } } -func (*testEnv) CheckMySQL() {} -func (te *testEnv) Config() *TabletConfig { return te.config } -func (te *testEnv) DBConfigs() *dbconfigs.DBConfigs { return te.dbconfigs } -func (te *testEnv) Exporter() *servenv.Exporter { return te.exporter } -func (te *testEnv) Stats() *Stats { return te.stats } +func (*testEnv) CheckMySQL() {} +func (te *testEnv) Config() *TabletConfig { return te.config } +func (te *testEnv) Exporter() *servenv.Exporter { return te.exporter } +func (te *testEnv) Stats() *Stats { return te.stats } func (te *testEnv) LogError() { if x := recover(); x != nil { diff --git a/go/vt/vttablet/tabletserver/tabletserver.go b/go/vt/vttablet/tabletserver/tabletserver.go index 1cc0e7589d6..be94d0f5d8e 100644 --- a/go/vt/vttablet/tabletserver/tabletserver.go +++ b/go/vt/vttablet/tabletserver/tabletserver.go @@ -157,10 +157,6 @@ type TabletServer struct { alsoAllow []topodatapb.TabletType requests sync.WaitGroup - // The following variables should be initialized only once - // before starting the tabletserver. - dbconfigs *dbconfigs.DBConfigs - // The following variables should only be accessed within // the context of a startRequest-endRequest. se *schema.Engine @@ -277,11 +273,6 @@ func (tsv *TabletServer) Config() *tabletenv.TabletConfig { return tsv.config } -// DBConfigs satisfies tabletenv.Env. -func (tsv *TabletServer) DBConfigs() *dbconfigs.DBConfigs { - return tsv.dbconfigs -} - // Stats satisfies tabletenv.Env. func (tsv *TabletServer) Stats() *tabletenv.Stats { return tsv.stats @@ -359,9 +350,9 @@ func (tsv *TabletServer) InitDBConfig(target querypb.Target, dbcfgs *dbconfigs.D return vterrors.Errorf(vtrpcpb.Code_UNKNOWN, "InitDBConfig failed, current state: %s", stateName[tsv.state]) } tsv.target = target - tsv.dbconfigs = dbcfgs + tsv.config.DB = dbcfgs - tsv.se.InitDBConfig(tsv.dbconfigs.DbaWithDB()) + tsv.se.InitDBConfig(tsv.config.DB.DbaWithDB()) return nil } @@ -514,7 +505,7 @@ func (tsv *TabletServer) decideAction(tabletType topodatapb.TabletType, serving } func (tsv *TabletServer) fullStart() (err error) { - c, err := dbconnpool.NewDBConnection(context.TODO(), tsv.dbconfigs.AppWithDB()) + c, err := dbconnpool.NewDBConnection(context.TODO(), tsv.config.DB.AppWithDB()) if err != nil { log.Errorf("error creating db app connection: %v", err) return err diff --git a/go/vt/vttablet/tabletserver/tx_engine.go b/go/vt/vttablet/tabletserver/tx_engine.go index 30e14a2cefb..51ede88c3e8 100644 --- a/go/vt/vttablet/tabletserver/tx_engine.go +++ b/go/vt/vttablet/tabletserver/tx_engine.go @@ -340,7 +340,7 @@ func (te *TxEngine) transitionTo(nextState txEngineState) error { // up the metadata tables. func (te *TxEngine) Init() error { if te.twopcEnabled { - return te.twoPC.Init(te.env.DBConfigs().DbaWithDB()) + return te.twoPC.Init(te.env.Config().DB.DbaWithDB()) } return nil } @@ -349,10 +349,10 @@ func (te *TxEngine) Init() error { // all previously prepared transactions from the redo log. // this should only be called when the state is already locked func (te *TxEngine) open() { - te.txPool.Open(te.env.DBConfigs().AppWithDB(), te.env.DBConfigs().DbaWithDB(), te.env.DBConfigs().AppDebugWithDB()) + te.txPool.Open(te.env.Config().DB.AppWithDB(), te.env.Config().DB.DbaWithDB(), te.env.Config().DB.AppDebugWithDB()) if te.twopcEnabled && te.state == AcceptingReadAndWrite { - te.twoPC.Open(te.env.DBConfigs()) + te.twoPC.Open(te.env.Config().DB) if err := te.prepareFromRedo(); err != nil { // If this operation fails, we choose to raise an alert and // continue anyway. Serving traffic is considered more important diff --git a/go/vt/vttablet/tabletserver/tx_engine_test.go b/go/vt/vttablet/tabletserver/tx_engine_test.go index 5277843ab36..d5486c55567 100644 --- a/go/vt/vttablet/tabletserver/tx_engine_test.go +++ b/go/vt/vttablet/tabletserver/tx_engine_test.go @@ -35,13 +35,13 @@ import ( func TestTxEngineClose(t *testing.T) { db := setUpQueryExecutorTest(t) defer db.Close() - dbcfgs := newDBConfigs(db) ctx := context.Background() config := tabletenv.NewDefaultConfig() + config.DB = newDBConfigs(db) config.TxPool.Size = 10 config.Oltp.TxTimeoutSeconds = 1 config.ShutdownGracePeriodSeconds = 0 - te := NewTxEngine(tabletenv.NewTestEnv(config, dbcfgs, "TabletServerTest")) + te := NewTxEngine(tabletenv.NewTestEnv(config, "TabletServerTest")) // Normal close. te.open() @@ -459,12 +459,12 @@ func TestWithInnerTests(outerT *testing.T) { } func setupTxEngine(db *fakesqldb.DB) *TxEngine { - dbcfgs := newDBConfigs(db) config := tabletenv.NewDefaultConfig() + config.DB = newDBConfigs(db) config.TxPool.Size = 10 config.Oltp.TxTimeoutSeconds = 1 config.ShutdownGracePeriodSeconds = 0 - te := NewTxEngine(tabletenv.NewTestEnv(config, dbcfgs, "TabletServerTest")) + te := NewTxEngine(tabletenv.NewTestEnv(config, "TabletServerTest")) return te } diff --git a/go/vt/vttablet/tabletserver/tx_pool_test.go b/go/vt/vttablet/tabletserver/tx_pool_test.go index e0ee396472c..7589617a7a4 100644 --- a/go/vt/vttablet/tabletserver/tx_pool_test.go +++ b/go/vt/vttablet/tabletserver/tx_pool_test.go @@ -707,5 +707,5 @@ func newTxPool() *TxPool { config.OlapReadPool.IdleTimeoutSeconds = 30 config.TxPool.IdleTimeoutSeconds = 30 limiter := &txlimiter.TxAllowAll{} - return NewTxPool(tabletenv.NewTestEnv(config, nil, "TabletServerTest"), limiter) + return NewTxPool(tabletenv.NewTestEnv(config, "TabletServerTest"), limiter) } diff --git a/go/vt/vttablet/tabletserver/txlimiter/tx_limiter_test.go b/go/vt/vttablet/tabletserver/txlimiter/tx_limiter_test.go index 913ee49b4f8..f41f9f5b271 100644 --- a/go/vt/vttablet/tabletserver/txlimiter/tx_limiter_test.go +++ b/go/vt/vttablet/tabletserver/txlimiter/tx_limiter_test.go @@ -47,7 +47,7 @@ func TestTxLimiter_DisabledAllowsAll(t *testing.T) { config.TransactionLimitByPrincipal = false config.TransactionLimitByComponent = false config.TransactionLimitBySubcomponent = false - limiter := New(tabletenv.NewTestEnv(config, nil, "TabletServerTest")) + limiter := New(tabletenv.NewTestEnv(config, "TabletServerTest")) im, ef := createCallers("", "", "", "") for i := 0; i < 5; i++ { if got, want := limiter.Get(im, ef), true; got != want { @@ -69,7 +69,7 @@ func TestTxLimiter_LimitsOnlyOffendingUser(t *testing.T) { config.TransactionLimitBySubcomponent = false // This should allow 3 slots to all users - newlimiter := New(tabletenv.NewTestEnv(config, nil, "TabletServerTest")) + newlimiter := New(tabletenv.NewTestEnv(config, "TabletServerTest")) limiter, ok := newlimiter.(*Impl) if !ok { t.Fatalf("New returned limiter of unexpected type: got %T, want %T", newlimiter, limiter) @@ -135,7 +135,7 @@ func TestTxLimiterDryRun(t *testing.T) { config.TransactionLimitBySubcomponent = false // This should allow 3 slots to all users - newlimiter := New(tabletenv.NewTestEnv(config, nil, "TabletServerTest")) + newlimiter := New(tabletenv.NewTestEnv(config, "TabletServerTest")) limiter, ok := newlimiter.(*Impl) if !ok { t.Fatalf("New returned limiter of unexpected type: got %T, want %T", newlimiter, limiter) diff --git a/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go b/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go index 4e6f756584c..d65aab2b556 100644 --- a/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go +++ b/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go @@ -48,7 +48,7 @@ func TestTxSerializer_NoHotRow(t *testing.T) { config.HotRowProtection.MaxQueueSize = 1 config.HotRowProtection.MaxGlobalQueueSize = 1 config.HotRowProtection.MaxConcurrency = 5 - txs := New(tabletenv.NewTestEnv(config, nil, "TxSerializerTest")) + txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) resetVariables(txs) done, waited, err := txs.Wait(context.Background(), "t1 where1", "t1") @@ -80,7 +80,7 @@ func TestTxSerializerRedactDebugUI(t *testing.T) { config.HotRowProtection.MaxQueueSize = 1 config.HotRowProtection.MaxGlobalQueueSize = 1 config.HotRowProtection.MaxConcurrency = 5 - txs := New(tabletenv.NewTestEnv(config, nil, "TxSerializerTest")) + txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) resetVariables(txs) done, waited, err := txs.Wait(context.Background(), "t1 where1", "t1") @@ -107,7 +107,7 @@ func TestTxSerializer(t *testing.T) { config.HotRowProtection.MaxQueueSize = 2 config.HotRowProtection.MaxGlobalQueueSize = 3 config.HotRowProtection.MaxConcurrency = 1 - txs := New(tabletenv.NewTestEnv(config, nil, "TxSerializerTest")) + txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) resetVariables(txs) // tx1. @@ -180,7 +180,7 @@ func TestTxSerializer_ConcurrentTransactions(t *testing.T) { config.HotRowProtection.MaxQueueSize = 3 config.HotRowProtection.MaxGlobalQueueSize = 3 config.HotRowProtection.MaxConcurrency = 2 - txs := New(tabletenv.NewTestEnv(config, nil, "TxSerializerTest")) + txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) resetVariables(txs) // tx1. @@ -303,7 +303,7 @@ func TestTxSerializerCancel(t *testing.T) { config.HotRowProtection.MaxQueueSize = 4 config.HotRowProtection.MaxGlobalQueueSize = 4 config.HotRowProtection.MaxConcurrency = 2 - txs := New(tabletenv.NewTestEnv(config, nil, "TxSerializerTest")) + txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) resetVariables(txs) // tx3 and tx4 will record their number once they're done waiting. @@ -404,7 +404,7 @@ func TestTxSerializerDryRun(t *testing.T) { config.HotRowProtection.MaxQueueSize = 1 config.HotRowProtection.MaxGlobalQueueSize = 2 config.HotRowProtection.MaxConcurrency = 1 - txs := New(tabletenv.NewTestEnv(config, nil, "TxSerializerTest")) + txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) resetVariables(txs) // tx1. @@ -474,7 +474,7 @@ func TestTxSerializerGlobalQueueOverflow(t *testing.T) { config.HotRowProtection.MaxQueueSize = 1 config.HotRowProtection.MaxGlobalQueueSize = 1 config.HotRowProtection.MaxConcurrency = 1 - txs := New(tabletenv.NewTestEnv(config, nil, "TxSerializerTest")) + txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) // tx1. done1, waited1, err1 := txs.Wait(context.Background(), "t1 where1", "t1") @@ -515,7 +515,7 @@ func TestTxSerializerPending(t *testing.T) { config.HotRowProtection.MaxQueueSize = 1 config.HotRowProtection.MaxGlobalQueueSize = 1 config.HotRowProtection.MaxConcurrency = 1 - txs := New(tabletenv.NewTestEnv(config, nil, "TxSerializerTest")) + txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) if got, want := txs.Pending("t1 where1"), 0; got != want { t.Errorf("there should be no pending transaction: got = %v, want = %v", got, want) } @@ -526,7 +526,7 @@ func BenchmarkTxSerializer_NoHotRow(b *testing.B) { config.HotRowProtection.MaxQueueSize = 1 config.HotRowProtection.MaxGlobalQueueSize = 1 config.HotRowProtection.MaxConcurrency = 5 - txs := New(tabletenv.NewTestEnv(config, nil, "TxSerializerTest")) + txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) b.ResetTimer() diff --git a/go/vt/vttablet/tabletserver/vstreamer/engine.go b/go/vt/vttablet/tabletserver/vstreamer/engine.go index 7b62717a162..834f5c5d878 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/engine.go +++ b/go/vt/vttablet/tabletserver/vstreamer/engine.go @@ -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.DBConfigs().AppWithDB(), vse.se, startPos, filter, vse.lvschema, send) + streamer := newVStreamer(ctx, vse.env.Config().DB.AppWithDB(), vse.se, 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.DBConfigs().AppWithDB(), vse.se, query, lastpk, vse.lvschema, send) + rowStreamer := newRowStreamer(ctx, vse.env.Config().DB.AppWithDB(), vse.se, query, lastpk, vse.lvschema, send) idx := vse.streamIdx vse.rowStreamers[idx] = rowStreamer vse.streamIdx++ @@ -230,7 +230,7 @@ func (vse *Engine) StreamResults(ctx context.Context, query string, send func(*b if !vse.isOpen { return nil, 0, errors.New("VStreamer is not open") } - resultStreamer := newResultStreamer(ctx, vse.env.DBConfigs().AppWithDB(), query, send) + resultStreamer := newResultStreamer(ctx, vse.env.Config().DB.AppWithDB(), query, send) idx := vse.streamIdx vse.resultStreamers[idx] = resultStreamer vse.streamIdx++ diff --git a/go/vt/vttablet/tabletserver/vstreamer/main_test.go b/go/vt/vttablet/tabletserver/vstreamer/main_test.go index e089729e767..774c98922e2 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/main_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/main_test.go @@ -65,8 +65,9 @@ func customEngine(t *testing.T, modifier func(mysql.ConnParams) mysql.ConnParams original, err := env.Dbcfgs.AppWithDB().MysqlParams() require.NoError(t, err) modified := modifier(*original) - dbcfgs := dbconfigs.NewTestDBConfigs(modified, modified, modified.DbName) - engine := NewEngine(tabletenv.NewTestEnv(env.TabletEnv.Config(), dbcfgs, "VStreamerTest"), env.SrvTopo, env.SchemaEngine) + config := env.TabletEnv.Config().Clone() + config.DB = dbconfigs.NewTestDBConfigs(modified, modified, modified.DbName) + engine := NewEngine(tabletenv.NewTestEnv(config, "VStreamerTest"), env.SrvTopo, env.SchemaEngine) engine.Open(env.KeyspaceName, env.Cells[0]) return engine } diff --git a/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn_test.go b/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn_test.go index 8d0f5955285..1d98e416ae7 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/snapshot_conn_test.go @@ -39,7 +39,7 @@ func TestStartSnapshot(t *testing.T) { }) ctx := context.Background() - conn, err := snapshotConnect(ctx, env.TabletEnv.DBConfigs().AppWithDB()) + conn, err := snapshotConnect(ctx, env.TabletEnv.Config().DB.AppWithDB()) require.NoError(t, err) defer conn.Close() diff --git a/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go b/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go index 14829cf9996..d4b48a24a19 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go +++ b/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go @@ -97,7 +97,8 @@ func Init() (*Env, error) { te.Dbcfgs = dbconfigs.NewTestDBConfigs(te.cluster.MySQLConnParams(), te.cluster.MySQLAppDebugConnParams(), te.cluster.DbName()) config := tabletenv.NewDefaultConfig() - te.TabletEnv = tabletenv.NewTestEnv(config, te.Dbcfgs, "VStreamerTest") + config.DB = te.Dbcfgs + te.TabletEnv = tabletenv.NewTestEnv(config, "VStreamerTest") te.Mysqld = mysqlctl.NewMysqld(te.Dbcfgs) te.SchemaEngine = schema.NewEngine(te.TabletEnv) te.SchemaEngine.InitDBConfig(te.Dbcfgs.DbaWithDB()) diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go index 347ef15a5e3..49cdbc535ef 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer_test.go @@ -1425,7 +1425,7 @@ func masterPosition(t *testing.T) string { // We use the engine's cp because there is one test that overrides // the flavor to FilePos. If so, we have to obtain the position // in that flavor format. - connParam, err := engine.env.DBConfigs().DbaWithDB().MysqlParams() + connParam, err := engine.env.Config().DB.DbaWithDB().MysqlParams() if err != nil { t.Fatal(err) } From 1747a519cfa7c594c2edb58f9cd39b10c4166509 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Sun, 19 Apr 2020 22:03:07 -0700 Subject: [PATCH 09/16] yaml: tabletenv.NewTestEnv -> NewEnv Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/heartbeat/reader_test.go | 2 +- go/vt/vttablet/heartbeat/writer_test.go | 2 +- .../vreplication/external_connector.go | 2 +- .../tabletserver/connpool/pool_test.go | 6 +++--- .../tabletserver/messager/engine_test.go | 2 +- .../messager/message_manager_test.go | 2 +- .../vttablet/tabletserver/query_engine_test.go | 4 ++-- .../tabletserver/schema/engine_test.go | 2 +- .../tabletserver/schema/load_table_test.go | 2 +- go/vt/vttablet/tabletserver/tabletenv/env.go | 6 +++--- go/vt/vttablet/tabletserver/tx_engine_test.go | 4 ++-- go/vt/vttablet/tabletserver/tx_pool_test.go | 2 +- .../tabletserver/txlimiter/tx_limiter_test.go | 6 +++--- .../txserializer/tx_serializer_test.go | 18 +++++++++--------- .../tabletserver/vstreamer/main_test.go | 2 +- .../tabletserver/vstreamer/testenv/testenv.go | 2 +- .../tabletserver/vstreamer/vstreamer.go | 12 ------------ 17 files changed, 32 insertions(+), 44 deletions(-) diff --git a/go/vt/vttablet/heartbeat/reader_test.go b/go/vt/vttablet/heartbeat/reader_test.go index af1accf37ea..ddfe0a23215 100644 --- a/go/vt/vttablet/heartbeat/reader_test.go +++ b/go/vt/vttablet/heartbeat/reader_test.go @@ -105,7 +105,7 @@ func newReader(db *fakesqldb.DB, nowFunc func() time.Time) *Reader { cp := *params dbc := dbconfigs.NewTestDBConfigs(cp, cp, "") - tr := NewReader(tabletenv.NewTestEnv(config, "ReaderTest")) + tr := NewReader(tabletenv.NewEnv(config, "ReaderTest")) tr.keyspaceShard = "test:0" tr.now = nowFunc tr.pool.Open(dbc.AppWithDB(), dbc.DbaWithDB(), dbc.AppDebugWithDB()) diff --git a/go/vt/vttablet/heartbeat/writer_test.go b/go/vt/vttablet/heartbeat/writer_test.go index 9c2c3213373..33b6a4cc506 100644 --- a/go/vt/vttablet/heartbeat/writer_test.go +++ b/go/vt/vttablet/heartbeat/writer_test.go @@ -110,7 +110,7 @@ func newTestWriter(db *fakesqldb.DB, nowFunc func() time.Time) *Writer { cp := *params dbc := dbconfigs.NewTestDBConfigs(cp, cp, "") - tw := NewWriter(tabletenv.NewTestEnv(config, "WriterTest"), topodatapb.TabletAlias{Cell: "test", Uid: 1111}) + tw := NewWriter(tabletenv.NewEnv(config, "WriterTest"), topodatapb.TabletAlias{Cell: "test", Uid: 1111}) tw.keyspaceShard = "test:0" tw.now = nowFunc tw.pool.Open(dbc.AppWithDB(), dbc.DbaWithDB(), dbc.AppDebugWithDB()) diff --git a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go index 16878f5b6b5..6aea497d318 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go +++ b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go @@ -85,7 +85,7 @@ func (ec *externalConnector) Get(name string) (*mysqlConnector, error) { return nil, vterrors.Errorf(vtrpcpb.Code_NOT_FOUND, "external mysqlConnector %v not found", name) } c := &mysqlConnector{} - c.env = tabletenv.NewTestEnv(config, name) + c.env = tabletenv.NewEnv(config, name) c.se = schema.NewEngine(c.env) c.vstreamer = vstreamer.NewEngine(c.env, nil, c.se) c.se.InitDBConfig(c.env.Config().DB.DbaWithDB()) diff --git a/go/vt/vttablet/tabletserver/connpool/pool_test.go b/go/vt/vttablet/tabletserver/connpool/pool_test.go index 6354e509687..622651d0e7b 100644 --- a/go/vt/vttablet/tabletserver/connpool/pool_test.go +++ b/go/vt/vttablet/tabletserver/connpool/pool_test.go @@ -54,7 +54,7 @@ func TestConnPoolGet(t *testing.T) { func TestConnPoolTimeout(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - connPool := NewPool(tabletenv.NewTestEnv(nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ + connPool := NewPool(tabletenv.NewEnv(nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ Size: 1, TimeoutSeconds: 1, IdleTimeoutSeconds: 10, @@ -71,7 +71,7 @@ func TestConnPoolTimeout(t *testing.T) { func TestConnPoolMaxWaiters(t *testing.T) { db := fakesqldb.New(t) defer db.Close() - connPool := NewPool(tabletenv.NewTestEnv(nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ + connPool := NewPool(tabletenv.NewEnv(nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ Size: 1, MaxWaiters: 1, }) @@ -282,7 +282,7 @@ func TestConnPoolStateWhilePoolIsOpen(t *testing.T) { } func newPool() *Pool { - return NewPool(tabletenv.NewTestEnv(nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ + return NewPool(tabletenv.NewEnv(nil, "PoolTest"), "TestPool", tabletenv.ConnPoolConfig{ Size: 100, IdleTimeoutSeconds: 10, }) diff --git a/go/vt/vttablet/tabletserver/messager/engine_test.go b/go/vt/vttablet/tabletserver/messager/engine_test.go index 8e9650240b1..b344918217c 100644 --- a/go/vt/vttablet/tabletserver/messager/engine_test.go +++ b/go/vt/vttablet/tabletserver/messager/engine_test.go @@ -172,7 +172,7 @@ func TestEngineGenerate(t *testing.T) { func newTestEngine(db *fakesqldb.DB) *Engine { config := tabletenv.NewDefaultConfig() tsv := &fakeTabletServer{ - Env: tabletenv.NewTestEnv(config, "MessagerTest"), + Env: tabletenv.NewEnv(config, "MessagerTest"), } se := schema.NewEngine(tsv) te := NewEngine(tsv, se, newFakeVStreamer()) diff --git a/go/vt/vttablet/tabletserver/messager/message_manager_test.go b/go/vt/vttablet/tabletserver/messager/message_manager_test.go index 366694843fc..a47569d01f6 100644 --- a/go/vt/vttablet/tabletserver/messager/message_manager_test.go +++ b/go/vt/vttablet/tabletserver/messager/message_manager_test.go @@ -836,7 +836,7 @@ type fakeTabletServer struct { func newFakeTabletServer() *fakeTabletServer { config := tabletenv.NewDefaultConfig() return &fakeTabletServer{ - Env: tabletenv.NewTestEnv(config, "MessagerTest"), + Env: tabletenv.NewEnv(config, "MessagerTest"), } } diff --git a/go/vt/vttablet/tabletserver/query_engine_test.go b/go/vt/vttablet/tabletserver/query_engine_test.go index 2b85cfd0930..bcbaa61554f 100644 --- a/go/vt/vttablet/tabletserver/query_engine_test.go +++ b/go/vt/vttablet/tabletserver/query_engine_test.go @@ -50,7 +50,7 @@ func TestStrictMode(t *testing.T) { // Test default behavior. config := tabletenv.NewDefaultConfig() config.DB = newDBConfigs(db) - env := tabletenv.NewTestEnv(config, "TabletServerTest") + env := tabletenv.NewEnv(config, "TabletServerTest") se := schema.NewEngine(env) qe := NewQueryEngine(env, se) qe.se.InitDBConfig(newDBConfigs(db).DbaWithDB()) @@ -280,7 +280,7 @@ func newTestQueryEngine(queryCacheSize int, idleTimeout time.Duration, strict bo config.OltpReadPool.IdleTimeoutSeconds = int(idleTimeout / 1e9) config.OlapReadPool.IdleTimeoutSeconds = int(idleTimeout / 1e9) config.TxPool.IdleTimeoutSeconds = int(idleTimeout / 1e9) - env := tabletenv.NewTestEnv(config, "TabletServerTest") + env := tabletenv.NewEnv(config, "TabletServerTest") se := schema.NewEngine(env) qe := NewQueryEngine(env, se) se.InitDBConfig(dbcfgs.DbaWithDB()) diff --git a/go/vt/vttablet/tabletserver/schema/engine_test.go b/go/vt/vttablet/tabletserver/schema/engine_test.go index 7af44a268b6..0d4a9caf297 100644 --- a/go/vt/vttablet/tabletserver/schema/engine_test.go +++ b/go/vt/vttablet/tabletserver/schema/engine_test.go @@ -305,7 +305,7 @@ func newEngine(queryCacheSize int, reloadTime time.Duration, idleTimeout time.Du config.OltpReadPool.IdleTimeoutSeconds = int(idleTimeout / 1e9) config.OlapReadPool.IdleTimeoutSeconds = int(idleTimeout / 1e9) config.TxPool.IdleTimeoutSeconds = int(idleTimeout / 1e9) - se := NewEngine(tabletenv.NewTestEnv(config, "SchemaTest")) + se := NewEngine(tabletenv.NewEnv(config, "SchemaTest")) se.InitDBConfig(newDBConfigs(db).DbaWithDB()) return se } diff --git a/go/vt/vttablet/tabletserver/schema/load_table_test.go b/go/vt/vttablet/tabletserver/schema/load_table_test.go index 4862df0b620..c8026269f18 100644 --- a/go/vt/vttablet/tabletserver/schema/load_table_test.go +++ b/go/vt/vttablet/tabletserver/schema/load_table_test.go @@ -161,7 +161,7 @@ func newTestLoadTable(tableType string, comment string, db *fakesqldb.DB) (*Tabl ctx := context.Background() appParams := db.ConnParams() dbaParams := db.ConnParams() - connPool := connpool.NewPool(tabletenv.NewTestEnv(nil, "SchemaTest"), "", tabletenv.ConnPoolConfig{ + connPool := connpool.NewPool(tabletenv.NewEnv(nil, "SchemaTest"), "", tabletenv.ConnPoolConfig{ Size: 2, IdleTimeoutSeconds: 10, }) diff --git a/go/vt/vttablet/tabletserver/tabletenv/env.go b/go/vt/vttablet/tabletserver/tabletenv/env.go index fe27acefbe4..c7202080c4d 100644 --- a/go/vt/vttablet/tabletserver/tabletenv/env.go +++ b/go/vt/vttablet/tabletserver/tabletenv/env.go @@ -40,9 +40,9 @@ type testEnv struct { stats *Stats } -// NewTestEnv creates an Env that can be used for tests. -// CheckMySQL is a no-op. -func NewTestEnv(config *TabletConfig, exporterName string) Env { +// NewEnv creates an Env that can be used for tabletserver subcomponents +// without an actual TabletServer. +func NewEnv(config *TabletConfig, exporterName string) Env { exporter := servenv.NewExporter(exporterName, "Tablet") return &testEnv{ config: config, diff --git a/go/vt/vttablet/tabletserver/tx_engine_test.go b/go/vt/vttablet/tabletserver/tx_engine_test.go index d5486c55567..85e80d34557 100644 --- a/go/vt/vttablet/tabletserver/tx_engine_test.go +++ b/go/vt/vttablet/tabletserver/tx_engine_test.go @@ -41,7 +41,7 @@ func TestTxEngineClose(t *testing.T) { config.TxPool.Size = 10 config.Oltp.TxTimeoutSeconds = 1 config.ShutdownGracePeriodSeconds = 0 - te := NewTxEngine(tabletenv.NewTestEnv(config, "TabletServerTest")) + te := NewTxEngine(tabletenv.NewEnv(config, "TabletServerTest")) // Normal close. te.open() @@ -464,7 +464,7 @@ func setupTxEngine(db *fakesqldb.DB) *TxEngine { config.TxPool.Size = 10 config.Oltp.TxTimeoutSeconds = 1 config.ShutdownGracePeriodSeconds = 0 - te := NewTxEngine(tabletenv.NewTestEnv(config, "TabletServerTest")) + te := NewTxEngine(tabletenv.NewEnv(config, "TabletServerTest")) return te } diff --git a/go/vt/vttablet/tabletserver/tx_pool_test.go b/go/vt/vttablet/tabletserver/tx_pool_test.go index 7589617a7a4..df49e45f26e 100644 --- a/go/vt/vttablet/tabletserver/tx_pool_test.go +++ b/go/vt/vttablet/tabletserver/tx_pool_test.go @@ -707,5 +707,5 @@ func newTxPool() *TxPool { config.OlapReadPool.IdleTimeoutSeconds = 30 config.TxPool.IdleTimeoutSeconds = 30 limiter := &txlimiter.TxAllowAll{} - return NewTxPool(tabletenv.NewTestEnv(config, "TabletServerTest"), limiter) + return NewTxPool(tabletenv.NewEnv(config, "TabletServerTest"), limiter) } diff --git a/go/vt/vttablet/tabletserver/txlimiter/tx_limiter_test.go b/go/vt/vttablet/tabletserver/txlimiter/tx_limiter_test.go index f41f9f5b271..3a4133b54d6 100644 --- a/go/vt/vttablet/tabletserver/txlimiter/tx_limiter_test.go +++ b/go/vt/vttablet/tabletserver/txlimiter/tx_limiter_test.go @@ -47,7 +47,7 @@ func TestTxLimiter_DisabledAllowsAll(t *testing.T) { config.TransactionLimitByPrincipal = false config.TransactionLimitByComponent = false config.TransactionLimitBySubcomponent = false - limiter := New(tabletenv.NewTestEnv(config, "TabletServerTest")) + limiter := New(tabletenv.NewEnv(config, "TabletServerTest")) im, ef := createCallers("", "", "", "") for i := 0; i < 5; i++ { if got, want := limiter.Get(im, ef), true; got != want { @@ -69,7 +69,7 @@ func TestTxLimiter_LimitsOnlyOffendingUser(t *testing.T) { config.TransactionLimitBySubcomponent = false // This should allow 3 slots to all users - newlimiter := New(tabletenv.NewTestEnv(config, "TabletServerTest")) + newlimiter := New(tabletenv.NewEnv(config, "TabletServerTest")) limiter, ok := newlimiter.(*Impl) if !ok { t.Fatalf("New returned limiter of unexpected type: got %T, want %T", newlimiter, limiter) @@ -135,7 +135,7 @@ func TestTxLimiterDryRun(t *testing.T) { config.TransactionLimitBySubcomponent = false // This should allow 3 slots to all users - newlimiter := New(tabletenv.NewTestEnv(config, "TabletServerTest")) + newlimiter := New(tabletenv.NewEnv(config, "TabletServerTest")) limiter, ok := newlimiter.(*Impl) if !ok { t.Fatalf("New returned limiter of unexpected type: got %T, want %T", newlimiter, limiter) diff --git a/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go b/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go index d65aab2b556..ecbc8c76dc8 100644 --- a/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go +++ b/go/vt/vttablet/tabletserver/txserializer/tx_serializer_test.go @@ -48,7 +48,7 @@ func TestTxSerializer_NoHotRow(t *testing.T) { config.HotRowProtection.MaxQueueSize = 1 config.HotRowProtection.MaxGlobalQueueSize = 1 config.HotRowProtection.MaxConcurrency = 5 - txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) + txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) resetVariables(txs) done, waited, err := txs.Wait(context.Background(), "t1 where1", "t1") @@ -80,7 +80,7 @@ func TestTxSerializerRedactDebugUI(t *testing.T) { config.HotRowProtection.MaxQueueSize = 1 config.HotRowProtection.MaxGlobalQueueSize = 1 config.HotRowProtection.MaxConcurrency = 5 - txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) + txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) resetVariables(txs) done, waited, err := txs.Wait(context.Background(), "t1 where1", "t1") @@ -107,7 +107,7 @@ func TestTxSerializer(t *testing.T) { config.HotRowProtection.MaxQueueSize = 2 config.HotRowProtection.MaxGlobalQueueSize = 3 config.HotRowProtection.MaxConcurrency = 1 - txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) + txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) resetVariables(txs) // tx1. @@ -180,7 +180,7 @@ func TestTxSerializer_ConcurrentTransactions(t *testing.T) { config.HotRowProtection.MaxQueueSize = 3 config.HotRowProtection.MaxGlobalQueueSize = 3 config.HotRowProtection.MaxConcurrency = 2 - txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) + txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) resetVariables(txs) // tx1. @@ -303,7 +303,7 @@ func TestTxSerializerCancel(t *testing.T) { config.HotRowProtection.MaxQueueSize = 4 config.HotRowProtection.MaxGlobalQueueSize = 4 config.HotRowProtection.MaxConcurrency = 2 - txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) + txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) resetVariables(txs) // tx3 and tx4 will record their number once they're done waiting. @@ -404,7 +404,7 @@ func TestTxSerializerDryRun(t *testing.T) { config.HotRowProtection.MaxQueueSize = 1 config.HotRowProtection.MaxGlobalQueueSize = 2 config.HotRowProtection.MaxConcurrency = 1 - txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) + txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) resetVariables(txs) // tx1. @@ -474,7 +474,7 @@ func TestTxSerializerGlobalQueueOverflow(t *testing.T) { config.HotRowProtection.MaxQueueSize = 1 config.HotRowProtection.MaxGlobalQueueSize = 1 config.HotRowProtection.MaxConcurrency = 1 - txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) + txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) // tx1. done1, waited1, err1 := txs.Wait(context.Background(), "t1 where1", "t1") @@ -515,7 +515,7 @@ func TestTxSerializerPending(t *testing.T) { config.HotRowProtection.MaxQueueSize = 1 config.HotRowProtection.MaxGlobalQueueSize = 1 config.HotRowProtection.MaxConcurrency = 1 - txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) + txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) if got, want := txs.Pending("t1 where1"), 0; got != want { t.Errorf("there should be no pending transaction: got = %v, want = %v", got, want) } @@ -526,7 +526,7 @@ func BenchmarkTxSerializer_NoHotRow(b *testing.B) { config.HotRowProtection.MaxQueueSize = 1 config.HotRowProtection.MaxGlobalQueueSize = 1 config.HotRowProtection.MaxConcurrency = 5 - txs := New(tabletenv.NewTestEnv(config, "TxSerializerTest")) + txs := New(tabletenv.NewEnv(config, "TxSerializerTest")) b.ResetTimer() diff --git a/go/vt/vttablet/tabletserver/vstreamer/main_test.go b/go/vt/vttablet/tabletserver/vstreamer/main_test.go index 774c98922e2..080817d0803 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/main_test.go +++ b/go/vt/vttablet/tabletserver/vstreamer/main_test.go @@ -67,7 +67,7 @@ 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) - engine := NewEngine(tabletenv.NewTestEnv(config, "VStreamerTest"), env.SrvTopo, env.SchemaEngine) + engine := NewEngine(tabletenv.NewEnv(config, "VStreamerTest"), env.SrvTopo, env.SchemaEngine) engine.Open(env.KeyspaceName, env.Cells[0]) return engine } diff --git a/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go b/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go index d4b48a24a19..a3cb74713f6 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go +++ b/go/vt/vttablet/tabletserver/vstreamer/testenv/testenv.go @@ -98,7 +98,7 @@ func Init() (*Env, error) { te.Dbcfgs = dbconfigs.NewTestDBConfigs(te.cluster.MySQLConnParams(), te.cluster.MySQLAppDebugConnParams(), te.cluster.DbName()) config := tabletenv.NewDefaultConfig() config.DB = te.Dbcfgs - te.TabletEnv = tabletenv.NewTestEnv(config, "VStreamerTest") + te.TabletEnv = tabletenv.NewEnv(config, "VStreamerTest") te.Mysqld = mysqlctl.NewMysqld(te.Dbcfgs) te.SchemaEngine = schema.NewEngine(te.TabletEnv) te.SchemaEngine.InitDBConfig(te.Dbcfgs.DbaWithDB()) diff --git a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go index 411f3cff8bb..afe047fb3bf 100644 --- a/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go +++ b/go/vt/vttablet/tabletserver/vstreamer/vstreamer.go @@ -32,7 +32,6 @@ import ( "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/vt/vtgate/vindexes" "vitess.io/vitess/go/vt/vttablet/tabletserver/schema" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" @@ -47,17 +46,6 @@ var PacketSize = flag.Int("vstream_packet_size", 30000, "Suggested packet size f // between the two timeouts. var HeartbeatTime = 900 * time.Millisecond -// VStreamer exposes an externally usable interface to vstreamer. -type VStreamer interface { - Stream() error - Cancel() -} - -// NewVStreamer returns a VStreamer. -func NewVStreamer(ctx context.Context, cp dbconfigs.Connector, se *schema.Engine, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) VStreamer { - return newVStreamer(ctx, cp, se, startPos, filter, &localVSchema{vschema: &vindexes.VSchema{}}, send) -} - // vschemaUpdateCount is for testing only. // vstreamer is a mutex free data structure. So, it's not safe to access its members // from a test. Since VSchema gets updated asynchronously, there's no way for a test From 407d845537a791c0a446ef83d0307e6d177f1f9c Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Mon, 20 Apr 2020 16:31:53 -0700 Subject: [PATCH 10/16] vrepl: reinstate VStreamClient.Open Signed-off-by: Sugu Sougoumarane --- .../tabletmanager/vreplication/controller.go | 7 +++-- .../vreplication/external_connector.go | 30 +++++++++++-------- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/go/vt/vttablet/tabletmanager/vreplication/controller.go b/go/vt/vttablet/tabletmanager/vreplication/controller.go index a45f6b3b5c5..058f54d6f70 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/controller.go +++ b/go/vt/vttablet/tabletmanager/vreplication/controller.go @@ -225,10 +225,13 @@ func (ct *controller) runBlp(ctx context.Context) (err error) { var err error if name := ct.source.GetExternalMysql(); name != "" { vsClient, err = ct.vre.ec.Get(name) + if err != nil { + return err + } } else { - vsClient, err = newTabletConnector(tablet) + vsClient = newTabletConnector(tablet) } - if err != nil { + if err := vsClient.Open(ctx); err != nil { return err } defer vsClient.Close(ctx) diff --git a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go index 6aea497d318..7c754f551b3 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go +++ b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go @@ -42,7 +42,8 @@ var ( // vstreamerClient exposes the core interface of a vstreamer type vstreamerClient interface { - Close(context.Context) + Open(context.Context) error + Close(context.Context) error // VStream streams VReplication events based on the specified filter. VStream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error @@ -114,7 +115,12 @@ func (c *mysqlConnector) shutdown() { c.se.Close() } -func (c *mysqlConnector) Close(ctx context.Context) { +func (c *mysqlConnector) Open(ctx context.Context) error { + return nil +} + +func (c *mysqlConnector) Close(ctx context.Context) error { + return nil } func (c *mysqlConnector) VStream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { @@ -141,8 +147,8 @@ type tabletConnector struct { qs queryservice.QueryService } -func newTabletConnector(tablet *topodatapb.Tablet) (*tabletConnector, error) { - tc := &tabletConnector{ +func newTabletConnector(tablet *topodatapb.Tablet) *tabletConnector { + return &tabletConnector{ tablet: tablet, target: &querypb.Target{ Keyspace: tablet.Keyspace, @@ -150,16 +156,16 @@ func newTabletConnector(tablet *topodatapb.Tablet) (*tabletConnector, error) { TabletType: tablet.Type, }, } - qs, err := tabletconn.GetDialer()(tc.tablet, grpcclient.FailFast(true)) - if err != nil { - return nil, err - } - tc.qs = qs - return tc, nil } -func (tc *tabletConnector) Close(ctx context.Context) { - tc.qs.Close(ctx) +func (tc *tabletConnector) Open(ctx context.Context) error { + var err error + tc.qs, err = tabletconn.GetDialer()(tc.tablet, grpcclient.FailFast(true)) + return err +} + +func (tc *tabletConnector) Close(ctx context.Context) error { + return tc.qs.Close(ctx) } func (tc *tabletConnector) VStream(ctx context.Context, startPos string, filter *binlogdatapb.Filter, send func([]*binlogdatapb.VEvent) error) error { From 6c6444d55b93dbd612a1a5812b2a494f2ae39a74 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Tue, 21 Apr 2020 16:13:49 -0700 Subject: [PATCH 11/16] vrepl: migration e2e test WIP Signed-off-by: Sugu Sougoumarane --- go/test/endtoend/migration/migration_test.go | 195 +++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 go/test/endtoend/migration/migration_test.go diff --git a/go/test/endtoend/migration/migration_test.go b/go/test/endtoend/migration/migration_test.go new file mode 100644 index 00000000000..28c6b4d249b --- /dev/null +++ b/go/test/endtoend/migration/migration_test.go @@ -0,0 +1,195 @@ +/* +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 migration + +import ( + "context" + "fmt" + "path" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/endtoend/cluster" +) + +var ( + clusterInstance *cluster.LocalProcessCluster + vtParams *mysql.ConnParams + cell = "test" + keyspaces = make(map[string]*cluster.Keyspace) + + legacyMerchant = cluster.Keyspace{ + Name: "merchant", + SchemaSQL: ` +create table merchant(mname varchar(128), category varchar(128), primary key(mname)); +create table customer(cid int, name varbinary(128), primary key(cid)); +create table orders(oid int, cid int, pid int, mname varchar(128), price int, primary key(oid)); +`, + } + legacyMerchantData = ` +insert into merchant(mname, category) values('monoprice', 'electronics'), ('newegg', 'electronics'); +insert into customer(cid, name) values(1, 'john'), (2, 'paul'), (3, 'ringo'), (4, 'led'); +insert into orders(oid, cid, mname, pid, price) values(1, 1, 'monoprice', 1, 10), (2, 1, 'newegg', 2, 15), (3, 4, 'monoprice', 2, 20); +` + + legacyProduct = cluster.Keyspace{ + Name: "product", + SchemaSQL: ` +create table product(pid int, description varbinary(128), primary key(pid)); +`, + } + legacyProductData = ` +insert into product(pid, description) values(1, 'keyboard'), (2, 'monitor'); +` + + commerce = cluster.Keyspace{ + Name: "commerce", + SchemaSQL: ` +create table product(pid int, description varbinary(128), primary key(pid)); +create table merchant(mname varchar(128), category varchar(128), primary key(mname)); +`, + VSchema: `{ + "tables": { + "product": {}, + "merchant": {} + } +}`, + } + + customer = cluster.Keyspace{ + Name: "customer", + SchemaSQL: ` +create table customer(cid int, name varbinary(128), primary key(cid)); +create table orders(oid int, cid int, pid int, mname varchar(128), price int, primary key(oid)); +`, + VSchema: ` +{ + "sharded": true, + "vindexes": { + "reverse_bits": { + "type": "reverse_bits" + } + }, + "tables": { + "customer": { + "column_vindexes": [{ + "column": "cid", + "name": "reverse_bits" + }] + }, + "orders": { + "column_vindexes": [{ + "column": "cid", + "name": "reverse_bits" + }], + "auto_increment": { + "column": "cid", + "sequence": "customer_seq" + } + } + } +}`, + } +) + +func TestMigration(t *testing.T) { + startCluster(t) + defer clusterInstance.Teardown() + + result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("ListAllTablets") + require.NoError(t, err) + fmt.Printf("result:\n%v", result) + fmt.Printf("Commerce vttablet: %v\n", keyspaces[commerce.Name].Shards[0].Vttablets[0].VttabletProcess) + fmt.Printf("Commerce mysqlctl: %v\n", keyspaces[commerce.Name].Shards[0].Vttablets[0].MysqlctlProcess) + + buf := &strings.Builder{} + fmt.Fprintf(buf, "externalConnections:\n") + buildConnYaml(buf, keyspaces[legacyProduct.Name]) + buildConnYaml(buf, keyspaces[legacyMerchant.Name]) + fmt.Printf("%s\n", buf.String()) +} + +func startCluster(t *testing.T) { + clusterInstance = cluster.NewCluster(cell, "localhost") + + err := clusterInstance.StartTopo() + if err != nil { + t.Fatal(err) + } + + createKeyspace(t, legacyMerchant, []string{"0"}) + createKeyspace(t, legacyProduct, []string{"0"}) + createKeyspace(t, commerce, []string{"0"}) + createKeyspace(t, customer, []string{"-80", "80-"}) + + err = clusterInstance.StartVtgate() + require.NoError(t, err) + vtParams = &mysql.ConnParams{ + Host: clusterInstance.Hostname, + Port: clusterInstance.VtgateMySQLPort, + } + + populate(t, "merchant:0", legacyMerchantData) + populate(t, "product:0", legacyProductData) +} + +func createKeyspace(t *testing.T, ks cluster.Keyspace, shards []string) { + t.Helper() + + err := clusterInstance.StartKeyspace(ks, []string{"0"}, 1, false) + require.NoError(t, err) + keyspaces[ks.Name] = &clusterInstance.Keyspaces[len(clusterInstance.Keyspaces)-1] +} + +func populate(t *testing.T, target, sql string) { + t.Helper() + + fmt.Printf("Populating: %v\n", target) + conn, err := mysql.Connect(context.Background(), vtParams) + require.NoError(t, err) + execQuery(t, conn, fmt.Sprintf("use `%v`", target)) + lines := strings.Split(sql, "\n") + for _, line := range lines { + if line == "" { + continue + } + execQuery(t, conn, line) + } +} + +func buildConnYaml(buf *strings.Builder, ks *cluster.Keyspace) { + connFormat := ` %s: + socket: %s + dbName: vt_%s + app: + user: vt_app + dba: + user: vt_dba +` + vttablet := ks.Shards[0].Vttablets[0].VttabletProcess + fmt.Fprintf(buf, connFormat, ks.Name, path.Join(vttablet.Directory, "mysql.sock"), ks.Name) +} + +func execQuery(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { + t.Helper() + qr, err := conn.ExecuteFetch(query, 10000, true) + require.Nil(t, err) + return qr +} From 4c836a11ae3ea20640dd0f0e9c1bec4420c66e4a Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Tue, 21 Apr 2020 19:16:51 -0700 Subject: [PATCH 12/16] vrepl: customizers for cluster creation Signed-off-by: Sugu Sougoumarane --- go/test/endtoend/cluster/cluster_process.go | 12 ++++++++++- go/test/endtoend/migration/migration_test.go | 22 +++++++++++++------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/go/test/endtoend/cluster/cluster_process.go b/go/test/endtoend/cluster/cluster_process.go index 7fa2ccee13b..8af0043e1dc 100644 --- a/go/test/endtoend/cluster/cluster_process.go +++ b/go/test/endtoend/cluster/cluster_process.go @@ -220,7 +220,9 @@ func (cluster *LocalProcessCluster) StartUnshardedKeyspace(keyspace Keyspace, re // shardName : list of shard names // replicaCount: total number of replicas excluding master and rdonly // rdonly: whether readonly tablets needed -func (cluster *LocalProcessCluster) StartKeyspace(keyspace Keyspace, shardNames []string, replicaCount int, rdonly bool) (err error) { +// customizers: functions like "func(*VttabletProcess)" that can modify settings of various objects +// after they're created. +func (cluster *LocalProcessCluster) StartKeyspace(keyspace Keyspace, shardNames []string, replicaCount int, rdonly bool, customizers ...interface{}) (err error) { totalTabletsRequired := replicaCount + 1 // + 1 is for master if rdonly { totalTabletsRequired = totalTabletsRequired + 1 // + 1 for rdonly @@ -276,6 +278,14 @@ func (cluster *LocalProcessCluster) StartKeyspace(keyspace Keyspace, shardNames cluster.EnableSemiSync) tablet.Alias = tablet.VttabletProcess.TabletPath shard.Vttablets = append(shard.Vttablets, tablet) + // Apply customizations + for _, customizer := range customizers { + if f, ok := customizer.(func(*VttabletProcess)); ok { + f(tablet.VttabletProcess) + } else { + return fmt.Errorf("type mismatch on customizer: %T", customizer) + } + } } // wait till all mysqlctl is instantiated diff --git a/go/test/endtoend/migration/migration_test.go b/go/test/endtoend/migration/migration_test.go index 28c6b4d249b..405699e399c 100644 --- a/go/test/endtoend/migration/migration_test.go +++ b/go/test/endtoend/migration/migration_test.go @@ -17,8 +17,10 @@ limitations under the License. package migration import ( + "bytes" "context" "fmt" + "io/ioutil" "path" "strings" "testing" @@ -116,14 +118,20 @@ func TestMigration(t *testing.T) { result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("ListAllTablets") require.NoError(t, err) fmt.Printf("result:\n%v", result) - fmt.Printf("Commerce vttablet: %v\n", keyspaces[commerce.Name].Shards[0].Vttablets[0].VttabletProcess) - fmt.Printf("Commerce mysqlctl: %v\n", keyspaces[commerce.Name].Shards[0].Vttablets[0].MysqlctlProcess) - buf := &strings.Builder{} + buf := &bytes.Buffer{} fmt.Fprintf(buf, "externalConnections:\n") buildConnYaml(buf, keyspaces[legacyProduct.Name]) buildConnYaml(buf, keyspaces[legacyMerchant.Name]) fmt.Printf("%s\n", buf.String()) + yamlFile := path.Join(clusterInstance.TmpDirectory, "external.yaml") + err = ioutil.WriteFile(yamlFile, buf.Bytes(), 0644) + require.NoError(t, err) + extraArgs := func(vt *cluster.VttabletProcess) { + vt.ExtraArgs = append(vt.ExtraArgs, "-tablet_config", yamlFile) + } + createKeyspace(t, commerce, []string{"0"}, extraArgs) + createKeyspace(t, customer, []string{"-80", "80-"}, extraArgs) } func startCluster(t *testing.T) { @@ -136,8 +144,6 @@ func startCluster(t *testing.T) { createKeyspace(t, legacyMerchant, []string{"0"}) createKeyspace(t, legacyProduct, []string{"0"}) - createKeyspace(t, commerce, []string{"0"}) - createKeyspace(t, customer, []string{"-80", "80-"}) err = clusterInstance.StartVtgate() require.NoError(t, err) @@ -150,10 +156,10 @@ func startCluster(t *testing.T) { populate(t, "product:0", legacyProductData) } -func createKeyspace(t *testing.T, ks cluster.Keyspace, shards []string) { +func createKeyspace(t *testing.T, ks cluster.Keyspace, shards []string, customizers ...interface{}) { t.Helper() - err := clusterInstance.StartKeyspace(ks, []string{"0"}, 1, false) + err := clusterInstance.StartKeyspace(ks, []string{"0"}, 1, false, customizers...) require.NoError(t, err) keyspaces[ks.Name] = &clusterInstance.Keyspaces[len(clusterInstance.Keyspaces)-1] } @@ -174,7 +180,7 @@ func populate(t *testing.T, target, sql string) { } } -func buildConnYaml(buf *strings.Builder, ks *cluster.Keyspace) { +func buildConnYaml(buf *bytes.Buffer, ks *cluster.Keyspace) { connFormat := ` %s: socket: %s dbName: vt_%s From 528c84bafeb35f1ff808c68b5bd3a31c0886aaa9 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Tue, 21 Apr 2020 22:21:58 -0700 Subject: [PATCH 13/16] vrepl: migration tests working Signed-off-by: Sugu Sougoumarane --- go/test/endtoend/migration/migration_test.go | 263 ++++++++++++------- 1 file changed, 172 insertions(+), 91 deletions(-) diff --git a/go/test/endtoend/migration/migration_test.go b/go/test/endtoend/migration/migration_test.go index 405699e399c..fa6950e7041 100644 --- a/go/test/endtoend/migration/migration_test.go +++ b/go/test/endtoend/migration/migration_test.go @@ -24,11 +24,14 @@ import ( "path" "strings" "testing" + "time" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/test/endtoend/cluster" + binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" ) var ( @@ -37,104 +40,151 @@ var ( cell = "test" keyspaces = make(map[string]*cluster.Keyspace) - legacyMerchant = cluster.Keyspace{ - Name: "merchant", + legacyProduct = cluster.Keyspace{ + Name: "product", SchemaSQL: ` -create table merchant(mname varchar(128), category varchar(128), primary key(mname)); -create table customer(cid int, name varbinary(128), primary key(cid)); -create table orders(oid int, cid int, pid int, mname varchar(128), price int, primary key(oid)); +create table product(pid bigint, description varbinary(128), primary key(pid)); `, } - legacyMerchantData = ` -insert into merchant(mname, category) values('monoprice', 'electronics'), ('newegg', 'electronics'); -insert into customer(cid, name) values(1, 'john'), (2, 'paul'), (3, 'ringo'), (4, 'led'); -insert into orders(oid, cid, mname, pid, price) values(1, 1, 'monoprice', 1, 10), (2, 1, 'newegg', 2, 15), (3, 4, 'monoprice', 2, 20); + legacyProductData = ` +insert into vt_product.product(pid, description) values(1, 'keyboard'), (2, 'monitor'); ` - legacyProduct = cluster.Keyspace{ - Name: "product", + legacyCustomer = cluster.Keyspace{ + Name: "customer", SchemaSQL: ` -create table product(pid int, description varbinary(128), primary key(pid)); +create table customer(cid bigint, name varbinary(128), primary key(cid)); +create table orders(oid bigint, cid bigint, pid bigint, mname varchar(128), price bigint, primary key(oid)); `, } - legacyProductData = ` -insert into product(pid, description) values(1, 'keyboard'), (2, 'monitor'); + legacyCustomerData = ` +insert into vt_customer.customer(cid, name) values(1, 'john'), (2, 'paul'), (3, 'ringo'); +insert into vt_customer.orders(oid, cid, mname, pid, price) values(1, 1, 'monoprice', 1, 10), (2, 1, 'newegg', 2, 15); ` commerce = cluster.Keyspace{ Name: "commerce", SchemaSQL: ` -create table product(pid int, description varbinary(128), primary key(pid)); -create table merchant(mname varchar(128), category varchar(128), primary key(mname)); +create table product(pid bigint, description varbinary(128), primary key(pid)); +create table customer(cid bigint, name varbinary(128), primary key(cid)); +create table orders(oid bigint, cid bigint, pid bigint, mname varchar(128), price bigint, primary key(oid)); `, VSchema: `{ "tables": { "product": {}, - "merchant": {} + "customer": {}, + "orders": {} } }`, } - customer = cluster.Keyspace{ - Name: "customer", - SchemaSQL: ` -create table customer(cid int, name varbinary(128), primary key(cid)); -create table orders(oid int, cid int, pid int, mname varchar(128), price int, primary key(oid)); -`, - VSchema: ` -{ - "sharded": true, - "vindexes": { - "reverse_bits": { - "type": "reverse_bits" - } - }, - "tables": { - "customer": { - "column_vindexes": [{ - "column": "cid", - "name": "reverse_bits" - }] - }, - "orders": { - "column_vindexes": [{ - "column": "cid", - "name": "reverse_bits" - }], - "auto_increment": { - "column": "cid", - "sequence": "customer_seq" - } - } - } -}`, - } + connFormat = `externalConnections: + product: + socket: %s + dbName: vt_product + app: + user: vt_app + dba: + user: vt_dba + customer: + flavor: FilePos + socket: %s + dbName: vt_customer + app: + user: vt_app + dba: + user: vt_dba +` ) func TestMigration(t *testing.T) { - startCluster(t) + yamlFile := startCluster(t) defer clusterInstance.Teardown() - result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("ListAllTablets") + tabletConfig := func(vt *cluster.VttabletProcess) { + vt.ExtraArgs = append(vt.ExtraArgs, "-tablet_config", yamlFile) + } + createKeyspace(t, commerce, []string{"0"}, tabletConfig) + err := clusterInstance.VtctlclientProcess.ExecuteCommand("RebuildKeyspaceGraph", "commerce") require.NoError(t, err) - fmt.Printf("result:\n%v", result) - buf := &bytes.Buffer{} - fmt.Fprintf(buf, "externalConnections:\n") - buildConnYaml(buf, keyspaces[legacyProduct.Name]) - buildConnYaml(buf, keyspaces[legacyMerchant.Name]) - fmt.Printf("%s\n", buf.String()) - yamlFile := path.Join(clusterInstance.TmpDirectory, "external.yaml") - err = ioutil.WriteFile(yamlFile, buf.Bytes(), 0644) + err = clusterInstance.StartVtgate() require.NoError(t, err) - extraArgs := func(vt *cluster.VttabletProcess) { - vt.ExtraArgs = append(vt.ExtraArgs, "-tablet_config", yamlFile) + + migrate(t, "product", "commerce", []string{"product"}) + migrate(t, "customer", "commerce", []string{"customer"}) + migrate(t, "customer", "commerce", []string{"orders"}) + vttablet := keyspaces["commerce"].Shards[0].Vttablets[0].VttabletProcess + waitForVReplicationToCatchup(t, vttablet, 1*time.Second) + + vtParams = &mysql.ConnParams{ + Host: clusterInstance.Hostname, + Port: clusterInstance.VtgateMySQLPort, + } + conn, err := mysql.Connect(context.Background(), vtParams) + require.NoError(t, err) + defer conn.Close() + + execQuery(t, conn, "use `commerce`") + testcases := []struct { + query string + result *sqltypes.Result + }{{ + query: "select * from product", + result: sqltypes.MakeTestResult(sqltypes.MakeTestFields( + "pid|description", + "int64|varbinary"), + "1|keyboard", + "2|monitor", + ), + }, { + query: "select * from customer", + result: sqltypes.MakeTestResult(sqltypes.MakeTestFields( + "cid|name", + "int64|varbinary"), + "1|john", + "2|paul", + "3|ringo", + ), + }, { + query: "select * from orders", + result: sqltypes.MakeTestResult(sqltypes.MakeTestFields( + "oid|cid|mname|pid|price", + "int64|int64|int64|varchar|int64"), + "1|1|1|monoprice|10", + "2|1|2|newegg|15", + ), + }} + for _, tcase := range testcases { + result := execQuery(t, conn, tcase.query) + // nil out the fields because they're too detailed. + result.Fields = nil + tcase.result.Fields = nil + assert.Equal(t, tcase.result, result, tcase.query) + } +} + +func migrate(t *testing.T, fromdb, toks string, tables []string) { + bls := &binlogdatapb.BinlogSource{ + ExternalMysql: fromdb, + Filter: &binlogdatapb.Filter{}, } - createKeyspace(t, commerce, []string{"0"}, extraArgs) - createKeyspace(t, customer, []string{"-80", "80-"}, extraArgs) + for _, table := range tables { + bls.Filter.Rules = append(bls.Filter.Rules, &binlogdatapb.Rule{Match: table}) + } + val := sqltypes.NewVarBinary(fmt.Sprintf("%v", bls)) + var sqlEscaped bytes.Buffer + val.EncodeSQL(&sqlEscaped) + query := fmt.Sprintf("insert into _vt.vreplication "+ + "(workflow, db_name, source, pos, max_tps, max_replication_lag, tablet_types, time_updated, transaction_timestamp, state) values"+ + "('%s', '%s', %s, '', 9999, 9999, 'master', 0, 0, 'Running')", tables[0], "vt_"+toks, sqlEscaped.String()) + fmt.Printf("query: %s\n", query) + vttablet := keyspaces[toks].Shards[0].Vttablets[0].VttabletProcess + err := clusterInstance.VtctlclientProcess.ExecuteCommand("VReplicationExec", vttablet.TabletPath, query) + require.NoError(t, err) } -func startCluster(t *testing.T) { +func startCluster(t *testing.T) string { clusterInstance = cluster.NewCluster(cell, "localhost") err := clusterInstance.StartTopo() @@ -142,35 +192,45 @@ func startCluster(t *testing.T) { t.Fatal(err) } - createKeyspace(t, legacyMerchant, []string{"0"}) + createKeyspace(t, legacyCustomer, []string{"0"}) createKeyspace(t, legacyProduct, []string{"0"}) - err = clusterInstance.StartVtgate() - require.NoError(t, err) - vtParams = &mysql.ConnParams{ - Host: clusterInstance.Hostname, - Port: clusterInstance.VtgateMySQLPort, - } + productSocket := path.Join(keyspaces["product"].Shards[0].Vttablets[0].VttabletProcess.Directory, "mysql.sock") + fmt.Println("product", productSocket) + customerSocket := path.Join(keyspaces["customer"].Shards[0].Vttablets[0].VttabletProcess.Directory, "mysql.sock") + fmt.Println("customer", customerSocket) + + populate(t, productSocket, legacyProductData) + populate(t, customerSocket, legacyCustomerData) - populate(t, "merchant:0", legacyMerchantData) - populate(t, "product:0", legacyProductData) + buf := &bytes.Buffer{} + fmt.Fprintf(buf, "externalConnections:\n") + tabletConfig := fmt.Sprintf(connFormat, productSocket, customerSocket) + fmt.Printf("tablet_config:\n%s\n", tabletConfig) + yamlFile := path.Join(clusterInstance.TmpDirectory, "external.yaml") + err = ioutil.WriteFile(yamlFile, []byte(tabletConfig), 0644) + require.NoError(t, err) + return yamlFile } func createKeyspace(t *testing.T, ks cluster.Keyspace, shards []string, customizers ...interface{}) { t.Helper() - err := clusterInstance.StartKeyspace(ks, []string{"0"}, 1, false, customizers...) + err := clusterInstance.StartKeyspace(ks, shards, 1, false, customizers...) require.NoError(t, err) keyspaces[ks.Name] = &clusterInstance.Keyspaces[len(clusterInstance.Keyspaces)-1] } -func populate(t *testing.T, target, sql string) { +func populate(t *testing.T, socket, sql string) { t.Helper() - fmt.Printf("Populating: %v\n", target) - conn, err := mysql.Connect(context.Background(), vtParams) + params := &mysql.ConnParams{ + UnixSocket: socket, + Uname: "vt_app", + } + conn, err := mysql.Connect(context.Background(), params) require.NoError(t, err) - execQuery(t, conn, fmt.Sprintf("use `%v`", target)) + defer conn.Close() lines := strings.Split(sql, "\n") for _, line := range lines { if line == "" { @@ -180,17 +240,38 @@ func populate(t *testing.T, target, sql string) { } } -func buildConnYaml(buf *bytes.Buffer, ks *cluster.Keyspace) { - connFormat := ` %s: - socket: %s - dbName: vt_%s - app: - user: vt_app - dba: - user: vt_dba -` - vttablet := ks.Shards[0].Vttablets[0].VttabletProcess - fmt.Fprintf(buf, connFormat, ks.Name, path.Join(vttablet.Directory, "mysql.sock"), ks.Name) +// waitForVReplicationToCatchup: copied from go/test/endtoend/vreplication/cluster.go +func waitForVReplicationToCatchup(t *testing.T, vttablet *cluster.VttabletProcess, duration time.Duration) { + queries := [3]string{ + `select count(*) from _vt.vreplication where pos = ''`, + "select count(*) from information_schema.tables where table_schema='_vt' and table_name='copy_state' limit 1;", + `select count(*) from _vt.copy_state`, + } + results := [3]string{"[INT64(0)]", "[INT64(1)]", "[INT64(0)]"} + var lastChecked time.Time + for ind, query := range queries { + waitDuration := 100 * time.Millisecond + for duration > 0 { + fmt.Printf("Executing query %s on %s\n", query, vttablet.Name) + lastChecked = time.Now() + qr, err := vttablet.QueryTablet(query, "", false) + if err != nil { + t.Fatal(err) + } + if qr != nil && qr.Rows != nil && len(qr.Rows) > 0 && fmt.Sprintf("%v", qr.Rows[0]) == string(results[ind]) { + break + } else { + fmt.Printf("In WaitForVReplicationToCatchup: %s %+v\n", query, qr.Rows) + } + time.Sleep(waitDuration) + duration -= waitDuration + } + if duration <= 0 { + fmt.Printf("WaitForVReplicationToCatchup timed out\n") + t.Fatal("WaitForVReplicationToCatchup timed out") + } + } + fmt.Printf("WaitForVReplicationToCatchup succeeded at %v\n", lastChecked) } func execQuery(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { From f5e7b5a48a8f2caebb00d365e2647a9e5ba8cde5 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Wed, 22 Apr 2020 12:49:12 -0700 Subject: [PATCH 14/16] vrepl: documentation for test Signed-off-by: Sugu Sougoumarane --- go/test/endtoend/migration/migration_test.go | 83 +++++++++++++------- 1 file changed, 56 insertions(+), 27 deletions(-) diff --git a/go/test/endtoend/migration/migration_test.go b/go/test/endtoend/migration/migration_test.go index fa6950e7041..9b626c55424 100644 --- a/go/test/endtoend/migration/migration_test.go +++ b/go/test/endtoend/migration/migration_test.go @@ -97,6 +97,43 @@ create table orders(oid bigint, cid bigint, pid bigint, mname varchar(128), pric ` ) +/* +TestMigration demonstrates how to setup vreplication to import data from multiple external +mysql sources. +We use vitess to bring up two databases that we'll treat as external. We'll directly access +the mysql instances instead of going through any vitess layer. +The product database contains a product table. +The customer database contains a customer and an orders table. +We create a new "commerce" keyspace, which will be the target. The commerce keyspace will +take a yaml config that defines these external sources. it will look like this: + +externalConnections: + product: + socket: /home/sougou/dev/src/vitess.io/vitess/vtdataroot/vtroot_15201/vt_0000000622/mysql.sock + dbName: vt_product + app: + user: vt_app + dba: + user: vt_dba + customer: + flavor: FilePos + socket: /home/sougou/dev/src/vitess.io/vitess/vtdataroot/vtroot_15201/vt_0000000620/mysql.sock + dbName: vt_customer + app: + user: vt_app + dba: + user: vt_dba + +We then execute the following vreplication inserts to initiate the import. The test uses +three streams although only two are required. This is to show that there can exist multiple +streams from the same source. The main difference between an external source vs a vitess +source is that the source proto contains an "external_mysql" field instead of keyspace and shard. +That field is the key into the externalConnections section of the input yaml. + +VReplicationExec: insert into _vt.vreplication (workflow, db_name, source, pos, max_tps, max_replication_lag, tablet_types, time_updated, transaction_timestamp, state) values('product', 'vt_commerce', 'filter: > external_mysql:\"product\" ', '', 9999, 9999, 'master', 0, 0, 'Running') +VReplicationExec: insert into _vt.vreplication (workflow, db_name, source, pos, max_tps, max_replication_lag, tablet_types, time_updated, transaction_timestamp, state) values('customer', 'vt_commerce', 'filter: > external_mysql:\"customer\" ', '', 9999, 9999, 'master', 0, 0, 'Running') +VReplicationExec: insert into _vt.vreplication (workflow, db_name, source, pos, max_tps, max_replication_lag, tablet_types, time_updated, transaction_timestamp, state) values('orders', 'vt_commerce', 'filter: > external_mysql:\"customer\" ', '', 9999, 9999, 'master', 0, 0, 'Running') +*/ func TestMigration(t *testing.T) { yamlFile := startCluster(t) defer clusterInstance.Teardown() @@ -117,15 +154,6 @@ func TestMigration(t *testing.T) { vttablet := keyspaces["commerce"].Shards[0].Vttablets[0].VttabletProcess waitForVReplicationToCatchup(t, vttablet, 1*time.Second) - vtParams = &mysql.ConnParams{ - Host: clusterInstance.Hostname, - Port: clusterInstance.VtgateMySQLPort, - } - conn, err := mysql.Connect(context.Background(), vtParams) - require.NoError(t, err) - defer conn.Close() - - execQuery(t, conn, "use `commerce`") testcases := []struct { query string result *sqltypes.Result @@ -155,6 +183,16 @@ func TestMigration(t *testing.T) { "2|1|2|newegg|15", ), }} + + vtParams = &mysql.ConnParams{ + Host: clusterInstance.Hostname, + Port: clusterInstance.VtgateMySQLPort, + } + conn, err := mysql.Connect(context.Background(), vtParams) + require.NoError(t, err) + defer conn.Close() + + execQuery(t, conn, "use `commerce`") for _, tcase := range testcases { result := execQuery(t, conn, tcase.query) // nil out the fields because they're too detailed. @@ -178,7 +216,7 @@ func migrate(t *testing.T, fromdb, toks string, tables []string) { query := fmt.Sprintf("insert into _vt.vreplication "+ "(workflow, db_name, source, pos, max_tps, max_replication_lag, tablet_types, time_updated, transaction_timestamp, state) values"+ "('%s', '%s', %s, '', 9999, 9999, 'master', 0, 0, 'Running')", tables[0], "vt_"+toks, sqlEscaped.String()) - fmt.Printf("query: %s\n", query) + fmt.Printf("VReplicationExec: %s\n", query) vttablet := keyspaces[toks].Shards[0].Vttablets[0].VttabletProcess err := clusterInstance.VtctlclientProcess.ExecuteCommand("VReplicationExec", vttablet.TabletPath, query) require.NoError(t, err) @@ -196,9 +234,7 @@ func startCluster(t *testing.T) string { createKeyspace(t, legacyProduct, []string{"0"}) productSocket := path.Join(keyspaces["product"].Shards[0].Vttablets[0].VttabletProcess.Directory, "mysql.sock") - fmt.Println("product", productSocket) customerSocket := path.Join(keyspaces["customer"].Shards[0].Vttablets[0].VttabletProcess.Directory, "mysql.sock") - fmt.Println("customer", customerSocket) populate(t, productSocket, legacyProductData) populate(t, customerSocket, legacyCustomerData) @@ -240,38 +276,31 @@ func populate(t *testing.T, socket, sql string) { } } -// waitForVReplicationToCatchup: copied from go/test/endtoend/vreplication/cluster.go +// waitForVReplicationToCatchup: logic copied from go/test/endtoend/vreplication/cluster.go func waitForVReplicationToCatchup(t *testing.T, vttablet *cluster.VttabletProcess, duration time.Duration) { - queries := [3]string{ + queries := []string{ `select count(*) from _vt.vreplication where pos = ''`, "select count(*) from information_schema.tables where table_schema='_vt' and table_name='copy_state' limit 1;", `select count(*) from _vt.copy_state`, } - results := [3]string{"[INT64(0)]", "[INT64(1)]", "[INT64(0)]"} - var lastChecked time.Time + results := []string{"[INT64(0)]", "[INT64(1)]", "[INT64(0)]"} for ind, query := range queries { waitDuration := 100 * time.Millisecond - for duration > 0 { - fmt.Printf("Executing query %s on %s\n", query, vttablet.Name) - lastChecked = time.Now() + for { qr, err := vttablet.QueryTablet(query, "", false) if err != nil { t.Fatal(err) } - if qr != nil && qr.Rows != nil && len(qr.Rows) > 0 && fmt.Sprintf("%v", qr.Rows[0]) == string(results[ind]) { + if len(qr.Rows) > 0 && fmt.Sprintf("%v", qr.Rows[0]) == string(results[ind]) { break - } else { - fmt.Printf("In WaitForVReplicationToCatchup: %s %+v\n", query, qr.Rows) } time.Sleep(waitDuration) duration -= waitDuration - } - if duration <= 0 { - fmt.Printf("WaitForVReplicationToCatchup timed out\n") - t.Fatal("WaitForVReplicationToCatchup timed out") + if duration <= 0 { + t.Fatalf("waitForVReplicationToCatchup timed out, query: %v, result: %v", query, qr) + } } } - fmt.Printf("WaitForVReplicationToCatchup succeeded at %v\n", lastChecked) } func execQuery(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result { From 516d6bd2bf78a66fed67be56444aa275425950b2 Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Wed, 29 Apr 2020 09:25:26 -0700 Subject: [PATCH 15/16] vrepl: vstreamerClient -> VStreamerClient Signed-off-by: Sugu Sougoumarane --- go/vt/vttablet/tabletmanager/vreplication/controller.go | 2 +- .../tabletmanager/vreplication/external_connector.go | 8 ++++---- go/vt/vttablet/tabletmanager/vreplication/vreplicator.go | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/go/vt/vttablet/tabletmanager/vreplication/controller.go b/go/vt/vttablet/tabletmanager/vreplication/controller.go index 058f54d6f70..42f53627ad0 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/controller.go +++ b/go/vt/vttablet/tabletmanager/vreplication/controller.go @@ -221,7 +221,7 @@ func (ct *controller) runBlp(ctx context.Context) (err error) { return err } - var vsClient vstreamerClient + var vsClient VStreamerClient var err error if name := ct.source.GetExternalMysql(); name != "" { vsClient, err = ct.vre.ec.Get(name) diff --git a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go index 7c754f551b3..6204c124a8f 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/external_connector.go +++ b/go/vt/vttablet/tabletmanager/vreplication/external_connector.go @@ -36,12 +36,12 @@ import ( ) var ( - _ vstreamerClient = (*mysqlConnector)(nil) - _ vstreamerClient = (*tabletConnector)(nil) + _ VStreamerClient = (*mysqlConnector)(nil) + _ VStreamerClient = (*tabletConnector)(nil) ) -// vstreamerClient exposes the core interface of a vstreamer -type vstreamerClient interface { +// VStreamerClient exposes the core interface of a vstreamer +type VStreamerClient interface { Open(context.Context) error Close(context.Context) error diff --git a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go index 104ff093be6..1302b3fd786 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vreplicator.go @@ -53,7 +53,7 @@ type vreplicator struct { dbClient *vdbClient // source source *binlogdatapb.BinlogSource - sourceVStreamer vstreamerClient + sourceVStreamer VStreamerClient stats *binlogplayer.Stats // mysqld is used to fetch the local schema. @@ -81,7 +81,7 @@ type vreplicator struct { // alias like "a+b as targetcol" must be used. // More advanced constructs can be used. Please see the table plan builder // documentation for more info. -func newVReplicator(id uint32, source *binlogdatapb.BinlogSource, sourceVStreamer vstreamerClient, stats *binlogplayer.Stats, dbClient binlogplayer.DBClient, mysqld mysqlctl.MysqlDaemon, vre *Engine) *vreplicator { +func newVReplicator(id uint32, source *binlogdatapb.BinlogSource, sourceVStreamer VStreamerClient, stats *binlogplayer.Stats, dbClient binlogplayer.DBClient, mysqld mysqlctl.MysqlDaemon, vre *Engine) *vreplicator { return &vreplicator{ vre: vre, id: id, From 494893562d3e01241a27d2c58e3265b9bccd4aaf Mon Sep 17 00:00:00 2001 From: Sugu Sougoumarane Date: Mon, 4 May 2020 19:03:11 -0700 Subject: [PATCH 16/16] vrepl: review comments Signed-off-by: Sugu Sougoumarane --- go/cmd/vtcombo/main.go | 2 +- go/cmd/vttablet/vttablet.go | 4 +- go/vt/dbconfigs/dbconfigs.go | 4 +- go/vt/dbconfigs/dbconfigs_test.go | 8 +-- go/vt/mysqlctl/cmd.go | 4 +- go/vt/mysqlctl/mycnf_test.go | 2 +- .../vreplication/external_connector_test.go | 70 +++++++++++++++---- 7 files changed, 69 insertions(+), 25 deletions(-) diff --git a/go/cmd/vtcombo/main.go b/go/cmd/vtcombo/main.go index 518ff88cf4b..4a1d446f3a5 100644 --- a/go/cmd/vtcombo/main.go +++ b/go/cmd/vtcombo/main.go @@ -97,7 +97,7 @@ func main() { servenv.Init() tabletenv.Init() - dbconfigs.GlobalDBConfigs.Init("") + dbconfigs.GlobalDBConfigs.InitWithSocket("") mysqld := mysqlctl.NewMysqld(&dbconfigs.GlobalDBConfigs) servenv.OnClose(mysqld.Close) diff --git a/go/cmd/vttablet/vttablet.go b/go/cmd/vttablet/vttablet.go index 577e6c833b9..d55ecb554dc 100644 --- a/go/cmd/vttablet/vttablet.go +++ b/go/cmd/vttablet/vttablet.go @@ -104,9 +104,9 @@ func main() { // If connection parameters were specified, socketFile will be empty. // Otherwise, the socketFile (read from mycnf) will be used to initialize // dbconfigs. - config.DB.Init(socketFile) + config.DB.InitWithSocket(socketFile) for _, cfg := range config.ExternalConnections { - cfg.Init("") + cfg.InitWithSocket("") } if *tableACLConfig != "" { diff --git a/go/vt/dbconfigs/dbconfigs.go b/go/vt/dbconfigs/dbconfigs.go index f42d4b47ebf..fe13a8ee782 100644 --- a/go/vt/dbconfigs/dbconfigs.go +++ b/go/vt/dbconfigs/dbconfigs.go @@ -313,14 +313,14 @@ func (dbcfgs *DBConfigs) Clone() *DBConfigs { return &result } -// Init will initialize all the necessary connection parameters. +// InitWithSocket will initialize all the necessary connection parameters. // Precedence is as follows: if UserConfig settings are set, // they supersede all other settings. // The next priority is with per-user connection // parameters. This is only for legacy support. // If no per-user parameters are supplied, then the defaultSocketFile // is used to initialize the per-user conn params. -func (dbcfgs *DBConfigs) Init(defaultSocketFile string) { +func (dbcfgs *DBConfigs) InitWithSocket(defaultSocketFile string) { for _, userKey := range All { uc, cp := dbcfgs.getParams(userKey, dbcfgs) // TODO @rafael: For ExternalRepl we need to respect the provided host / port diff --git a/go/vt/dbconfigs/dbconfigs_test.go b/go/vt/dbconfigs/dbconfigs_test.go index bb61246a6c9..57f5b695b45 100644 --- a/go/vt/dbconfigs/dbconfigs_test.go +++ b/go/vt/dbconfigs/dbconfigs_test.go @@ -35,7 +35,7 @@ func TestInit(t *testing.T) { appParams: mysql.ConnParams{UnixSocket: "socket"}, dbaParams: mysql.ConnParams{Host: "host"}, } - dbConfigs.Init("default") + dbConfigs.InitWithSocket("default") assert.Equal(t, mysql.ConnParams{UnixSocket: "socket"}, dbConfigs.appParams) assert.Equal(t, mysql.ConnParams{Host: "host"}, dbConfigs.dbaParams) assert.Equal(t, mysql.ConnParams{UnixSocket: "default"}, dbConfigs.appdebugParams) @@ -71,7 +71,7 @@ func TestInit(t *testing.T) { Host: "host", }, } - dbConfigs.Init("default") + dbConfigs.InitWithSocket("default") want := mysql.ConnParams{ Host: "a", @@ -148,7 +148,7 @@ func TestInit(t *testing.T) { Flags: 2, }, } - dbConfigs.Init("default") + dbConfigs.InitWithSocket("default") want = mysql.ConnParams{ Host: "a", Port: 1, @@ -196,7 +196,7 @@ func TestUseTCP(t *testing.T) { User: "dba", }, } - dbConfigs.Init("default") + dbConfigs.InitWithSocket("default") want := mysql.ConnParams{ Host: "a", diff --git a/go/vt/mysqlctl/cmd.go b/go/vt/mysqlctl/cmd.go index ac6b0415164..f54815cf2ce 100644 --- a/go/vt/mysqlctl/cmd.go +++ b/go/vt/mysqlctl/cmd.go @@ -46,7 +46,7 @@ func CreateMysqldAndMycnf(tabletUID uint32, mysqlSocket string, mysqlPort int32) mycnf.SocketFile = mysqlSocket } - dbconfigs.GlobalDBConfigs.Init(mycnf.SocketFile) + dbconfigs.GlobalDBConfigs.InitWithSocket(mycnf.SocketFile) return NewMysqld(&dbconfigs.GlobalDBConfigs), mycnf, nil } @@ -60,6 +60,6 @@ func OpenMysqldAndMycnf(tabletUID uint32) (*Mysqld, *Mycnf, error) { return nil, nil, fmt.Errorf("couldn't read my.cnf file: %v", err) } - dbconfigs.GlobalDBConfigs.Init(mycnf.SocketFile) + dbconfigs.GlobalDBConfigs.InitWithSocket(mycnf.SocketFile) return NewMysqld(&dbconfigs.GlobalDBConfigs), mycnf, nil } diff --git a/go/vt/mysqlctl/mycnf_test.go b/go/vt/mysqlctl/mycnf_test.go index 2911c0b5b61..903cae62927 100644 --- a/go/vt/mysqlctl/mycnf_test.go +++ b/go/vt/mysqlctl/mycnf_test.go @@ -96,7 +96,7 @@ func NoTestMycnfHook(t *testing.T) { // this is not being passed, so it should be nil os.Setenv("MY_VAR", "myvalue") - dbconfigs.GlobalDBConfigs.Init(cnf.SocketFile) + dbconfigs.GlobalDBConfigs.InitWithSocket(cnf.SocketFile) mysqld := NewMysqld(&dbconfigs.GlobalDBConfigs) servenv.OnClose(mysqld.Close) diff --git a/go/vt/vttablet/tabletmanager/vreplication/external_connector_test.go b/go/vt/vttablet/tabletmanager/vreplication/external_connector_test.go index ec9bf0fed50..601a00352ef 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/external_connector_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/external_connector_test.go @@ -25,7 +25,7 @@ import ( binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" ) -func TestExternalConnector(t *testing.T) { +func TestExternalConnectorCopy(t *testing.T) { execStatements(t, []string{ "create table tab1(id int, val varbinary(128), primary key(id))", fmt.Sprintf("create table %s.tab1(id int, val varbinary(128), primary key(id))", vrepldb), @@ -58,7 +58,7 @@ func TestExternalConnector(t *testing.T) { ExternalMysql: "exta", Filter: filter1, } - cancel1 := startExternalVReplication(t, bls1) + cancel1 := startExternalVReplication(t, bls1, "") expectDBClientQueries(t, []string{ "begin", @@ -91,7 +91,7 @@ func TestExternalConnector(t *testing.T) { ExternalMysql: "exta", Filter: filter2, } - cancel2 := startExternalVReplication(t, bls2) + cancel2 := startExternalVReplication(t, bls2, "") expectDBClientQueries(t, []string{ "begin", @@ -116,7 +116,7 @@ func TestExternalConnector(t *testing.T) { ExternalMysql: "extb", Filter: filter3, } - cancel3 := startExternalVReplication(t, bls3) + cancel3 := startExternalVReplication(t, bls3, "") expectDBClientQueries(t, []string{ "begin", @@ -132,20 +132,64 @@ func TestExternalConnector(t *testing.T) { assert.Equal(t, 2, len(playerEngine.ec.connectors)) } -func startExternalVReplication(t *testing.T, bls *binlogdatapb.BinlogSource) (cancelr func()) { - query := binlogplayer.CreateVReplication("test", bls, "", 9223372036854775807, 9223372036854775807, 0, vrepldb) - qr, err := playerEngine.Exec(query) - if err != nil { - t.Fatal(err) +func TestExternalConnectorPlay(t *testing.T) { + execStatements(t, []string{ + "create table tab1(id int, val varbinary(128), primary key(id))", + fmt.Sprintf("create table %s.tab1(id int, val varbinary(128), primary key(id))", vrepldb), + }) + defer execStatements(t, []string{ + "drop table tab1", + fmt.Sprintf("drop table %s.tab1", vrepldb), + }) + + filter1 := &binlogdatapb.Filter{ + Rules: []*binlogdatapb.Rule{{ + Match: "tab1", + Filter: "", + }}, } + bls1 := &binlogdatapb.BinlogSource{ + ExternalMysql: "exta", + Filter: filter1, + } + pos := masterPosition(t) + cancel1 := startExternalVReplication(t, bls1, pos) + defer cancel1() + + execStatements(t, []string{ + "insert into tab1 values(1, 'a'), (2, 'b')", + }) + expectDBClientQueries(t, []string{ - "/insert into _vt.vreplication", "begin", - "/insert into _vt.copy_state", - "/update _vt.vreplication set state='Copying'", - "commit", + "insert into tab1(id,val) values (1,'a')", + "insert into tab1(id,val) values (2,'b')", "/update _vt.vreplication set pos=", + "commit", }) +} + +func startExternalVReplication(t *testing.T, bls *binlogdatapb.BinlogSource, pos string) (cancelr func()) { + query := binlogplayer.CreateVReplication("test", bls, pos, 9223372036854775807, 9223372036854775807, 0, vrepldb) + qr, err := playerEngine.Exec(query) + if err != nil { + t.Fatal(err) + } + if pos == "" { + expectDBClientQueries(t, []string{ + "/insert into _vt.vreplication", + "begin", + "/insert into _vt.copy_state", + "/update _vt.vreplication set state='Copying'", + "commit", + "/update _vt.vreplication set pos=", + }) + } else { + expectDBClientQueries(t, []string{ + "/insert into _vt.vreplication", + "/update _vt.vreplication set state='Running'", + }) + } return func() { t.Helper() query := fmt.Sprintf("delete from _vt.vreplication where id = %d", qr.InsertID)