vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/vstreamer/engine_test.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package vstreamer
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/stretchr/testify/require"
    26  
    27  	"vitess.io/vitess/go/mysql/fakesqldb"
    28  	"vitess.io/vitess/go/sqltypes"
    29  	"vitess.io/vitess/go/vt/dbconfigs"
    30  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    31  	"vitess.io/vitess/go/vt/vttablet/tabletserver/schema"
    32  )
    33  
    34  var (
    35  	shardedVSchema = `{
    36    "sharded": true,
    37    "vindexes": {
    38      "hash": {
    39        "type": "hash"
    40      }
    41    },
    42    "tables": {
    43      "t1": {
    44        "column_vindexes": [
    45          {
    46            "column": "id1",
    47            "name": "hash"
    48          }
    49        ]
    50      }
    51    }
    52  }`
    53  
    54  	multicolumnVSchema = `{
    55    "sharded": true,
    56    "vindexes": {
    57      "region_vdx": {
    58        "type": "region_experimental",
    59  			"params": {
    60  				"region_bytes": "1"
    61  			}
    62      }
    63    },
    64    "tables": {
    65      "t1": {
    66        "column_vindexes": [
    67          {
    68            "columns": [
    69  						"region",
    70  						"id"
    71  					],
    72            "name": "region_vdx"
    73          }
    74        ]
    75      }
    76    }
    77  }`
    78  )
    79  
    80  func TestUpdateVSchema(t *testing.T) {
    81  	if testing.Short() {
    82  		t.Skip()
    83  	}
    84  
    85  	defer env.SetVSchema("{}")
    86  
    87  	// We have to start at least one stream to start the vschema watcher.
    88  	ctx, cancel := context.WithCancel(context.Background())
    89  	cancel()
    90  	filter := &binlogdatapb.Filter{
    91  		Rules: []*binlogdatapb.Rule{{
    92  			Match: "/.*/",
    93  		}},
    94  	}
    95  	// Stream should terminate immediately due to canceled context.
    96  	_ = engine.Stream(ctx, "current", nil, filter, func(_ []*binlogdatapb.VEvent) error {
    97  		return nil
    98  	})
    99  
   100  	startCount := expectUpdateCount(t, 1)
   101  
   102  	if err := env.SetVSchema(shardedVSchema); err != nil {
   103  		t.Fatal(err)
   104  	}
   105  	expectUpdateCount(t, startCount+1)
   106  
   107  	want := `{
   108    "routing_rules": {},
   109    "keyspaces": {
   110      "vttest": {
   111        "sharded": true,
   112        "tables": {
   113          "dual": {
   114            "type": "reference",
   115            "name": "dual"
   116          },
   117          "t1": {
   118            "name": "t1",
   119            "column_vindexes": [
   120              {
   121                "columns": [
   122                  "id1"
   123                ],
   124                "type": "hash",
   125                "name": "hash",
   126                "vindex": {}
   127              }
   128            ],
   129            "ordered": [
   130              {
   131                "columns": [
   132                  "id1"
   133                ],
   134                "type": "hash",
   135                "name": "hash",
   136                "vindex": {}
   137              }
   138            ]
   139          }
   140        },
   141        "vindexes": {
   142          "hash": {}
   143        }
   144      }
   145    },
   146    "shard_routing_rules": null
   147  }`
   148  	b, err := json.MarshalIndent(engine.vschema(), "", "  ")
   149  	if err != nil {
   150  		t.Fatal(err)
   151  	}
   152  	got := string(b)
   153  	require.Equal(t, want, got)
   154  }
   155  
   156  func expectUpdateCount(t *testing.T, wantCount int64) int64 {
   157  	for i := 0; i < 10; i++ {
   158  		gotCount := engine.vschemaUpdates.Get()
   159  		if gotCount >= wantCount {
   160  			return gotCount
   161  		}
   162  		if i == 9 {
   163  			t.Fatalf("update count: %d, want %d", gotCount, wantCount)
   164  		}
   165  		time.Sleep(10 * time.Millisecond)
   166  	}
   167  	panic("unreachable")
   168  }
   169  
   170  func TestVStreamerWaitForMySQL(t *testing.T) {
   171  	tableName := "test"
   172  	type fields struct {
   173  		vse                   *Engine
   174  		cp                    dbconfigs.Connector
   175  		se                    *schema.Engine
   176  		ReplicationLagSeconds int64
   177  		maxInnoDBTrxHistLen   int64
   178  		maxMySQLReplLagSecs   int64
   179  	}
   180  	tests := []struct {
   181  		name    string
   182  		fields  fields
   183  		wantErr bool
   184  	}{
   185  		{
   186  			name: "Small InnoDB MVCC impact limit",
   187  			fields: fields{
   188  				vse:                 engine,
   189  				se:                  engine.se,
   190  				maxInnoDBTrxHistLen: 100,
   191  				maxMySQLReplLagSecs: 5000,
   192  			},
   193  			wantErr: true,
   194  		},
   195  		{
   196  			name: "Small Repl Lag impact limit",
   197  			fields: fields{
   198  				vse:                 engine,
   199  				se:                  engine.se,
   200  				maxInnoDBTrxHistLen: 10000,
   201  				maxMySQLReplLagSecs: 5,
   202  			},
   203  			wantErr: true,
   204  		},
   205  		{
   206  			name: "Large impact limits",
   207  			fields: fields{
   208  				vse:                 engine,
   209  				se:                  engine.se,
   210  				maxInnoDBTrxHistLen: 10000,
   211  				maxMySQLReplLagSecs: 200,
   212  			},
   213  			wantErr: false,
   214  		},
   215  	}
   216  	testDB := fakesqldb.New(t)
   217  	hostres := sqltypes.MakeTestResult(sqltypes.MakeTestFields(
   218  		"hostname|port",
   219  		"varchar|int64"),
   220  		"localhost|3306",
   221  	)
   222  	thlres := sqltypes.MakeTestResult(sqltypes.MakeTestFields(
   223  		"history_len",
   224  		"int64"),
   225  		"1000",
   226  	)
   227  	sbmres := sqltypes.MakeTestResult(sqltypes.MakeTestFields(
   228  		"Seconds_Behind_Master",
   229  		"int64"),
   230  		"10",
   231  	)
   232  	testDB.AddQuery(hostQuery, hostres)
   233  	testDB.AddQuery(trxHistoryLenQuery, thlres)
   234  	testDB.AddQuery(replicaLagQuery, sbmres)
   235  	for _, tt := range tests {
   236  		tt.fields.cp = testDB.ConnParams()
   237  		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
   238  		defer cancel()
   239  		t.Run(tt.name, func(t *testing.T) {
   240  			uvs := &uvstreamer{
   241  				ctx:                   ctx,
   242  				cancel:                cancel,
   243  				vse:                   tt.fields.vse,
   244  				cp:                    tt.fields.cp,
   245  				se:                    tt.fields.se,
   246  				ReplicationLagSeconds: tt.fields.ReplicationLagSeconds,
   247  			}
   248  			env.TabletEnv.Config().RowStreamer.MaxInnoDBTrxHistLen = tt.fields.maxInnoDBTrxHistLen
   249  			env.TabletEnv.Config().RowStreamer.MaxMySQLReplLagSecs = tt.fields.maxMySQLReplLagSecs
   250  			if err := uvs.vse.waitForMySQL(ctx, uvs.cp, tableName); (err != nil) != tt.wantErr {
   251  				t.Errorf("vstreamer.waitForMySQL() error = %v, wantErr %v", err, tt.wantErr)
   252  			}
   253  		})
   254  	}
   255  
   256  	require.Equal(t, engine.rowStreamerWaits.Counts()["VStreamerTest.waitForMySQL"], int64(2))
   257  	require.Equal(t, engine.vstreamerPhaseTimings.Counts()["VStreamerTest."+tableName+":waitForMySQL"], int64(2))
   258  }