github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/relay/meta_test.go (about)

     1  // Copyright 2019 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 relay
    15  
    16  import (
    17  	"fmt"
    18  	"os"
    19  	"path"
    20  	"strings"
    21  
    22  	"github.com/go-mysql-org/go-mysql/mysql"
    23  	. "github.com/pingcap/check"
    24  	"github.com/pingcap/tiflow/dm/pkg/gtid"
    25  )
    26  
    27  var _ = Suite(&testMetaSuite{})
    28  
    29  type testMetaSuite struct{}
    30  
    31  type MetaTestCase struct {
    32  	uuid           string
    33  	uuidWithSuffix string
    34  	pos            mysql.Position
    35  	gset           mysql.GTIDSet
    36  }
    37  
    38  func (r *testMetaSuite) TestLocalMeta(c *C) {
    39  	dir := c.MkDir()
    40  
    41  	gset0, _ := gtid.ParserGTID("mysql", "")
    42  	gset1, _ := gtid.ParserGTID("mysql", "85ab69d1-b21f-11e6-9c5e-64006a8978d2:1-12")
    43  	gset2, _ := gtid.ParserGTID("mysql", "85ab69d1-b21f-11e6-9c5e-64006a8978d2:13-23")
    44  	gset3, _ := gtid.ParserGTID("mysql", "85ab69d1-b21f-11e6-9c5e-64006a8978d2:24-33")
    45  	gset4, _ := gtid.ParserGTID("mysql", "85ab69d1-b21f-11e6-9c5e-64006a8978d2:34-46")
    46  	gset5, _ := gtid.ParserGTID("mysql", "85ab69d1-b21f-11e6-9c5e-64006a8978d2:45-56")
    47  
    48  	cases := []MetaTestCase{
    49  		{
    50  			uuid:           "server-a-uuid",
    51  			uuidWithSuffix: "server-a-uuid.000001",
    52  			pos:            mysql.Position{Name: "mysql-bin.000003", Pos: 123},
    53  			gset:           gset1,
    54  		},
    55  		{
    56  			uuid:           "server-b-uuid",
    57  			uuidWithSuffix: "server-b-uuid.000002",
    58  			pos:            mysql.Position{Name: "mysql-bin.000001", Pos: 234},
    59  			gset:           gset2,
    60  		},
    61  		{
    62  			uuid:           "server-b-uuid", // server-b-uuid again
    63  			uuidWithSuffix: "server-b-uuid.000003",
    64  			pos:            mysql.Position{Name: "mysql-bin.000002", Pos: 345},
    65  			gset:           gset3,
    66  		},
    67  		{
    68  			uuid:           "server-c-uuid",
    69  			uuidWithSuffix: "server-c-uuid.000004",
    70  			pos:            mysql.Position{Name: "mysql-bin.000004", Pos: 678},
    71  			gset:           gset4,
    72  		},
    73  	}
    74  
    75  	// load, but empty
    76  	lm := NewLocalMeta("mysql", dir)
    77  	err := lm.Load()
    78  	c.Assert(err, IsNil)
    79  
    80  	uuid, pos := lm.Pos()
    81  	c.Assert(uuid, Equals, "")
    82  	c.Assert(pos, DeepEquals, minCheckpoint)
    83  	uuid, gset := lm.GTID()
    84  	c.Assert(uuid, Equals, "")
    85  	c.Assert(gset, DeepEquals, gset0)
    86  
    87  	err = lm.Save(minCheckpoint, nil)
    88  	c.Assert(err, NotNil)
    89  
    90  	err = lm.Flush()
    91  	c.Assert(err, NotNil)
    92  
    93  	dirty := lm.Dirty()
    94  	c.Assert(dirty, IsFalse)
    95  
    96  	// set currentSubDir because lm.doFlush need it
    97  	currentUUID := "uuid.000001"
    98  	c.Assert(os.MkdirAll(path.Join(dir, currentUUID), 0o777), IsNil)
    99  	setLocalMetaWithCurrentUUID := func() {
   100  		lm = NewLocalMeta("mysql", dir)
   101  		lm.(*LocalMeta).currentSubDir = currentUUID
   102  	}
   103  
   104  	// adjust to start pos
   105  	setLocalMetaWithCurrentUUID()
   106  	latestBinlogName := "mysql-bin.000009"
   107  	latestGTIDStr := "85ab69d1-b21f-11e6-9c5e-64006a8978d2:45-57"
   108  	cs0 := cases[0]
   109  	adjusted, err := lm.AdjustWithStartPos(cs0.pos.Name, cs0.gset.String(), false, latestBinlogName, latestGTIDStr)
   110  	c.Assert(err, IsNil)
   111  	c.Assert(adjusted, IsTrue)
   112  	uuid, pos = lm.Pos()
   113  	c.Assert(uuid, Equals, currentUUID)
   114  	c.Assert(pos.Name, Equals, cs0.pos.Name)
   115  	uuid, gset = lm.GTID()
   116  	c.Assert(uuid, Equals, currentUUID)
   117  	c.Assert(gset.String(), Equals, "")
   118  
   119  	// adjust to start pos with enableGTID
   120  	setLocalMetaWithCurrentUUID()
   121  	adjusted, err = lm.AdjustWithStartPos(cs0.pos.Name, cs0.gset.String(), true, latestBinlogName, latestGTIDStr)
   122  	c.Assert(err, IsNil)
   123  	c.Assert(adjusted, IsTrue)
   124  	uuid, pos = lm.Pos()
   125  	c.Assert(uuid, Equals, currentUUID)
   126  	c.Assert(pos.Name, Equals, cs0.pos.Name)
   127  	uuid, gset = lm.GTID()
   128  	c.Assert(uuid, Equals, currentUUID)
   129  	c.Assert(gset, DeepEquals, cs0.gset)
   130  
   131  	// adjust to the last binlog if start pos is empty
   132  	setLocalMetaWithCurrentUUID()
   133  	adjusted, err = lm.AdjustWithStartPos("", cs0.gset.String(), false, latestBinlogName, latestGTIDStr)
   134  	c.Assert(err, IsNil)
   135  	c.Assert(adjusted, IsTrue)
   136  	uuid, pos = lm.Pos()
   137  	c.Assert(uuid, Equals, currentUUID)
   138  	c.Assert(pos.Name, Equals, latestBinlogName)
   139  	uuid, gset = lm.GTID()
   140  	c.Assert(uuid, Equals, currentUUID)
   141  	c.Assert(gset.String(), Equals, "")
   142  
   143  	setLocalMetaWithCurrentUUID()
   144  	adjusted, err = lm.AdjustWithStartPos("", "", true, latestBinlogName, latestGTIDStr)
   145  	c.Assert(err, IsNil)
   146  	c.Assert(adjusted, IsTrue)
   147  	uuid, pos = lm.Pos()
   148  	c.Assert(uuid, Equals, currentUUID)
   149  	c.Assert(pos.Name, Equals, latestBinlogName)
   150  	uuid, gset = lm.GTID()
   151  	c.Assert(uuid, Equals, currentUUID)
   152  	c.Assert(gset.String(), Equals, latestGTIDStr)
   153  
   154  	// reset
   155  	lm.(*LocalMeta).currentSubDir = ""
   156  
   157  	for _, cs := range cases {
   158  		err = lm.AddDir(cs.uuid, nil, nil, 0)
   159  		c.Assert(err, IsNil)
   160  
   161  		err = lm.Save(cs.pos, cs.gset)
   162  		c.Assert(err, IsNil)
   163  
   164  		currentUUID2, pos2 := lm.Pos()
   165  		c.Assert(currentUUID2, Equals, cs.uuidWithSuffix)
   166  		c.Assert(pos2, DeepEquals, cs.pos)
   167  
   168  		currentUUID, gset = lm.GTID()
   169  		c.Assert(currentUUID, Equals, cs.uuidWithSuffix)
   170  		c.Assert(gset, DeepEquals, cs.gset)
   171  
   172  		dirty = lm.Dirty()
   173  		c.Assert(dirty, IsTrue)
   174  
   175  		currentDir := lm.Dir()
   176  		c.Assert(strings.HasSuffix(currentDir, cs.uuidWithSuffix), IsTrue)
   177  	}
   178  
   179  	err = lm.Flush()
   180  	c.Assert(err, IsNil)
   181  
   182  	dirty = lm.Dirty()
   183  	c.Assert(dirty, IsFalse)
   184  
   185  	// try adjust to start pos again
   186  	csn1 := cases[len(cases)-1]
   187  	adjusted, err = lm.AdjustWithStartPos(cs0.pos.Name, cs0.gset.String(), false, "", "")
   188  	c.Assert(err, IsNil)
   189  	c.Assert(adjusted, IsFalse)
   190  	uuid, pos = lm.Pos()
   191  	c.Assert(uuid, Equals, csn1.uuidWithSuffix)
   192  	c.Assert(pos.Name, Equals, csn1.pos.Name)
   193  	uuid, gset = lm.GTID()
   194  	c.Assert(uuid, Equals, csn1.uuidWithSuffix)
   195  	c.Assert(gset, DeepEquals, csn1.gset)
   196  
   197  	// create a new LocalMeta, and load it
   198  	lm2 := NewLocalMeta("mysql", dir)
   199  	err = lm2.Load()
   200  	c.Assert(err, IsNil)
   201  
   202  	lastCase := cases[len(cases)-1]
   203  
   204  	uuid, pos = lm2.Pos()
   205  	c.Assert(uuid, Equals, lastCase.uuidWithSuffix)
   206  	c.Assert(pos, DeepEquals, lastCase.pos)
   207  	uuid, gset = lm2.GTID()
   208  	c.Assert(uuid, Equals, lastCase.uuidWithSuffix)
   209  	c.Assert(gset, DeepEquals, lastCase.gset)
   210  
   211  	// another case for AddDir, specify pos and GTID
   212  	cs := MetaTestCase{
   213  		uuid:           "server-c-uuid",
   214  		uuidWithSuffix: "server-c-uuid.000005",
   215  		pos:            mysql.Position{Name: "mysql-bin.000005", Pos: 789},
   216  		gset:           gset5,
   217  	}
   218  	err = lm.AddDir(cs.uuid, &cs.pos, cs.gset, 0)
   219  	c.Assert(err, IsNil)
   220  
   221  	dirty = lm.Dirty()
   222  	c.Assert(dirty, IsFalse)
   223  
   224  	currentUUID, pos = lm.Pos()
   225  	c.Assert(currentUUID, Equals, cs.uuidWithSuffix)
   226  	c.Assert(pos, DeepEquals, cs.pos)
   227  
   228  	currentUUID, gset = lm.GTID()
   229  	c.Assert(currentUUID, Equals, cs.uuidWithSuffix)
   230  	c.Assert(gset, DeepEquals, cs.gset)
   231  
   232  	currentDir := lm.Dir()
   233  	c.Assert(strings.HasSuffix(currentDir, cs.uuidWithSuffix), IsTrue)
   234  }
   235  
   236  func (r *testMetaSuite) TestLocalMetaPotentialDataRace(c *C) {
   237  	var err error
   238  	lm := NewLocalMeta("mysql", "/FAKE_DIR")
   239  	uuidStr := "85ab69d1-b21f-11e6-9c5e-64006a8978d2"
   240  	initGSet, _ := gtid.ParserGTID("mysql", fmt.Sprintf("%s:1", uuidStr))
   241  	lm.(*LocalMeta).currentSubDir = uuidStr
   242  	err = lm.Save(
   243  		mysql.Position{Name: "mysql-bin.000001", Pos: 234},
   244  		initGSet,
   245  	)
   246  	c.Assert(err, IsNil)
   247  
   248  	ch1 := make(chan error)
   249  	ch2 := make(chan error)
   250  	pendingCh := make(chan struct{})
   251  	go func() {
   252  		<-pendingCh
   253  		var err error
   254  		defer func() {
   255  			ch1 <- err
   256  		}()
   257  		var theMGSet mysql.GTIDSet
   258  		for i := 2; i < 100; i++ {
   259  			theMGSet, err = mysql.ParseGTIDSet("mysql", fmt.Sprintf("%s:1-%d", uuidStr, i*10))
   260  			if err != nil {
   261  				return
   262  			}
   263  
   264  			lastGTID := theMGSet
   265  			if err != nil {
   266  				return
   267  			}
   268  			err = lm.Save(
   269  				mysql.Position{Name: fmt.Sprintf("mysql-bin.%06d", i), Pos: 123},
   270  				lastGTID,
   271  			)
   272  			if err != nil {
   273  				return
   274  			}
   275  		}
   276  	}()
   277  	var gtidString string
   278  	go func() {
   279  		<-pendingCh
   280  		var err error
   281  		defer func() {
   282  			ch2 <- err
   283  		}()
   284  		for i := 0; i < 100; i++ {
   285  			_, currentGTID := lm.GTID()
   286  			gtidString = currentGTID.String()
   287  		}
   288  	}()
   289  	close(pendingCh)
   290  	ch1Err := <-ch1
   291  	ch2Err := <-ch2
   292  	c.Assert(ch1Err, IsNil)
   293  	c.Assert(ch2Err, IsNil)
   294  	c.Logf("GTID string from the go routine: %s", gtidString)
   295  }