vitess.io/vitess@v0.16.2/go/vt/vtgate/schema/update_controller_flaky_test.go (about)

     1  /*
     2  Copyright 2021 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 schema
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  
    26  	"vitess.io/vitess/go/vt/discovery"
    27  	querypb "vitess.io/vitess/go/vt/proto/query"
    28  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    29  )
    30  
    31  func TestMultipleUpdatesFromDifferentShards(t *testing.T) {
    32  	type input struct {
    33  		shard         string
    34  		tablesUpdates []string
    35  	}
    36  	type testCase struct {
    37  		updateTables                 []string
    38  		signalExpected, initExpected int
    39  		inputs                       []input
    40  		delay                        time.Duration
    41  		init, initFail, updateFail   bool
    42  	}
    43  	tests := []testCase{{
    44  		inputs: []input{{
    45  			shard:         "-80",
    46  			tablesUpdates: []string{"a"},
    47  		}, {
    48  			shard:         "80-",
    49  			tablesUpdates: []string{"a"},
    50  		}},
    51  		updateTables:   []string{"a"},
    52  		signalExpected: 1,
    53  	}, {
    54  		inputs: []input{{
    55  			shard:         "0",
    56  			tablesUpdates: []string{"a"},
    57  		}, {
    58  			shard:         "0",
    59  			tablesUpdates: []string{"b"},
    60  		}},
    61  		updateTables:   []string{"a", "b"},
    62  		signalExpected: 1,
    63  	}, {
    64  		inputs: []input{{
    65  			shard:         "0",
    66  			tablesUpdates: []string{"a"},
    67  		}, {
    68  			shard:         "0",
    69  			tablesUpdates: []string{"b"},
    70  		}},
    71  		updateTables:   []string{"b"},
    72  		signalExpected: 2,
    73  		delay:          10 * time.Millisecond,
    74  	}, {
    75  		inputs: []input{{
    76  			shard: "0",
    77  		}, {
    78  			shard: "0",
    79  		}},
    80  	}, {
    81  		inputs: []input{{
    82  			shard:         "-80",
    83  			tablesUpdates: []string{"a"},
    84  		}, {
    85  			shard:         "80-",
    86  			tablesUpdates: []string{"a"},
    87  		}},
    88  		signalExpected: 1,
    89  		initExpected:   1,
    90  		init:           true,
    91  	}, {
    92  		inputs: []input{{
    93  			shard:         "-80",
    94  			tablesUpdates: []string{"a"},
    95  		}, {
    96  			shard:         "80-",
    97  			tablesUpdates: []string{"a"},
    98  		}},
    99  		signalExpected: 0,
   100  		initExpected:   1,
   101  		init:           true,
   102  		initFail:       true,
   103  	}, {
   104  		inputs: []input{{
   105  			shard:         "-80",
   106  			tablesUpdates: []string{"a"},
   107  		}, {
   108  			shard:         "80-",
   109  			tablesUpdates: []string{"b"},
   110  		}},
   111  		updateTables:   []string{"a", "b"},
   112  		signalExpected: 0,
   113  		updateFail:     true,
   114  	},
   115  	}
   116  	for i, test := range tests {
   117  		t.Run(fmt.Sprintf("%d", i+1), func(t *testing.T) {
   118  			var signalNb, initNb int
   119  			var updatedTables []string
   120  			update := func(th *discovery.TabletHealth) bool {
   121  				updatedTables = th.Stats.TableSchemaChanged
   122  				return !test.updateFail
   123  			}
   124  			signal := func() {
   125  				signalNb++
   126  			}
   127  			updateCont := updateController{
   128  				update:       update,
   129  				signal:       signal,
   130  				consumeDelay: 5 * time.Millisecond,
   131  				reloadKeyspace: func(th *discovery.TabletHealth) error {
   132  					initNb++
   133  					var err error
   134  					if test.initFail {
   135  						err = fmt.Errorf("error")
   136  					}
   137  					return err
   138  				},
   139  				loaded: !test.init,
   140  			}
   141  
   142  			for _, in := range test.inputs {
   143  				target := &querypb.Target{
   144  					Keyspace:   "ks",
   145  					Shard:      in.shard,
   146  					TabletType: topodatapb.TabletType_PRIMARY,
   147  				}
   148  				tablet := &topodatapb.Tablet{
   149  					Keyspace: target.Keyspace,
   150  					Shard:    target.Shard,
   151  					Type:     target.TabletType,
   152  				}
   153  				d := &discovery.TabletHealth{
   154  					Tablet:  tablet,
   155  					Target:  target,
   156  					Serving: true,
   157  					Stats:   &querypb.RealtimeStats{TableSchemaChanged: in.tablesUpdates},
   158  				}
   159  				if test.delay > 0 {
   160  					time.Sleep(test.delay)
   161  				}
   162  				updateCont.add(d)
   163  			}
   164  
   165  			for {
   166  				updateCont.mu.Lock()
   167  				done := updateCont.queue == nil
   168  				updateCont.mu.Unlock()
   169  				if done {
   170  					break
   171  				}
   172  			}
   173  
   174  			assert.Equal(t, test.signalExpected, signalNb, "signal required")
   175  			assert.Equal(t, test.initExpected, initNb, "init required")
   176  			assert.Equal(t, test.updateTables, updatedTables, "tables to update")
   177  		})
   178  	}
   179  }
   180  
   181  // TestViewUpdate tests the update of the view handled as expected.
   182  func TestViewsUpdates(t *testing.T) {
   183  	// receives a input from a shard and what views are updated.
   184  	type input struct {
   185  		shard       string
   186  		viewUpdates []string
   187  	}
   188  	// test will receive multiple inputs and then find the consolidated view updates.
   189  	// if able to receive results successfully, then signal is made to the consumer.
   190  	type testCase struct {
   191  		desc                         string
   192  		updateViews                  []string
   193  		signalExpected, initExpected int
   194  		inputs                       []input
   195  		delay                        time.Duration // this causes a delay between inputs
   196  		init, initFail, updateFail   bool
   197  	}
   198  	tests := []testCase{{
   199  		desc:           "received same view updates from shards",
   200  		inputs:         []input{{shard: "-80", viewUpdates: []string{"a"}}, {shard: "80-", viewUpdates: []string{"a"}}},
   201  		updateViews:    []string{"a"},
   202  		signalExpected: 1,
   203  	}, {
   204  		desc:           "received different view updates from shards",
   205  		inputs:         []input{{shard: "0", viewUpdates: []string{"a"}}, {shard: "0", viewUpdates: []string{"b"}}},
   206  		updateViews:    []string{"a", "b"},
   207  		signalExpected: 1,
   208  	}, {
   209  		desc:           "delay between inputs - different signals from each input",
   210  		inputs:         []input{{shard: "0", viewUpdates: []string{"a"}}, {shard: "0", viewUpdates: []string{"b"}}},
   211  		updateViews:    []string{"b"},
   212  		signalExpected: 2,
   213  		delay:          10 * time.Millisecond,
   214  	}, {
   215  		desc:   "no change - no signal",
   216  		inputs: []input{{shard: "0"}, {shard: "0"}},
   217  	}, {
   218  		desc:           "initialization did not happen - full views load over only updated views",
   219  		inputs:         []input{{shard: "-80", viewUpdates: []string{"a"}}, {shard: "80-", viewUpdates: []string{"a"}}},
   220  		signalExpected: 1,
   221  		initExpected:   1,
   222  		init:           true,
   223  	}, {
   224  		desc:           "initialization did not happen - full view load failed - no signal",
   225  		inputs:         []input{{shard: "-80", viewUpdates: []string{"a"}}, {shard: "80-", viewUpdates: []string{"a"}}},
   226  		signalExpected: 0,
   227  		initExpected:   1,
   228  		init:           true,
   229  		initFail:       true,
   230  	}, {
   231  		desc:           "updated views failed - no signal",
   232  		inputs:         []input{{shard: "-80", viewUpdates: []string{"a"}}, {shard: "80-", viewUpdates: []string{"b"}}},
   233  		updateViews:    []string{"a", "b"},
   234  		signalExpected: 0,
   235  		updateFail:     true,
   236  	},
   237  	}
   238  	for i, test := range tests {
   239  		t.Run(fmt.Sprintf("%d", i+1), func(t *testing.T) {
   240  			var signalNb, initNb int
   241  			var updatedViews []string
   242  			update := func(th *discovery.TabletHealth) bool {
   243  				updatedViews = th.Stats.ViewSchemaChanged
   244  				return !test.updateFail
   245  			}
   246  			signal := func() {
   247  				signalNb++
   248  			}
   249  			updateCont := updateController{
   250  				update:       update,
   251  				signal:       signal,
   252  				consumeDelay: 5 * time.Millisecond,
   253  				reloadKeyspace: func(th *discovery.TabletHealth) error {
   254  					initNb++
   255  					var err error
   256  					if test.initFail {
   257  						err = fmt.Errorf("error")
   258  					}
   259  					return err
   260  				},
   261  				loaded: !test.init,
   262  			}
   263  
   264  			for _, in := range test.inputs {
   265  				target := &querypb.Target{
   266  					Keyspace:   "ks",
   267  					Shard:      in.shard,
   268  					TabletType: topodatapb.TabletType_PRIMARY,
   269  				}
   270  				tablet := &topodatapb.Tablet{
   271  					Keyspace: target.Keyspace,
   272  					Shard:    target.Shard,
   273  					Type:     target.TabletType,
   274  				}
   275  				d := &discovery.TabletHealth{
   276  					Tablet:  tablet,
   277  					Target:  target,
   278  					Serving: true,
   279  					Stats:   &querypb.RealtimeStats{ViewSchemaChanged: in.viewUpdates},
   280  				}
   281  				if test.delay > 0 {
   282  					time.Sleep(test.delay)
   283  				}
   284  				updateCont.add(d)
   285  			}
   286  
   287  			for {
   288  				updateCont.mu.Lock()
   289  				done := updateCont.queue == nil
   290  				updateCont.mu.Unlock()
   291  				if done {
   292  					break
   293  				}
   294  			}
   295  
   296  			assert.Equal(t, test.signalExpected, signalNb, "signal required")
   297  			assert.Equal(t, test.initExpected, initNb, "init required")
   298  			assert.Equal(t, test.updateViews, updatedViews, "views to update")
   299  		})
   300  	}
   301  }