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 }