github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/relay/purger_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  	"bytes"
    18  	"context"
    19  	"os"
    20  	"path/filepath"
    21  	"strings"
    22  	"time"
    23  
    24  	. "github.com/pingcap/check"
    25  	"github.com/pingcap/tiflow/dm/config"
    26  	"github.com/pingcap/tiflow/dm/pb"
    27  	"github.com/pingcap/tiflow/dm/pkg/streamer"
    28  	"github.com/pingcap/tiflow/dm/pkg/utils"
    29  )
    30  
    31  var _ = Suite(&testPurgerSuite{
    32  	uuids: []string{
    33  		"c6ae5afe-c7a3-11e8-a19d-0242ac130006.000001",
    34  		"e9540a0d-f16d-11e8-8cb7-0242ac130008.000002",
    35  		"195c342f-f46e-11e8-927c-0242ac150008.000003",
    36  	},
    37  	relayFiles: [][]string{
    38  		{"mysql-bin.000001", "mysql-bin.000002", "mysql-bin.000003"},
    39  		{"mysql-bin.000001", "mysql-bin.000002", "mysql-bin.000003"},
    40  		{"mysql-bin.000001", "mysql-bin.000002", "mysql-bin.000003"},
    41  	},
    42  	activeRelayLog: &streamer.RelayLogInfo{
    43  		TaskName:     fakeStrategyTaskName,
    44  		SubDir:       "e9540a0d-f16d-11e8-8cb7-0242ac130008.000002",
    45  		SubDirSuffix: 2,
    46  		Filename:     "mysql-bin.000003", // last in second sub dir
    47  	},
    48  })
    49  
    50  type testPurgerSuite struct {
    51  	uuids          []string
    52  	relayFiles     [][]string
    53  	activeRelayLog *streamer.RelayLogInfo
    54  }
    55  
    56  func (t *testPurgerSuite) EarliestActiveRelayLog() *streamer.RelayLogInfo {
    57  	return t.activeRelayLog
    58  }
    59  
    60  func (t *testPurgerSuite) TestPurgeManuallyInactive(c *C) {
    61  	// create relay log dir
    62  	baseDir := c.MkDir()
    63  
    64  	// prepare files and directories
    65  	relayDirsPath, relayFilesPath, _ := t.genRelayLogFiles(c, baseDir, -1, -1)
    66  	c.Assert(len(relayDirsPath), Equals, 3)
    67  	c.Assert(len(relayFilesPath), Equals, 3)
    68  	c.Assert(len(relayFilesPath[2]), Equals, 3)
    69  
    70  	err := t.genUUIDIndexFile(baseDir)
    71  	c.Assert(err, IsNil)
    72  
    73  	cfg := config.PurgeConfig{
    74  		Interval: 0, // disable automatically
    75  	}
    76  
    77  	purger := NewPurger(cfg, baseDir, []Operator{t}, nil)
    78  
    79  	req := &pb.PurgeRelayRequest{
    80  		Inactive: true,
    81  	}
    82  	err = purger.Do(context.Background(), req)
    83  	c.Assert(err, IsNil)
    84  
    85  	c.Assert(utils.IsDirExists(relayDirsPath[0]), IsFalse)
    86  	c.Assert(utils.IsDirExists(relayDirsPath[1]), IsTrue)
    87  	c.Assert(utils.IsDirExists(relayDirsPath[2]), IsTrue)
    88  
    89  	c.Assert(utils.IsFileExists(relayFilesPath[1][0]), IsFalse)
    90  	c.Assert(utils.IsFileExists(relayFilesPath[1][1]), IsFalse)
    91  	c.Assert(utils.IsFileExists(relayFilesPath[1][2]), IsTrue)
    92  	for _, fp := range relayFilesPath[2] {
    93  		c.Assert(utils.IsFileExists(fp), IsTrue)
    94  	}
    95  }
    96  
    97  func (t *testPurgerSuite) TestPurgeManuallyTime(c *C) {
    98  	// create relay log dir
    99  	baseDir := c.MkDir()
   100  
   101  	// prepare files and directories
   102  	relayDirsPath, relayFilesPath, safeTime := t.genRelayLogFiles(c, baseDir, 1, 0)
   103  	c.Assert(len(relayDirsPath), Equals, 3)
   104  	c.Assert(len(relayFilesPath), Equals, 3)
   105  	c.Assert(len(relayFilesPath[2]), Equals, 3)
   106  
   107  	err := t.genUUIDIndexFile(baseDir)
   108  	c.Assert(err, IsNil)
   109  
   110  	cfg := config.PurgeConfig{
   111  		Interval: 0, // disable automatically
   112  	}
   113  
   114  	purger := NewPurger(cfg, baseDir, []Operator{t}, nil)
   115  
   116  	req := &pb.PurgeRelayRequest{
   117  		Time: safeTime.Unix(),
   118  	}
   119  	err = purger.Do(context.Background(), req)
   120  	c.Assert(err, IsNil)
   121  
   122  	c.Assert(utils.IsDirExists(relayDirsPath[0]), IsFalse)
   123  	c.Assert(utils.IsDirExists(relayDirsPath[1]), IsTrue)
   124  	c.Assert(utils.IsDirExists(relayDirsPath[2]), IsTrue)
   125  
   126  	c.Assert(utils.IsFileExists(relayFilesPath[1][0]), IsFalse)
   127  	c.Assert(utils.IsFileExists(relayFilesPath[1][1]), IsTrue)
   128  	c.Assert(utils.IsFileExists(relayFilesPath[1][2]), IsTrue)
   129  	for _, fp := range relayFilesPath[2] {
   130  		c.Assert(utils.IsFileExists(fp), IsTrue)
   131  	}
   132  }
   133  
   134  func (t *testPurgerSuite) TestPurgeManuallyFilename(c *C) {
   135  	// create relay log dir
   136  	baseDir := c.MkDir()
   137  
   138  	// prepare files and directories
   139  	relayDirsPath, relayFilesPath, _ := t.genRelayLogFiles(c, baseDir, -1, -1)
   140  	c.Assert(len(relayDirsPath), Equals, 3)
   141  	c.Assert(len(relayFilesPath), Equals, 3)
   142  	c.Assert(len(relayFilesPath[2]), Equals, 3)
   143  
   144  	err := t.genUUIDIndexFile(baseDir)
   145  	c.Assert(err, IsNil)
   146  
   147  	cfg := config.PurgeConfig{
   148  		Interval: 0, // disable automatically
   149  	}
   150  
   151  	purger := NewPurger(cfg, baseDir, []Operator{t}, nil)
   152  
   153  	req := &pb.PurgeRelayRequest{
   154  		Filename: t.relayFiles[0][2],
   155  		SubDir:   t.uuids[0],
   156  	}
   157  	err = purger.Do(context.Background(), req)
   158  	c.Assert(err, IsNil)
   159  
   160  	c.Assert(utils.IsDirExists(relayDirsPath[0]), IsTrue)
   161  	c.Assert(utils.IsDirExists(relayDirsPath[1]), IsTrue)
   162  	c.Assert(utils.IsDirExists(relayDirsPath[2]), IsTrue)
   163  
   164  	c.Assert(utils.IsFileExists(relayFilesPath[0][0]), IsFalse)
   165  	c.Assert(utils.IsFileExists(relayFilesPath[0][1]), IsFalse)
   166  	c.Assert(utils.IsFileExists(relayFilesPath[0][2]), IsTrue)
   167  	for _, fp := range relayFilesPath[1] {
   168  		c.Assert(utils.IsFileExists(fp), IsTrue)
   169  	}
   170  	for _, fp := range relayFilesPath[2] {
   171  		c.Assert(utils.IsFileExists(fp), IsTrue)
   172  	}
   173  }
   174  
   175  func (t *testPurgerSuite) TestPurgeAutomaticallyTime(c *C) {
   176  	// create relay log dir
   177  	baseDir := c.MkDir()
   178  
   179  	// prepare files and directories
   180  	relayDirsPath, relayFilesPath, _ := t.genRelayLogFiles(c, baseDir, -1, -1)
   181  	c.Assert(len(relayDirsPath), Equals, 3)
   182  	c.Assert(len(relayFilesPath), Equals, 3)
   183  	c.Assert(len(relayFilesPath[2]), Equals, 3)
   184  
   185  	err := t.genUUIDIndexFile(baseDir)
   186  	c.Assert(err, IsNil)
   187  
   188  	cfg := config.PurgeConfig{
   189  		Interval: 1, // enable automatically
   190  		Expires:  1,
   191  	}
   192  
   193  	// change files' modification time
   194  	aTime := time.Now().Add(time.Duration(-cfg.Expires*3) * time.Hour)
   195  	mTime := time.Now().Add(time.Duration(-cfg.Expires*2) * time.Hour)
   196  	for _, fps := range relayFilesPath {
   197  		for _, fp := range fps {
   198  			err = os.Chtimes(fp, aTime, mTime)
   199  			c.Assert(err, IsNil)
   200  		}
   201  	}
   202  
   203  	purger := NewPurger(cfg, baseDir, []Operator{t}, nil)
   204  	purger.Start()
   205  	time.Sleep(2 * time.Second) // sleep enough time to purge all inactive relay log files
   206  	purger.Close()
   207  
   208  	c.Assert(utils.IsDirExists(relayDirsPath[0]), IsFalse)
   209  	c.Assert(utils.IsDirExists(relayDirsPath[1]), IsTrue)
   210  	c.Assert(utils.IsDirExists(relayDirsPath[2]), IsTrue)
   211  
   212  	c.Assert(utils.IsFileExists(relayFilesPath[1][0]), IsFalse)
   213  	c.Assert(utils.IsFileExists(relayFilesPath[1][1]), IsFalse)
   214  	c.Assert(utils.IsFileExists(relayFilesPath[1][2]), IsTrue)
   215  	for _, fp := range relayFilesPath[2] {
   216  		c.Assert(utils.IsFileExists(fp), IsTrue)
   217  	}
   218  }
   219  
   220  func (t *testPurgerSuite) TestPurgeAutomaticallySpace(c *C) {
   221  	// create relay log dir
   222  	baseDir := c.MkDir()
   223  
   224  	// prepare files and directories
   225  	relayDirsPath, relayFilesPath, _ := t.genRelayLogFiles(c, baseDir, -1, -1)
   226  	c.Assert(len(relayDirsPath), Equals, 3)
   227  	c.Assert(len(relayFilesPath), Equals, 3)
   228  	c.Assert(len(relayFilesPath[2]), Equals, 3)
   229  
   230  	err := t.genUUIDIndexFile(baseDir)
   231  	c.Assert(err, IsNil)
   232  
   233  	storageSize, err := utils.GetStorageSize(baseDir)
   234  	c.Assert(err, IsNil)
   235  
   236  	cfg := config.PurgeConfig{
   237  		Interval:    1,                                                  // enable automatically
   238  		RemainSpace: int64(storageSize.Available)/1024/1024/1024 + 1024, // always trigger purge
   239  	}
   240  
   241  	purger := NewPurger(cfg, baseDir, []Operator{t}, nil)
   242  	purger.Start()
   243  	time.Sleep(2 * time.Second) // sleep enough time to purge all inactive relay log files
   244  	purger.Close()
   245  
   246  	c.Assert(utils.IsDirExists(relayDirsPath[0]), IsFalse)
   247  	c.Assert(utils.IsDirExists(relayDirsPath[1]), IsTrue)
   248  	c.Assert(utils.IsDirExists(relayDirsPath[2]), IsTrue)
   249  
   250  	c.Assert(utils.IsFileExists(relayFilesPath[1][0]), IsFalse)
   251  	c.Assert(utils.IsFileExists(relayFilesPath[1][1]), IsFalse)
   252  	c.Assert(utils.IsFileExists(relayFilesPath[1][2]), IsTrue)
   253  	for _, fp := range relayFilesPath[2] {
   254  		c.Assert(utils.IsFileExists(fp), IsTrue)
   255  	}
   256  }
   257  
   258  func (t *testPurgerSuite) genRelayLogFiles(c *C, baseDir string, safeTimeIdxI, safeTimeIdxJ int) ([]string, [][]string, time.Time) {
   259  	var (
   260  		relayDirsPath  = make([]string, 0, 3)
   261  		relayFilesPath = make([][]string, 0, 3)
   262  		safeTime       = time.Unix(0, 0)
   263  	)
   264  
   265  	for _, uuid := range t.uuids {
   266  		dir := filepath.Join(baseDir, uuid)
   267  		err := os.Mkdir(dir, 0o700)
   268  		c.Assert(err, IsNil)
   269  		relayDirsPath = append(relayDirsPath, dir)
   270  	}
   271  
   272  	// create relay log files
   273  	for i, uuid := range t.uuids {
   274  		dir := filepath.Join(baseDir, uuid)
   275  		relayFilesPath = append(relayFilesPath, []string{})
   276  		for j, fn := range t.relayFiles[i] {
   277  			fp := filepath.Join(dir, fn)
   278  			err2 := os.WriteFile(fp, []byte("meaningless file content"), 0o644)
   279  			c.Assert(err2, IsNil)
   280  			relayFilesPath[i] = append(relayFilesPath[i], fp)
   281  
   282  			if i == safeTimeIdxI && j == safeTimeIdxJ {
   283  				time.Sleep(time.Second)
   284  				safeTime = time.Now()
   285  				time.Sleep(time.Second)
   286  			}
   287  		}
   288  	}
   289  
   290  	return relayDirsPath, relayFilesPath, safeTime
   291  }
   292  
   293  func (t *testPurgerSuite) genUUIDIndexFile(baseDir string) error {
   294  	fp := filepath.Join(baseDir, utils.UUIDIndexFilename)
   295  
   296  	var buf bytes.Buffer
   297  	for _, uuid := range t.uuids {
   298  		buf.WriteString(uuid)
   299  		buf.WriteString("\n")
   300  	}
   301  
   302  	return utils.WriteFileAtomic(fp, buf.Bytes(), 0o644)
   303  }
   304  
   305  type fakeInterceptor struct {
   306  	msg string
   307  }
   308  
   309  func newFakeInterceptor() *fakeInterceptor {
   310  	return &fakeInterceptor{
   311  		msg: "forbid purge by fake interceptor",
   312  	}
   313  }
   314  
   315  func (i *fakeInterceptor) ForbidPurge() (bool, string) {
   316  	return true, i.msg
   317  }
   318  
   319  func (t *testPurgerSuite) TestPurgerInterceptor(c *C) {
   320  	cfg := config.PurgeConfig{}
   321  	interceptor := newFakeInterceptor()
   322  
   323  	purger := NewPurger(cfg, "", []Operator{t}, []PurgeInterceptor{interceptor})
   324  
   325  	req := &pb.PurgeRelayRequest{
   326  		Inactive: true,
   327  	}
   328  	err := purger.Do(context.Background(), req)
   329  	c.Assert(err, NotNil)
   330  	c.Assert(strings.Contains(err.Error(), interceptor.msg), IsTrue)
   331  }