github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/shardddl/optimism/table_test.go (about)

     1  // Copyright 2020 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package optimism
    15  
    16  import (
    17  	"context"
    18  	"testing"
    19  	"time"
    20  
    21  	. "github.com/pingcap/check"
    22  	"github.com/stretchr/testify/require"
    23  )
    24  
    25  func (t *testForEtcd) TestSourceTablesJSON(c *C) {
    26  	st1 := NewSourceTables("test", "mysql-replica-1")
    27  	st1.AddTable("db1", "tbl1", "db", "tbl")
    28  	j, err := st1.toJSON()
    29  	c.Assert(err, IsNil)
    30  	c.Assert(j, Equals, `{"task":"test","source":"mysql-replica-1","tables":{"db":{"tbl":{"db1":{"tbl1":{}}}}}}`)
    31  	c.Assert(j, Equals, st1.String())
    32  
    33  	st2, err := sourceTablesFromJSON(j)
    34  	c.Assert(err, IsNil)
    35  	c.Assert(st2, DeepEquals, st1)
    36  }
    37  
    38  func (t *testForEtcd) TestSourceTablesAddRemove(c *C) {
    39  	var (
    40  		task       = "task"
    41  		source     = "mysql-replica-1"
    42  		downSchema = "foo"
    43  		downTable1 = "bar1"
    44  		downTable2 = "bar2"
    45  		st         = NewSourceTables(task, source)
    46  	)
    47  
    48  	// no target table exist.
    49  	c.Assert(st.TargetTable(downSchema, downTable1).IsEmpty(), IsTrue)
    50  	c.Assert(st.TargetTable(downSchema, downTable2).IsEmpty(), IsTrue)
    51  
    52  	// add a table for downTable1.
    53  	c.Assert(st.AddTable("foo1", "bar1", downSchema, downTable1), IsTrue)
    54  	c.Assert(st.AddTable("foo1", "bar1", downSchema, downTable1), IsFalse)
    55  	c.Assert(st.TargetTable(downSchema, downTable1), DeepEquals,
    56  		newTargetTable(task, source, downSchema, downTable1, map[string]map[string]struct{}{
    57  			"foo1": {"bar1": struct{}{}},
    58  		}))
    59  	c.Assert(st.TargetTable(downSchema, downTable2).IsEmpty(), IsTrue)
    60  
    61  	// add a table for downTable2.
    62  	c.Assert(st.AddTable("foo2", "bar2", downSchema, downTable2), IsTrue)
    63  	c.Assert(st.AddTable("foo2", "bar2", downSchema, downTable2), IsFalse)
    64  	c.Assert(st.TargetTable(downSchema, downTable2), DeepEquals,
    65  		newTargetTable(task, source, downSchema, downTable2, map[string]map[string]struct{}{
    66  			"foo2": {"bar2": struct{}{}},
    67  		}))
    68  
    69  	// remove a table for downTable1.
    70  	c.Assert(st.RemoveTable("foo1", "bar1", downSchema, downTable1), IsTrue)
    71  	c.Assert(st.RemoveTable("foo1", "bar1", downSchema, downTable1), IsFalse)
    72  	c.Assert(st.TargetTable(downSchema, downTable1).IsEmpty(), IsTrue)
    73  
    74  	// remove a table for downTable2.
    75  	c.Assert(st.RemoveTable("foo2", "bar2", downSchema, downTable2), IsTrue)
    76  	c.Assert(st.RemoveTable("foo2", "bar2", downSchema, downTable2), IsFalse)
    77  	c.Assert(st.TargetTable(downSchema, downTable2).IsEmpty(), IsTrue)
    78  }
    79  
    80  func (t *testForEtcd) TestSourceTablesEtcd(c *C) {
    81  	defer clearTestInfoOperation(c)
    82  
    83  	var (
    84  		watchTimeout = 2 * time.Second
    85  		task         = "task"
    86  		source1      = "mysql-replica-1"
    87  		source2      = "mysql-replica-2"
    88  		downSchema   = "db"
    89  		downTable    = "tbl"
    90  		st1          = NewSourceTables(task, source1)
    91  		st2          = NewSourceTables(task, source2)
    92  	)
    93  
    94  	st1.AddTable("db", "tbl-1", downSchema, downTable)
    95  	st1.AddTable("db", "tbl-2", downSchema, downTable)
    96  	st2.AddTable("db", "tbl-1", downSchema, downTable)
    97  	st2.AddTable("db", "tbl-2", downSchema, downTable)
    98  
    99  	// put two SourceTables.
   100  	rev1, err := PutSourceTables(etcdTestCli, st1)
   101  	c.Assert(err, IsNil)
   102  	rev2, err := PutSourceTables(etcdTestCli, st2)
   103  	c.Assert(err, IsNil)
   104  	c.Assert(rev2, Greater, rev1)
   105  
   106  	// get with two SourceTables.
   107  	stm, rev3, err := GetAllSourceTables(etcdTestCli)
   108  	c.Assert(err, IsNil)
   109  	c.Assert(rev3, Equals, rev2)
   110  	c.Assert(stm, HasLen, 1)
   111  	c.Assert(stm[task], HasLen, 2)
   112  	c.Assert(stm[task][source1], DeepEquals, st1)
   113  	c.Assert(stm[task][source2], DeepEquals, st2)
   114  
   115  	// watch with an older revision for all SourceTables.
   116  	wch := make(chan SourceTables, 10)
   117  	ech := make(chan error, 10)
   118  	ctx, cancel := context.WithTimeout(context.Background(), watchTimeout)
   119  	WatchSourceTables(ctx, etcdTestCli, rev1, wch, ech)
   120  	cancel()
   121  	close(wch)
   122  	close(ech)
   123  
   124  	// get two source tables.
   125  	c.Assert(len(wch), Equals, 2)
   126  	c.Assert(<-wch, DeepEquals, st1)
   127  	c.Assert(<-wch, DeepEquals, st2)
   128  	c.Assert(len(ech), Equals, 0)
   129  
   130  	// delete tow sources tables.
   131  	_, err = DeleteSourceTables(etcdTestCli, st1)
   132  	c.Assert(err, IsNil)
   133  	rev4, err := DeleteSourceTables(etcdTestCli, st2)
   134  	c.Assert(err, IsNil)
   135  
   136  	// get without SourceTables.
   137  	stm, rev5, err := GetAllSourceTables(etcdTestCli)
   138  	c.Assert(err, IsNil)
   139  	c.Assert(rev5, Equals, rev4)
   140  	c.Assert(stm, HasLen, 0)
   141  
   142  	// watch the deletion for SourceTables.
   143  	wch = make(chan SourceTables, 10)
   144  	ech = make(chan error, 10)
   145  	ctx, cancel = context.WithTimeout(context.Background(), watchTimeout)
   146  	WatchSourceTables(ctx, etcdTestCli, rev4, wch, ech)
   147  	cancel()
   148  	close(wch)
   149  	close(ech)
   150  	c.Assert(len(wch), Equals, 1)
   151  	std := <-wch
   152  	c.Assert(std.IsDeleted, IsTrue)
   153  	c.Assert(std.Task, Equals, st2.Task)
   154  	c.Assert(std.Source, Equals, st2.Source)
   155  	c.Assert(len(ech), Equals, 0)
   156  }
   157  
   158  func TestToRouteTable(t *testing.T) {
   159  	var (
   160  		task1      = "task-1"
   161  		source1    = "mysql-replica-1"
   162  		downSchema = "db"
   163  		downTable  = "tbl"
   164  		upSchema   = "db"
   165  		upTable1   = "tbl-1"
   166  		upTable2   = "tbl-2"
   167  
   168  		tt11 = newTargetTable(task1, source1, downSchema, downTable, map[string]map[string]struct{}{
   169  			upSchema: {upTable1: struct{}{}, upTable2: struct{}{}},
   170  		})
   171  
   172  		result = map[RouteTable]struct{}{
   173  			{
   174  				UpSchema:   upSchema,
   175  				UpTable:    upTable1,
   176  				DownSchema: downSchema,
   177  				DownTable:  downTable,
   178  			}: {},
   179  			{
   180  				UpSchema:   upSchema,
   181  				UpTable:    upTable2,
   182  				DownSchema: downSchema,
   183  				DownTable:  downTable,
   184  			}: {},
   185  		}
   186  
   187  		st11 = NewSourceTables(task1, source1)
   188  	)
   189  
   190  	rt := st11.toRouteTable()
   191  	require.Len(t, rt, 0)
   192  
   193  	for schema, tables := range tt11.UpTables {
   194  		for table := range tables {
   195  			st11.AddTable(schema, table, tt11.DownSchema, tt11.DownTable)
   196  		}
   197  	}
   198  
   199  	rt = st11.toRouteTable()
   200  	require.Len(t, rt, 2)
   201  	require.Equal(t, result, rt)
   202  }
   203  
   204  func TestDiffSourceTable(t *testing.T) {
   205  	var (
   206  		task1      = "task-1"
   207  		source1    = "mysql-replica-1"
   208  		downSchema = "db"
   209  		downTable  = "tbl"
   210  		upSchema   = "db"
   211  		upTable1   = "tbl-1"
   212  		upTable2   = "tbl-2"
   213  
   214  		tt11 = newTargetTable(task1, source1, downSchema, downTable, map[string]map[string]struct{}{
   215  			upSchema: {upTable1: struct{}{}, upTable2: struct{}{}},
   216  		})
   217  
   218  		result1 = map[RouteTable]struct{}{
   219  			{
   220  				UpSchema:   upSchema,
   221  				UpTable:    upTable1,
   222  				DownSchema: downSchema,
   223  				DownTable:  downTable,
   224  			}: {},
   225  		}
   226  		result2 = map[RouteTable]struct{}{
   227  			{
   228  				UpSchema:   upSchema,
   229  				UpTable:    upTable2,
   230  				DownSchema: downSchema,
   231  				DownTable:  downTable,
   232  			}: {},
   233  		}
   234  		st11 SourceTables
   235  		st12 SourceTables
   236  	)
   237  
   238  	addTables, dropTables := DiffSourceTables(st11, st12)
   239  	require.Len(t, addTables, 0)
   240  	require.Len(t, dropTables, 0)
   241  
   242  	st11 = NewSourceTables(task1, source1)
   243  	st12 = NewSourceTables(task1, source1)
   244  	addTables, dropTables = DiffSourceTables(st11, st12)
   245  	require.Len(t, addTables, 0)
   246  	require.Len(t, dropTables, 0)
   247  
   248  	st11.AddTable(upSchema, upTable1, tt11.DownSchema, tt11.DownTable)
   249  
   250  	addTables, dropTables = DiffSourceTables(st11, st12)
   251  	require.Len(t, addTables, 0)
   252  	require.Len(t, dropTables, 1)
   253  	require.Equal(t, dropTables, result1)
   254  	addTables, dropTables = DiffSourceTables(st12, st11)
   255  	require.Len(t, addTables, 1)
   256  	require.Len(t, dropTables, 0)
   257  	require.Equal(t, addTables, result1)
   258  
   259  	st12.AddTable(upSchema, upTable2, tt11.DownSchema, tt11.DownTable)
   260  	addTables, dropTables = DiffSourceTables(st11, st12)
   261  	require.Len(t, addTables, 1)
   262  	require.Len(t, dropTables, 1)
   263  	require.Equal(t, addTables, result2)
   264  	require.Equal(t, dropTables, result1)
   265  }