github.com/pingcap/ticdc@v0.0.0-20220526033649-485a10ef2652/pkg/regionspan/region_range_lock_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 regionspan
    15  
    16  import (
    17  	"context"
    18  	"math"
    19  	"time"
    20  
    21  	"github.com/pingcap/check"
    22  	"github.com/pingcap/ticdc/pkg/util/testleak"
    23  )
    24  
    25  type regionRangeLockSuite struct{}
    26  
    27  var _ = check.Suite(&regionRangeLockSuite{})
    28  
    29  func mustSuccess(c *check.C, res LockRangeResult, expectedCheckpointTs uint64) {
    30  	c.Assert(res.Status, check.Equals, LockRangeStatusSuccess)
    31  	c.Assert(res.CheckpointTs, check.Equals, expectedCheckpointTs)
    32  }
    33  
    34  func mustStale(c *check.C, res LockRangeResult, expectedRetryRanges ...ComparableSpan) {
    35  	c.Assert(res.Status, check.Equals, LockRangeStatusStale)
    36  	c.Assert(res.RetryRanges, check.DeepEquals, expectedRetryRanges)
    37  }
    38  
    39  func mustWaitFn(c *check.C, res LockRangeResult) func() LockRangeResult {
    40  	c.Assert(res.Status, check.Equals, LockRangeStatusWait)
    41  	return res.WaitFn
    42  }
    43  
    44  func mustLockRangeSuccess(
    45  	ctx context.Context,
    46  	c *check.C,
    47  	l *RegionRangeLock,
    48  	startKey, endKey string,
    49  	regionID, version, expectedCheckpointTs uint64,
    50  ) {
    51  	res := l.LockRange(ctx, []byte(startKey), []byte(endKey), regionID, version)
    52  	mustSuccess(c, res, expectedCheckpointTs)
    53  }
    54  
    55  //nolint:unparam
    56  func mustLockRangeStale(
    57  	ctx context.Context,
    58  	c *check.C,
    59  	l *RegionRangeLock,
    60  	startKey, endKey string,
    61  	regionID, version uint64,
    62  	expectRetrySpans ...string,
    63  ) {
    64  	res := l.LockRange(ctx, []byte(startKey), []byte(endKey), regionID, version)
    65  	spans := make([]ComparableSpan, 0)
    66  	for i := 0; i < len(expectRetrySpans); i += 2 {
    67  		spans = append(spans, ComparableSpan{Start: []byte(expectRetrySpans[i]), End: []byte(expectRetrySpans[i+1])})
    68  	}
    69  	mustStale(c, res, spans...)
    70  }
    71  
    72  //nolint:unparam
    73  func mustLockRangeWait(
    74  	ctx context.Context,
    75  	c *check.C,
    76  	l *RegionRangeLock,
    77  	startKey, endKey string,
    78  	regionID, version uint64,
    79  ) func() LockRangeResult {
    80  	res := l.LockRange(ctx, []byte(startKey), []byte(endKey), regionID, version)
    81  	return mustWaitFn(c, res)
    82  }
    83  
    84  func unlockRange(l *RegionRangeLock, startKey, endKey string, regionID, version uint64, checkpointTs uint64) {
    85  	l.UnlockRange([]byte(startKey), []byte(endKey), regionID, version, checkpointTs)
    86  }
    87  
    88  func (s *regionRangeLockSuite) TestRegionRangeLock(c *check.C) {
    89  	defer testleak.AfterTest(c)()
    90  	ctx := context.TODO()
    91  	l := NewRegionRangeLock([]byte("a"), []byte("h"), math.MaxUint64)
    92  	mustLockRangeSuccess(ctx, c, l, "a", "e", 1, 1, math.MaxUint64)
    93  	unlockRange(l, "a", "e", 1, 1, 100)
    94  
    95  	mustLockRangeSuccess(ctx, c, l, "a", "e", 1, 2, 100)
    96  	mustLockRangeStale(ctx, c, l, "a", "e", 1, 2)
    97  	wait := mustLockRangeWait(ctx, c, l, "a", "h", 1, 3)
    98  
    99  	unlockRange(l, "a", "e", 1, 2, 110)
   100  	res := wait()
   101  	mustSuccess(c, res, 110)
   102  	unlockRange(l, "a", "h", 1, 3, 120)
   103  }
   104  
   105  func (s *regionRangeLockSuite) TestRegionRangeLockStale(c *check.C) {
   106  	defer testleak.AfterTest(c)()
   107  	l := NewRegionRangeLock([]byte("a"), []byte("z"), math.MaxUint64)
   108  	ctx := context.TODO()
   109  	mustLockRangeSuccess(ctx, c, l, "c", "g", 1, 10, math.MaxUint64)
   110  	mustLockRangeSuccess(ctx, c, l, "j", "n", 2, 8, math.MaxUint64)
   111  
   112  	mustLockRangeStale(ctx, c, l, "c", "g", 1, 10)
   113  	mustLockRangeStale(ctx, c, l, "c", "i", 1, 9, "g", "i")
   114  	mustLockRangeStale(ctx, c, l, "a", "z", 1, 9, "a", "c", "g", "j", "n", "z")
   115  	mustLockRangeStale(ctx, c, l, "a", "e", 1, 9, "a", "c")
   116  	mustLockRangeStale(ctx, c, l, "e", "h", 1, 9, "g", "h")
   117  	mustLockRangeStale(ctx, c, l, "e", "k", 1, 9, "g", "j")
   118  	mustLockRangeSuccess(ctx, c, l, "g", "j", 3, 1, math.MaxUint64)
   119  	unlockRange(l, "g", "j", 3, 1, 2)
   120  	unlockRange(l, "c", "g", 1, 10, 5)
   121  	unlockRange(l, "j", "n", 2, 8, 8)
   122  	mustLockRangeSuccess(ctx, c, l, "a", "z", 1, 11, 2)
   123  	unlockRange(l, "a", "z", 1, 11, 2)
   124  }
   125  
   126  func (s *regionRangeLockSuite) TestRegionRangeLockLockingRegionID(c *check.C) {
   127  	defer testleak.AfterTest(c)()
   128  	ctx := context.TODO()
   129  	l := NewRegionRangeLock([]byte("a"), []byte("z"), math.MaxUint64)
   130  	mustLockRangeSuccess(ctx, c, l, "c", "d", 1, 10, math.MaxUint64)
   131  
   132  	mustLockRangeStale(ctx, c, l, "e", "f", 1, 5, "e", "f")
   133  	mustLockRangeStale(ctx, c, l, "e", "f", 1, 10, "e", "f")
   134  	wait := mustLockRangeWait(ctx, c, l, "e", "f", 1, 11)
   135  	unlockRange(l, "c", "d", 1, 10, 10)
   136  	mustSuccess(c, wait(), math.MaxUint64)
   137  	// Now ["e", "f") is locked by region 1 at version 11 and ts 11.
   138  
   139  	mustLockRangeSuccess(ctx, c, l, "g", "h", 2, 10, math.MaxUint64)
   140  	wait = mustLockRangeWait(ctx, c, l, "g", "h", 1, 12)
   141  	ch := make(chan LockRangeResult, 1)
   142  	go func() {
   143  		ch <- wait()
   144  	}()
   145  	unlockRange(l, "g", "h", 2, 10, 20)
   146  	// Locking should still be blocked because the regionID 1 is still locked.
   147  	select {
   148  	case <-ch:
   149  		c.Fatalf("locking finished unexpectedly")
   150  	case <-time.After(time.Millisecond * 50):
   151  	}
   152  
   153  	unlockRange(l, "e", "f", 1, 11, 11)
   154  	res := <-ch
   155  	// CheckpointTS calculation should still be based on range and do not consider the regionID. So
   156  	// the result's checkpointTs should be 20 from of range ["g", "h"), instead of 11 from min(11, 20).
   157  	mustSuccess(c, res, 20)
   158  	unlockRange(l, "g", "h", 1, 12, 30)
   159  }
   160  
   161  func (s *regionRangeLockSuite) TestRegionRangeLockCanBeCancelled(c *check.C) {
   162  	defer testleak.AfterTest(c)()
   163  	ctx, cancel := context.WithCancel(context.Background())
   164  	l := NewRegionRangeLock([]byte("a"), []byte("z"), math.MaxUint64)
   165  	mustLockRangeSuccess(ctx, c, l, "g", "h", 1, 10, math.MaxUint64)
   166  	wait := mustLockRangeWait(ctx, c, l, "g", "h", 1, 12)
   167  	cancel()
   168  	lockResult := wait()
   169  	c.Assert(lockResult.Status, check.Equals, LockRangeStatusCancel)
   170  }
   171  
   172  func (s *regionRangeLockSuite) TestRangeTsMap(c *check.C) {
   173  	defer testleak.AfterTest(c)()
   174  	m := NewRangeTsMap([]byte("a"), []byte("z"), math.MaxUint64)
   175  
   176  	mustGetMin := func(startKey, endKey string, expectedTs uint64) {
   177  		ts := m.GetMin([]byte(startKey), []byte(endKey))
   178  		c.Assert(ts, check.Equals, expectedTs)
   179  	}
   180  	set := func(startKey, endKey string, ts uint64) {
   181  		m.Set([]byte(startKey), []byte(endKey), ts)
   182  	}
   183  
   184  	mustGetMin("a", "z", math.MaxUint64)
   185  	set("b", "e", 100)
   186  	mustGetMin("a", "z", 100)
   187  	mustGetMin("b", "e", 100)
   188  	mustGetMin("a", "c", 100)
   189  	mustGetMin("d", "f", 100)
   190  	mustGetMin("a", "b", math.MaxUint64)
   191  	mustGetMin("e", "f", math.MaxUint64)
   192  	mustGetMin("a", "b\x00", 100)
   193  
   194  	set("d", "g", 80)
   195  	mustGetMin("d", "g", 80)
   196  	mustGetMin("a", "z", 80)
   197  	mustGetMin("d", "e", 80)
   198  	mustGetMin("a", "d", 100)
   199  
   200  	set("c", "f", 120)
   201  	mustGetMin("c", "f", 120)
   202  	mustGetMin("c", "d", 120)
   203  	mustGetMin("d", "e", 120)
   204  	mustGetMin("e", "f", 120)
   205  	mustGetMin("b", "e", 100)
   206  	mustGetMin("a", "z", 80)
   207  
   208  	set("c", "f", 130)
   209  	mustGetMin("c", "f", 130)
   210  	mustGetMin("c", "d", 130)
   211  	mustGetMin("d", "e", 130)
   212  	mustGetMin("e", "f", 130)
   213  	mustGetMin("b", "e", 100)
   214  	mustGetMin("a", "z", 80)
   215  }