github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/cdc/puller/frontier/frontier_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 frontier
    15  
    16  import (
    17  	"bytes"
    18  	"context"
    19  	"fmt"
    20  	"math"
    21  	"math/rand"
    22  	"sort"
    23  	"testing"
    24  
    25  	"github.com/pingcap/tiflow/cdc/kv/regionlock"
    26  	"github.com/pingcap/tiflow/cdc/processor/tablepb"
    27  	"github.com/pingcap/tiflow/pkg/spanz"
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  func TestSpanFrontier(t *testing.T) {
    32  	t.Parallel()
    33  	keyA := []byte("a")
    34  	keyB := []byte("b")
    35  	keyC := []byte("c")
    36  	keyD := []byte("d")
    37  
    38  	spAB := tablepb.Span{StartKey: keyA, EndKey: keyB}
    39  	spAC := tablepb.Span{StartKey: keyA, EndKey: keyC}
    40  	spAD := tablepb.Span{StartKey: keyA, EndKey: keyD}
    41  	spBC := tablepb.Span{StartKey: keyB, EndKey: keyC}
    42  	spBD := tablepb.Span{StartKey: keyB, EndKey: keyD}
    43  	spCD := tablepb.Span{StartKey: keyC, EndKey: keyD}
    44  
    45  	f := NewFrontier(5, spAD).(*spanFrontier)
    46  
    47  	require.Equal(t, uint64(5), f.Frontier())
    48  	require.Equal(t, `[a @ 5] [d @ Max] `, f.String())
    49  	checkFrontier(t, f)
    50  
    51  	f.Forward(
    52  		0, tablepb.Span{StartKey: []byte("d"), EndKey: []byte("e")},
    53  		100,
    54  	)
    55  	require.Equal(t, uint64(5), f.Frontier())
    56  	require.Equal(t, `[a @ 5] [d @ 100] [e @ Max] `, f.String())
    57  	checkFrontier(t, f)
    58  
    59  	f.Forward(
    60  		0, tablepb.Span{StartKey: []byte("g"), EndKey: []byte("h")},
    61  		200,
    62  	)
    63  	require.Equal(t, uint64(5), f.Frontier())
    64  	require.Equal(t, `[a @ 5] [d @ 100] [e @ Max] [g @ 200] [h @ Max] `, f.String())
    65  	checkFrontier(t, f)
    66  
    67  	// Forward the tracked span space.
    68  	f.Forward(
    69  		0, tablepb.Span{StartKey: []byte("a"), EndKey: []byte("d")},
    70  		1,
    71  	)
    72  	require.Equal(t, uint64(1), f.Frontier())
    73  	require.Equal(t, `[a @ 1] [d @ 100] [e @ Max] [g @ 200] [h @ Max] `, f.String())
    74  	checkFrontier(t, f)
    75  
    76  	// // Forward it again
    77  	f.Forward(
    78  		0, tablepb.Span{StartKey: []byte("a"), EndKey: []byte("d")},
    79  		2,
    80  	)
    81  	require.Equal(t, uint64(2), f.Frontier())
    82  	require.Equal(t, `[a @ 2] [d @ 100] [e @ Max] [g @ 200] [h @ Max] `, f.String())
    83  	checkFrontier(t, f)
    84  
    85  	// // Forward to smaller ts
    86  	f.Forward(
    87  		0, tablepb.Span{StartKey: []byte("a"), EndKey: []byte("d")},
    88  		1,
    89  	)
    90  	require.Equal(t, uint64(1), f.Frontier())
    91  	require.Equal(t, `[a @ 1] [d @ 100] [e @ Max] [g @ 200] [h @ Max] `, f.String())
    92  	checkFrontier(t, f)
    93  
    94  	// // Forward b-c
    95  	f.Forward(0, spBC, 3)
    96  	require.Equal(t, uint64(1), f.Frontier())
    97  	require.Equal(t, `[a @ 1] [b @ 3] [c @ 1] [d @ 100] [e @ Max] [g @ 200] [h @ Max] `, f.String())
    98  	checkFrontier(t, f)
    99  
   100  	// Forward b-c more to be 4
   101  	f.Forward(0, spBC, 4)
   102  	require.Equal(t, uint64(1), f.Frontier())
   103  	require.Equal(t, `[a @ 1] [b @ 4] [c @ 1] [d @ 100] [e @ Max] [g @ 200] [h @ Max] `, f.String())
   104  	checkFrontier(t, f)
   105  
   106  	// Forward all to at least 3
   107  	f.Forward(0, spAD, 3)
   108  	require.Equal(t, uint64(3), f.Frontier())
   109  	require.Equal(t, `[a @ 3] [d @ 100] [e @ Max] [g @ 200] [h @ Max] `, f.String())
   110  	checkFrontier(t, f)
   111  
   112  	// Forward AB and CD to be 5, keep BC at 4
   113  	f.Forward(0, spAB, 5)
   114  	require.Equal(t, uint64(3), f.Frontier())
   115  	require.Equal(t, `[a @ 5] [b @ 3] [d @ 100] [e @ Max] [g @ 200] [h @ Max] `, f.String())
   116  	checkFrontier(t, f)
   117  
   118  	f.Forward(0, spCD, 5)
   119  	require.Equal(t, uint64(3), f.Frontier())
   120  	require.Equal(t, `[a @ 5] [b @ 3] [c @ 5] [d @ 100] [e @ Max] [g @ 200] [h @ Max] `, f.String())
   121  	checkFrontier(t, f)
   122  
   123  	// Catch BC to be 5 too
   124  	f.Forward(0, spBC, 5)
   125  	require.Equal(t, uint64(5), f.Frontier())
   126  	require.Equal(t, `[a @ 5] [b @ 5] [c @ 5] [d @ 100] [e @ Max] [g @ 200] [h @ Max] `, f.String())
   127  	checkFrontier(t, f)
   128  
   129  	// Forward all to be 6
   130  	f.Forward(0, spAD, 6)
   131  	require.Equal(t, uint64(6), f.Frontier())
   132  	require.Equal(t, `[a @ 6] [d @ 100] [e @ Max] [g @ 200] [h @ Max] `, f.String())
   133  	checkFrontier(t, f)
   134  
   135  	// Forward ac to 7
   136  	f.Forward(0, spAC, 7)
   137  	require.Equal(t, uint64(6), f.Frontier())
   138  	require.Equal(t, `[a @ 7] [c @ 6] [d @ 100] [e @ Max] [g @ 200] [h @ Max] `, f.String())
   139  	checkFrontier(t, f)
   140  
   141  	// Forward bd to 8
   142  	f.Forward(0, spBD, 8)
   143  	require.Equal(t, uint64(7), f.Frontier())
   144  	require.Equal(t, `[a @ 7] [b @ 8] [d @ 100] [e @ Max] [g @ 200] [h @ Max] `, f.String())
   145  	checkFrontier(t, f)
   146  
   147  	// Forward ab to 8
   148  	f.Forward(0, spAB, 8)
   149  	require.Equal(t, uint64(8), f.Frontier())
   150  	require.Equal(t, `[a @ 8] [b @ 8] [d @ 100] [e @ Max] [g @ 200] [h @ Max] `, f.String())
   151  	checkFrontier(t, f)
   152  
   153  	f.Forward(0, tablepb.Span{StartKey: []byte("1"), EndKey: []byte("g")}, 9)
   154  	require.Equal(t, uint64(9), f.Frontier())
   155  	require.Equal(t, `[1 @ 9] [g @ 200] [h @ Max] `, f.String())
   156  	checkFrontier(t, f)
   157  
   158  	f.Forward(0, tablepb.Span{StartKey: []byte("g"), EndKey: []byte("i")}, 10)
   159  	require.Equal(t, uint64(9), f.Frontier())
   160  	require.Equal(t, `[1 @ 9] [g @ 10] [i @ Max] `, f.String())
   161  	checkFrontier(t, f)
   162  }
   163  
   164  func TestSpanFrontierFallback(t *testing.T) {
   165  	t.Parallel()
   166  	keyA := []byte("a")
   167  	keyB := []byte("b")
   168  	keyC := []byte("c")
   169  	keyD := []byte("d")
   170  	keyE := []byte("e")
   171  
   172  	spAB := tablepb.Span{StartKey: keyA, EndKey: keyB}
   173  	spBC := tablepb.Span{StartKey: keyB, EndKey: keyC}
   174  	spCD := tablepb.Span{StartKey: keyC, EndKey: keyD}
   175  	spDE := tablepb.Span{StartKey: keyD, EndKey: keyE}
   176  
   177  	f := NewFrontier(20, spAB).(*spanFrontier)
   178  	f.Forward(0, spBC, 20)
   179  	f.Forward(0, spCD, 10)
   180  	f.Forward(0, spDE, 20)
   181  
   182  	// [A, B) [B, C) [C, D) [D, E)
   183  	// 20     20     10     20
   184  	require.Equal(t, uint64(10), f.Frontier())
   185  	require.Equal(t, `[a @ 20] [b @ 20] [c @ 10] [d @ 20] [e @ Max] `, f.String())
   186  	checkFrontier(t, f)
   187  
   188  	// [A, B) [B, D) [D, E)
   189  	// 20     10     10
   190  	// [B, D) does not forward, because of split to [B, C) and [C, D) immediately
   191  
   192  	// [A, B) [B, C) [C, D) [D, E)
   193  	// 20     10     10     20
   194  	// [B, C) does not forward, because of merge into [A, C) immediately
   195  	f.Forward(0, spCD, 20)
   196  	require.Equal(t, uint64(20), f.Frontier())
   197  	// the frontier stoes [A, B) and [B, C) but they are not correct exactly
   198  	require.Equal(t, `[a @ 20] [b @ 20] [c @ 20] [d @ 20] [e @ Max] `, f.String())
   199  	checkFrontier(t, f)
   200  
   201  	// Bump, here we meet resolved ts fall back, where 10 is less than f.Frontier()
   202  	// But there is no data loss actually.
   203  	// f.Forward(spAC, 10)
   204  }
   205  
   206  func TestSpanString(t *testing.T) {
   207  	t.Parallel()
   208  
   209  	spAB := tablepb.Span{StartKey: []byte("a"), EndKey: []byte("b")}
   210  	spBC := tablepb.Span{StartKey: []byte("b"), EndKey: []byte("c")}
   211  	spCD := tablepb.Span{StartKey: []byte("c"), EndKey: []byte("d")}
   212  	spDE := tablepb.Span{StartKey: []byte("d"), EndKey: []byte("e")}
   213  	spEF := tablepb.Span{StartKey: []byte("e"), EndKey: []byte("f")}
   214  	spFG := tablepb.Span{StartKey: []byte("f"), EndKey: []byte("g")}
   215  	spGH := tablepb.Span{StartKey: []byte("g"), EndKey: []byte("h")}
   216  
   217  	spAH := tablepb.Span{StartKey: []byte("a"), EndKey: []byte("h")}
   218  	f := NewFrontier(1, spAH).(*spanFrontier)
   219  	require.Equal(t, `[0:61 @ 1] [0:68 @ Max] `, f.SpanString(spAH))
   220  
   221  	f.Forward(1, spAB, 2)
   222  	f.Forward(2, spBC, 5)
   223  	f.Forward(3, spCD, 10)
   224  	f.Forward(4, spDE, 20)
   225  	f.Forward(5, spEF, 30)
   226  	f.Forward(6, spFG, 25)
   227  	f.Forward(7, spGH, 35)
   228  	require.Equal(t, uint64(2), f.Frontier())
   229  	require.Equal(t, `[1:61 @ 2] [2:62 @ 5] [3:63 @ 10] [4:64 @ 20] [5:65 @ 30] [6:66 @ 25] [7:67 @ 35] [0:68 @ Max] `, f.stringWtihRegionID())
   230  	// Print 5 span: start, before, target span, next, end
   231  	require.Equal(t, `[1:61 @ 2] [3:63 @ 10] [4:64 @ 20] [5:65 @ 30] [0:68 @ Max] `, f.SpanString(spDE))
   232  
   233  	spBH := tablepb.Span{StartKey: []byte("b"), EndKey: []byte("h")}
   234  	f.Forward(8, spBH, 18)
   235  	require.Equal(t, uint64(2), f.Frontier())
   236  	require.Equal(t, `[1:61 @ 2] [8:62 @ 18] [0:68 @ Max] `, f.stringWtihRegionID())
   237  }
   238  
   239  func TestMinMax(t *testing.T) {
   240  	t.Parallel()
   241  	var keyMin []byte
   242  	var keyMax []byte
   243  	keyMid := []byte("m")
   244  
   245  	spMinMid := tablepb.Span{StartKey: keyMin, EndKey: keyMid}
   246  	spMinMid = spanz.HackSpan(spMinMid)
   247  	spMidMax := tablepb.Span{StartKey: keyMid, EndKey: keyMax}
   248  	spMidMax = spanz.HackSpan(spMidMax)
   249  	spMinMax := tablepb.Span{StartKey: keyMin, EndKey: keyMax}
   250  	spMinMax = spanz.HackSpan(spMinMax)
   251  
   252  	f := NewFrontier(0, spMinMax)
   253  	require.Equal(t, uint64(0), f.Frontier())
   254  	require.Equal(t, "[ @ 0] [\xff\xff\xff\xff\xff @ Max] ", f.String())
   255  	checkFrontier(t, f)
   256  
   257  	f.Forward(0, spMinMax, 1)
   258  	require.Equal(t, uint64(1), f.Frontier())
   259  	require.Equal(t, "[ @ 1] [\xff\xff\xff\xff\xff @ Max] ", f.String())
   260  	checkFrontier(t, f)
   261  
   262  	f.Forward(0, spMinMid, 2)
   263  	require.Equal(t, uint64(1), f.Frontier())
   264  	require.Equal(t, "[ @ 2] [m @ 1] [\xff\xff\xff\xff\xff @ Max] ", f.String())
   265  	checkFrontier(t, f)
   266  
   267  	f.Forward(0, spMidMax, 2)
   268  	require.Equal(t, uint64(2), f.Frontier())
   269  	require.Equal(t, "[ @ 2] [m @ 2] [\xff\xff\xff\xff\xff @ Max] ", f.String())
   270  	checkFrontier(t, f)
   271  
   272  	f.Forward(0, spMinMax, 3)
   273  	require.Equal(t, uint64(3), f.Frontier())
   274  	require.Equal(t, "[ @ 3] [\xff\xff\xff\xff\xff @ Max] ", f.String())
   275  	checkFrontier(t, f)
   276  }
   277  
   278  func TestSpanFrontierDisjoinSpans(t *testing.T) {
   279  	t.Parallel()
   280  	key1 := []byte("1")
   281  	key2 := []byte("2")
   282  	keyA := []byte("a")
   283  	keyB := []byte("b")
   284  	keyC := []byte("c")
   285  	keyD := []byte("d")
   286  	keyE := []byte("e")
   287  	keyF := []byte("f")
   288  
   289  	spAB := tablepb.Span{StartKey: keyA, EndKey: keyB}
   290  	spAD := tablepb.Span{StartKey: keyA, EndKey: keyD}
   291  	spAE := tablepb.Span{StartKey: keyA, EndKey: keyE}
   292  	spDE := tablepb.Span{StartKey: keyD, EndKey: keyE}
   293  	spCE := tablepb.Span{StartKey: keyC, EndKey: keyE}
   294  	sp12 := tablepb.Span{StartKey: key1, EndKey: key2}
   295  	sp1F := tablepb.Span{StartKey: key1, EndKey: keyF}
   296  
   297  	f := NewFrontier(0, spAB, spCE)
   298  	require.Equal(t, uint64(0), f.Frontier())
   299  	require.Equal(t, `[a @ 0] [b @ Max] [c @ 0] [e @ Max] `, f.String())
   300  	checkFrontier(t, f)
   301  
   302  	// Advance the tracked spans
   303  	f.Forward(0, spAB, 1)
   304  	require.Equal(t, uint64(0), f.Frontier())
   305  	require.Equal(t, `[a @ 1] [b @ Max] [c @ 0] [e @ Max] `, f.String())
   306  	checkFrontier(t, f)
   307  	f.Forward(0, spCE, 1)
   308  	require.Equal(t, uint64(1), f.Frontier())
   309  	require.Equal(t, `[a @ 1] [b @ Max] [c @ 1] [e @ Max] `, f.String())
   310  	checkFrontier(t, f)
   311  
   312  	// Advance d-e split c-e to c-d and d-e
   313  	f.Forward(0, spDE, 2)
   314  	require.Equal(t, uint64(1), f.Frontier())
   315  	require.Equal(t, `[a @ 1] [b @ Max] [c @ 1] [d @ 2] [e @ Max] `, f.String())
   316  	checkFrontier(t, f)
   317  
   318  	// Advance a-d cover a-b and c-d
   319  	f.Forward(0, spAD, 3)
   320  	require.Equal(t, uint64(2), f.Frontier())
   321  	require.Equal(t, `[a @ 3] [d @ 2] [e @ Max] `, f.String())
   322  	checkFrontier(t, f)
   323  
   324  	// Advance one cover all 3 span
   325  	f.Forward(0, spAE, 4)
   326  	require.Equal(t, uint64(4), f.Frontier())
   327  	require.Equal(t, `[a @ 4] [e @ Max] `, f.String())
   328  	checkFrontier(t, f)
   329  
   330  	// Advance all with a larger span
   331  	f.Forward(0, sp1F, 5)
   332  	require.Equal(t, uint64(5), f.Frontier())
   333  	require.Equal(t, `[1 @ 5] [f @ Max] `, f.String())
   334  	checkFrontier(t, f)
   335  
   336  	// Advance span smaller than all tracked spans
   337  	f.Forward(0, sp12, 6)
   338  	require.Equal(t, uint64(5), f.Frontier())
   339  	require.Equal(t, `[1 @ 6] [2 @ 5] [f @ Max] `, f.String())
   340  	checkFrontier(t, f)
   341  }
   342  
   343  func TestSpanFrontierRandomly(t *testing.T) {
   344  	t.Parallel()
   345  	var keyMin []byte
   346  	var keyMax []byte
   347  	spMinMax := tablepb.Span{StartKey: keyMin, EndKey: keyMax}
   348  	f := NewFrontier(0, spMinMax)
   349  
   350  	var spans []tablepb.Span
   351  	for len(spans) < 500000 {
   352  		span := tablepb.Span{
   353  			StartKey: make([]byte, rand.Intn(32)+1),
   354  			EndKey:   make([]byte, rand.Intn(32)+1),
   355  		}
   356  		rand.Read(span.StartKey)
   357  		rand.Read(span.EndKey)
   358  		cmp := bytes.Compare(span.StartKey, span.EndKey)
   359  		if cmp == 0 {
   360  			continue
   361  		} else if cmp > 0 {
   362  			span.StartKey, span.EndKey = span.EndKey, span.StartKey
   363  		}
   364  
   365  		spans = append(spans, span)
   366  
   367  		ts := rand.Uint64()
   368  
   369  		f.Forward(0, span, ts)
   370  		checkFrontier(t, f)
   371  	}
   372  }
   373  
   374  func checkFrontier(t *testing.T, f Frontier) {
   375  	sf := f.(*spanFrontier)
   376  	var tsInList, tsInHeap []uint64
   377  	sf.spanList.Entries(func(n *skipListNode) bool {
   378  		tsInList = append(tsInList, n.Value().key)
   379  		return true
   380  	})
   381  	sf.minTsHeap.Entries(func(n *fibonacciHeapNode) bool {
   382  		tsInHeap = append(tsInHeap, n.key)
   383  		return true
   384  	})
   385  	require.Equal(t, len(tsInHeap), len(tsInList))
   386  	sort.Slice(tsInList, func(i, j int) bool { return tsInList[i] < tsInList[j] })
   387  	sort.Slice(tsInHeap, func(i, j int) bool { return tsInHeap[i] < tsInHeap[j] })
   388  	require.Equal(t, tsInHeap, tsInList)
   389  	require.Equal(t, tsInList[0], f.Frontier())
   390  }
   391  
   392  func TestMinMaxWithRegionSplitMerge(t *testing.T) {
   393  	t.Parallel()
   394  
   395  	ab := tablepb.Span{StartKey: []byte("a"), EndKey: []byte("b")}
   396  	bc := tablepb.Span{StartKey: []byte("b"), EndKey: []byte("c")}
   397  	cd := tablepb.Span{StartKey: []byte("c"), EndKey: []byte("d")}
   398  	de := tablepb.Span{StartKey: []byte("d"), EndKey: []byte("e")}
   399  	ef := tablepb.Span{StartKey: []byte("e"), EndKey: []byte("f")}
   400  	af := tablepb.Span{StartKey: []byte("a"), EndKey: []byte("f")}
   401  
   402  	f := NewFrontier(0, af)
   403  	require.Equal(t, uint64(0), f.Frontier())
   404  	f.Forward(1, ab, 1)
   405  	require.Equal(t, uint64(0), f.Frontier())
   406  	f.Forward(2, bc, 1)
   407  	require.Equal(t, uint64(0), f.Frontier())
   408  	f.Forward(3, cd, 1)
   409  	require.Equal(t, uint64(0), f.Frontier())
   410  	f.Forward(4, de, 1)
   411  	require.Equal(t, uint64(0), f.Frontier())
   412  	f.Forward(5, ef, 1)
   413  	require.Equal(t, uint64(1), f.Frontier())
   414  	f.Forward(6, tablepb.Span{StartKey: []byte("a"), EndKey: []byte("d")}, 6)
   415  	require.Equal(t, uint64(1), f.Frontier())
   416  	f.Forward(7, tablepb.Span{StartKey: []byte("d"), EndKey: []byte("f")}, 2)
   417  	require.Equal(t, uint64(2), f.Frontier())
   418  	f.Forward(7, tablepb.Span{StartKey: []byte("d"), EndKey: []byte("f")}, 3)
   419  	require.Equal(t, uint64(3), f.Frontier())
   420  	f.Forward(7, tablepb.Span{StartKey: []byte("d"), EndKey: []byte("f")}, 4)
   421  	require.Equal(t, uint64(4), f.Frontier())
   422  	f.Forward(8, tablepb.Span{StartKey: []byte("d"), EndKey: []byte("e")}, 4)
   423  	require.Equal(t, uint64(4), f.Frontier())
   424  	f.Forward(9, tablepb.Span{StartKey: []byte("e"), EndKey: []byte("f")}, 4)
   425  	require.Equal(t, uint64(4), f.Frontier())
   426  	f.Forward(9, tablepb.Span{StartKey: []byte("e"), EndKey: []byte("f")}, 7)
   427  	require.Equal(t, uint64(4), f.Frontier())
   428  	f.Forward(8, tablepb.Span{StartKey: []byte("d"), EndKey: []byte("e")}, 5)
   429  	require.Equal(t, uint64(5), f.Frontier())
   430  }
   431  
   432  func TestFrontierEntries(t *testing.T) {
   433  	t.Parallel()
   434  
   435  	ab := tablepb.Span{StartKey: []byte("a"), EndKey: []byte("b")}
   436  	bc := tablepb.Span{StartKey: []byte("b"), EndKey: []byte("c")}
   437  	cd := tablepb.Span{StartKey: []byte("c"), EndKey: []byte("d")}
   438  	de := tablepb.Span{StartKey: []byte("d"), EndKey: []byte("e")}
   439  	ef := tablepb.Span{StartKey: []byte("e"), EndKey: []byte("f")}
   440  	af := tablepb.Span{StartKey: []byte("a"), EndKey: []byte("f")}
   441  	f := NewFrontier(0, af)
   442  
   443  	var slowestTs uint64 = math.MaxUint64
   444  	var slowestRange tablepb.Span
   445  	getSlowestRange := func() {
   446  		slowestTs = math.MaxUint64
   447  		slowestRange = tablepb.Span{}
   448  		f.Entries(func(key []byte, ts uint64) {
   449  			if ts < slowestTs {
   450  				slowestTs = ts
   451  				slowestRange.StartKey = key
   452  				slowestRange.EndKey = nil
   453  			} else if slowestTs != math.MaxUint64 && len(slowestRange.EndKey) == 0 {
   454  				slowestRange.EndKey = key
   455  			}
   456  		})
   457  	}
   458  
   459  	getSlowestRange()
   460  	require.Equal(t, uint64(0), slowestTs)
   461  	require.Equal(t, []byte("a"), []byte(slowestRange.StartKey))
   462  	require.Equal(t, []byte("f"), []byte(slowestRange.EndKey))
   463  
   464  	f.Forward(1, ab, 100)
   465  	f.Forward(2, bc, 200)
   466  	f.Forward(3, cd, 300)
   467  	f.Forward(4, de, 400)
   468  	f.Forward(5, ef, 500)
   469  	getSlowestRange()
   470  	require.Equal(t, uint64(100), slowestTs)
   471  	require.Equal(t, []byte("a"), []byte(slowestRange.StartKey))
   472  	require.Equal(t, []byte("b"), []byte(slowestRange.EndKey))
   473  }
   474  
   475  func TestMergeSpitWithDifferentRegionID(t *testing.T) {
   476  	frontier := NewFrontier(100, tablepb.Span{StartKey: []byte("a"), EndKey: []byte("c")})
   477  	frontier.Forward(1, tablepb.Span{StartKey: []byte("a"), EndKey: []byte("b")}, 1222)
   478  	frontier.Forward(2, tablepb.Span{StartKey: []byte("b"), EndKey: []byte("c")}, 102)
   479  	frontier.Forward(4, tablepb.Span{StartKey: []byte("b"), EndKey: []byte("c")}, 103)
   480  	frontier.Forward(1, tablepb.Span{StartKey: []byte("a"), EndKey: []byte("c")}, 104)
   481  	frontier.Forward(1, tablepb.Span{StartKey: []byte("a"), EndKey: []byte("b")}, 1223)
   482  	frontier.Forward(3, tablepb.Span{StartKey: []byte("b"), EndKey: []byte("c")}, 105)
   483  	frontier.Forward(2, tablepb.Span{StartKey: []byte("b"), EndKey: []byte("c")}, 107)
   484  	frontier.(*spanFrontier).spanList.Entries(func(node *skipListNode) bool {
   485  		fmt.Printf("%d:[%s: %s) %d\n", node.regionID,
   486  			string(node.Key()),
   487  			string(node.End()), node.value.key)
   488  		return true
   489  	})
   490  	require.Equal(t, uint64(107), frontier.Frontier())
   491  }
   492  
   493  func TestRandomMergeAndSplit(t *testing.T) {
   494  	t.Parallel()
   495  
   496  	start, end := spanz.GetTableRange(8616)
   497  	rangelock := regionlock.NewRangeLock(1, start, end, 100, "")
   498  	frontier := NewFrontier(100, tablepb.Span{StartKey: start, EndKey: end})
   499  	ctx := context.Background()
   500  
   501  	var nextRegionID uint64 = 1
   502  	var nextVersion uint64 = 1
   503  	var nextTs uint64 = 100
   504  	rangelock.LockRange(ctx, start, end, nextRegionID, nextVersion)
   505  
   506  	nextTs += 1
   507  	frontier.Forward(1, tablepb.Span{StartKey: start, EndKey: end}, nextTs)
   508  	require.Equal(t, nextTs, frontier.Frontier())
   509  
   510  	for i := 0; i < 5000; i++ {
   511  		totalLockedRanges := rangelock.Len()
   512  		unchangedRegions := make([]lockedRegion, 0, totalLockedRanges)
   513  
   514  		mergeOrSplit := "split"
   515  		if totalLockedRanges > 1 && rand.Intn(2) > 0 {
   516  			mergeOrSplit = "merge"
   517  		}
   518  
   519  		nextTs += 1
   520  		if mergeOrSplit == "split" {
   521  			var r1, r2 lockedRegion
   522  			selected := rand.Intn(totalLockedRanges)
   523  			count := 0
   524  			rangelock.IterForTest(func(regionID, version uint64, state *regionlock.LockedRangeState, span tablepb.Span) {
   525  				ts := state.ResolvedTs.Load()
   526  				startKey := span.StartKey
   527  				endKey := span.EndKey
   528  				if count == selected {
   529  					r1 = lockedRegion{regionID, version, startKey, endKey, ts}
   530  				} else {
   531  					r := lockedRegion{regionID, version, startKey, endKey, ts}
   532  					unchangedRegions = append(unchangedRegions, r)
   533  				}
   534  				count += 1
   535  			})
   536  
   537  			rangelock.UnlockRange(r1.startKey, r1.endKey, r1.regionID, r1.version)
   538  
   539  			r2 = r1.split(&nextRegionID, &nextVersion)
   540  			rangelock.LockRange(ctx, r1.startKey, r1.endKey, r1.regionID, nextVersion)
   541  			rangelock.LockRange(ctx, r2.startKey, r2.endKey, r2.regionID, nextVersion)
   542  
   543  			frontier.Forward(r1.regionID, tablepb.Span{StartKey: r1.startKey, EndKey: r1.endKey}, nextTs)
   544  			frontier.Forward(r2.regionID, tablepb.Span{StartKey: r2.startKey, EndKey: r2.endKey}, nextTs)
   545  		} else {
   546  			var r1, r2 lockedRegion
   547  			selected := rand.Intn(totalLockedRanges - 1)
   548  			count := 0
   549  			rangelock.IterForTest(func(regionID, version uint64, state *regionlock.LockedRangeState, span tablepb.Span) {
   550  				ts := state.ResolvedTs.Load()
   551  				startKey := span.StartKey
   552  				endKey := span.EndKey
   553  				if count == selected {
   554  					r1 = lockedRegion{regionID, version, startKey, endKey, ts}
   555  				} else if count == selected+1 {
   556  					r2 = lockedRegion{regionID, version, startKey, endKey, ts}
   557  				} else {
   558  					r := lockedRegion{regionID, version, startKey, endKey, ts}
   559  					unchangedRegions = append(unchangedRegions, r)
   560  				}
   561  				count += 1
   562  			})
   563  
   564  			rangelock.UnlockRange(r1.startKey, r1.endKey, r1.regionID, r1.version)
   565  			rangelock.UnlockRange(r2.startKey, r2.endKey, r2.regionID, r2.version)
   566  
   567  			r2.merge(r1, &nextVersion)
   568  			rangelock.LockRange(ctx, r2.startKey, r2.endKey, r2.regionID, nextVersion)
   569  
   570  			frontier.Forward(r2.regionID, tablepb.Span{StartKey: r2.startKey, EndKey: r2.endKey}, nextTs)
   571  		}
   572  		for _, r := range unchangedRegions {
   573  			frontier.Forward(r.regionID, tablepb.Span{StartKey: r.startKey, EndKey: r.endKey}, nextTs)
   574  		}
   575  		require.Equal(t, nextTs, frontier.Frontier())
   576  	}
   577  }
   578  
   579  type lockedRegion struct {
   580  	regionID uint64
   581  	version  uint64
   582  	startKey []byte
   583  	endKey   []byte
   584  	ts       uint64
   585  }
   586  
   587  func (r *lockedRegion) split(regionIDGen *uint64, versionGen *uint64) (s lockedRegion) {
   588  	*regionIDGen += 1
   589  	*versionGen += 1
   590  
   591  	s.regionID = *regionIDGen
   592  	s.version = *versionGen
   593  	s.ts = r.ts
   594  	s.startKey = r.startKey
   595  
   596  	s.endKey = make([]byte, len(r.startKey)+1)
   597  	copy(s.endKey, r.startKey)
   598  	for {
   599  		s.endKey[len(s.endKey)-1] = '1'
   600  		if bytes.Compare(s.endKey, r.endKey) < 0 {
   601  			break
   602  		}
   603  		s.endKey[len(s.endKey)-1] = '0'
   604  		s.endKey = append(s.endKey, '0')
   605  	}
   606  
   607  	r.version = *versionGen
   608  	r.startKey = make([]byte, len(s.endKey))
   609  	copy(r.startKey, s.endKey)
   610  	return
   611  }
   612  
   613  func (r *lockedRegion) merge(s lockedRegion, versionGen *uint64) {
   614  	if !bytes.Equal(r.startKey, s.endKey) {
   615  		panic("bad merge")
   616  	}
   617  
   618  	*versionGen += 1
   619  	r.startKey = s.startKey
   620  	r.version = *versionGen
   621  }