github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/kv/kvserver/tscache/interval_skl_test.go (about) 1 // Copyright 2017 Andy Kimball 2 // Copyright 2017 The Cockroach Authors. 3 // 4 // Use of this software is governed by the Business Source License 5 // included in the file licenses/BSL.txt. 6 // 7 // As of the Change Date specified in that file, in accordance with 8 // the Business Source License, use of this software will be governed 9 // by the Apache License, Version 2.0, included in the file 10 // licenses/APL.txt. 11 12 package tscache 13 14 import ( 15 "bytes" 16 "fmt" 17 "math" 18 "math/rand" 19 "runtime" 20 "sync" 21 "testing" 22 "time" 23 24 "github.com/andy-kimball/arenaskl" 25 "github.com/cockroachdb/cockroach/pkg/testutils" 26 "github.com/cockroachdb/cockroach/pkg/util" 27 "github.com/cockroachdb/cockroach/pkg/util/hlc" 28 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 29 "github.com/cockroachdb/cockroach/pkg/util/timeutil" 30 "github.com/cockroachdb/cockroach/pkg/util/uuid" 31 "github.com/stretchr/testify/require" 32 ) 33 34 var ( 35 emptyVal = cacheValue{} 36 floorTS = makeTS(100, 0) 37 floorVal = makeValWithoutID(floorTS) 38 ) 39 40 func makeTS(walltime int64, logical int32) hlc.Timestamp { 41 return hlc.Timestamp{WallTime: walltime, Logical: logical} 42 } 43 44 func makeValWithoutID(ts hlc.Timestamp) cacheValue { 45 return cacheValue{ts: ts, txnID: noTxnID} 46 } 47 48 func makeVal(ts hlc.Timestamp, txnIDStr string) cacheValue { 49 txnIDBytes := []byte(txnIDStr) 50 if len(txnIDBytes) < 16 { 51 // If too short, pad front with zeros. 52 oldTxnIDBytes := txnIDBytes 53 txnIDBytes = make([]byte, 16) 54 copy(txnIDBytes[16-len(oldTxnIDBytes):], oldTxnIDBytes) 55 } 56 txnID, err := uuid.FromBytes(txnIDBytes) 57 if err != nil { 58 panic(err) 59 } 60 return cacheValue{ts: ts, txnID: txnID} 61 } 62 63 func makeSklMetrics() sklMetrics { 64 return makeMetrics().Skl 65 } 66 67 // setFixedPageSize sets the pageSize of the intervalSkl to a fixed value. 68 func (s *intervalSkl) setFixedPageSize(pageSize uint32) { 69 s.pageSize = pageSize 70 s.pageSizeFixed = true 71 s.pages.Init() // clear 72 s.pushNewPage(0 /* maxWallTime */, nil /* arena */) 73 } 74 75 // setMinPages sets the minimum number of pages intervalSkl will evict down to. 76 // This is only exposed as a testing method because there's no reason to use 77 // this outside of testing. 78 func (s *intervalSkl) setMinPages(minPages int) { 79 s.minPages = minPages 80 } 81 82 func TestIntervalSklAdd(t *testing.T) { 83 ts1 := makeTS(200, 0) 84 ts2 := makeTS(200, 201) 85 ts3Ceil := makeTS(201, 0) 86 87 val1 := makeVal(ts1, "1") 88 val2 := makeVal(ts2, "2") 89 90 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 91 92 s.Add([]byte("apricot"), val1) 93 require.Equal(t, ts1.WallTime, s.frontPage().maxWallTime) 94 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("apple"))) 95 require.Equal(t, val1, s.LookupTimestamp([]byte("apricot"))) 96 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("banana"))) 97 98 s.Add([]byte("banana"), val2) 99 require.Equal(t, ts3Ceil.WallTime, s.frontPage().maxWallTime) 100 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("apple"))) 101 require.Equal(t, val1, s.LookupTimestamp([]byte("apricot"))) 102 require.Equal(t, val2, s.LookupTimestamp([]byte("banana"))) 103 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("cherry"))) 104 } 105 106 func TestIntervalSklSingleRange(t *testing.T) { 107 val1 := makeVal(makeTS(100, 10), "1") 108 val2 := makeVal(makeTS(200, 50), "2") 109 val3 := makeVal(makeTS(300, 50), "3") 110 val4 := makeVal(makeTS(400, 50), "4") 111 112 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 113 114 // val1: [a--------------o] 115 s.AddRange([]byte("apricot"), []byte("orange"), 0, val1) 116 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("apple"))) 117 require.Equal(t, val1, s.LookupTimestamp([]byte("apricot"))) 118 require.Equal(t, val1, s.LookupTimestamp([]byte("banana"))) 119 require.Equal(t, val1, s.LookupTimestamp([]byte("orange"))) 120 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("raspberry"))) 121 122 // Try again and make sure it's a no-op. 123 // val1: [a--------------o] 124 s.AddRange([]byte("apricot"), []byte("orange"), 0, val1) 125 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("apple"))) 126 require.Equal(t, val1, s.LookupTimestamp([]byte("apricot"))) 127 require.Equal(t, val1, s.LookupTimestamp([]byte("banana"))) 128 require.Equal(t, val1, s.LookupTimestamp([]byte("orange"))) 129 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("raspberry"))) 130 131 // Ratchet up the timestamps. 132 // val1: [a--------------o] 133 // val2: [a--------------o] 134 s.AddRange([]byte("apricot"), []byte("orange"), 0, val2) 135 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("apple"))) 136 require.Equal(t, val2, s.LookupTimestamp([]byte("apricot"))) 137 require.Equal(t, val2, s.LookupTimestamp([]byte("banana"))) 138 require.Equal(t, val2, s.LookupTimestamp([]byte("orange"))) 139 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("raspberry"))) 140 141 // Add disjoint open range. 142 // val1: [a--------------o] (p--------------t) 143 // val2: [a--------------o] 144 s.AddRange([]byte("pear"), []byte("tomato"), excludeFrom|excludeTo, val1) 145 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("peach"))) 146 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("pear"))) 147 require.Equal(t, val1, s.LookupTimestamp([]byte("raspberry"))) 148 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("tomato"))) 149 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("watermelon"))) 150 151 // Try again and make sure it's a no-op. 152 // val1: [a--------------o] (p--------------t) 153 // val2: [a--------------o] 154 s.AddRange([]byte("pear"), []byte("tomato"), excludeFrom|excludeTo, val1) 155 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("peach"))) 156 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("pear"))) 157 require.Equal(t, val1, s.LookupTimestamp([]byte("raspberry"))) 158 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("tomato"))) 159 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("watermelon"))) 160 161 // Ratchet up the timestamps. 162 // val1: [a--------------o] (p--------------t) 163 // val2: [a--------------o] (p--------------t) 164 s.AddRange([]byte("pear"), []byte("tomato"), excludeFrom|excludeTo, val2) 165 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("peach"))) 166 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("pear"))) 167 require.Equal(t, val2, s.LookupTimestamp([]byte("raspberry"))) 168 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("tomato"))) 169 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("watermelon"))) 170 171 // Add disjoint left-open range. 172 // val1: [a--------------o] (p--------------t) 173 // val2: [a--------------o] (p--------------t) 174 // val3: (t--------------w] 175 s.AddRange([]byte("tomato"), []byte("watermelon"), excludeFrom, val3) 176 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("peach"))) 177 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("pear"))) 178 require.Equal(t, val2, s.LookupTimestamp([]byte("raspberry"))) 179 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("tomato"))) 180 require.Equal(t, val3, s.LookupTimestamp([]byte("watermelon"))) 181 182 // Add disjoint right-open range. 183 // val1: [a--------------o] (p--------------t) 184 // val2: [a--------------o] (p--------------t) 185 // val3: (t--------------w] 186 // val4: [p--p) 187 s.AddRange([]byte("peach"), []byte("pear"), excludeTo, val4) 188 require.Equal(t, val4, s.LookupTimestamp([]byte("peach"))) 189 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("pear"))) 190 require.Equal(t, val2, s.LookupTimestamp([]byte("raspberry"))) 191 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("tomato"))) 192 require.Equal(t, val3, s.LookupTimestamp([]byte("watermelon"))) 193 } 194 195 func TestIntervalSklKeyBoundaries(t *testing.T) { 196 val1 := makeVal(makeTS(200, 200), "1") 197 val2 := makeVal(makeTS(200, 201), "2") 198 val3 := makeVal(makeTS(300, 0), "3") 199 val4 := makeVal(makeTS(400, 0), "4") 200 val5 := makeVal(makeTS(500, 0), "5") 201 202 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 203 s.floorTS = floorTS 204 205 // Can't insert a key at infinity. 206 require.Panics(t, func() { s.Add([]byte(nil), val1) }) 207 require.Panics(t, func() { s.AddRange([]byte(nil), []byte(nil), 0, val1) }) 208 require.Panics(t, func() { s.AddRange([]byte(nil), []byte(nil), excludeFrom, val1) }) 209 require.Panics(t, func() { s.AddRange([]byte(nil), []byte(nil), excludeTo, val1) }) 210 require.Panics(t, func() { s.AddRange([]byte(nil), []byte(nil), excludeFrom|excludeTo, val1) }) 211 212 // Can't lookup a key at infinity. 213 require.Panics(t, func() { s.LookupTimestamp([]byte(nil)) }) 214 require.Panics(t, func() { s.LookupTimestampRange([]byte(nil), []byte(nil), 0) }) 215 require.Panics(t, func() { s.LookupTimestampRange([]byte(nil), []byte(nil), excludeFrom) }) 216 require.Panics(t, func() { s.LookupTimestampRange([]byte(nil), []byte(nil), excludeTo) }) 217 require.Panics(t, func() { s.LookupTimestampRange([]byte(nil), []byte(nil), excludeFrom|excludeTo) }) 218 219 // Single timestamp at minimum key. 220 // val1: [""] 221 s.Add([]byte(""), val1) 222 require.Equal(t, val1, s.LookupTimestamp([]byte(""))) 223 require.Equal(t, floorVal, s.LookupTimestamp([]byte("apple"))) 224 225 // Range extending to infinity. excludeTo doesn't make a difference because 226 // the boundary is open. 227 // val1: [""] 228 // val2: [b-----------> 229 s.AddRange([]byte("banana"), nil, excludeTo, val2) 230 require.Equal(t, val1, s.LookupTimestamp([]byte(""))) 231 require.Equal(t, floorVal, s.LookupTimestamp([]byte("apple"))) 232 require.Equal(t, val2, s.LookupTimestamp([]byte("banana"))) 233 require.Equal(t, val2, s.LookupTimestamp([]byte("orange"))) 234 235 // val1: [""] 236 // val2: [b-----------> 237 // val3: [b-----------> 238 s.AddRange([]byte("banana"), nil, 0, val3) 239 require.Equal(t, val1, s.LookupTimestamp([]byte(""))) 240 require.Equal(t, floorVal, s.LookupTimestamp([]byte("apple"))) 241 require.Equal(t, val3, s.LookupTimestamp([]byte("banana"))) 242 require.Equal(t, val3, s.LookupTimestamp([]byte("orange"))) 243 244 // Range starting at the minimum key. excludeFrom makes a difference because 245 // the boundary is closed. 246 // val1: [""] 247 // val2: [b-----------> 248 // val3: [b-----------> 249 // val4: (""----------k] 250 s.AddRange([]byte(""), []byte("kiwi"), excludeFrom, val4) 251 require.Equal(t, val1, s.LookupTimestamp([]byte(""))) 252 require.Equal(t, val4, s.LookupTimestamp([]byte("banana"))) 253 require.Equal(t, val4, s.LookupTimestamp([]byte("kiwi"))) 254 require.Equal(t, val3, s.LookupTimestamp([]byte("orange"))) 255 256 // val1: [""] 257 // val2: [b-----------> 258 // val3: [b-----------> 259 // val4: (""----------k] 260 // val5: [""----------k] 261 s.AddRange([]byte(""), []byte("kiwi"), 0, val5) 262 require.Equal(t, val5, s.LookupTimestamp([]byte(""))) 263 require.Equal(t, val5, s.LookupTimestamp([]byte("banana"))) 264 require.Equal(t, val5, s.LookupTimestamp([]byte("kiwi"))) 265 require.Equal(t, val3, s.LookupTimestamp([]byte("orange"))) 266 } 267 268 func TestIntervalSklSupersetRange(t *testing.T) { 269 val1 := makeVal(makeTS(200, 1), "1") 270 val2 := makeVal(makeTS(201, 0), "2") 271 val3 := makeVal(makeTS(300, 0), "3") 272 val4 := makeVal(makeTS(400, 0), "4") 273 val5 := makeVal(makeTS(500, 0), "5") 274 val6 := makeVal(makeTS(600, 0), "6") 275 276 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 277 s.floorTS = floorTS 278 279 // Same range. 280 // val1: [k---------o] 281 // val2: [k---------o] 282 s.AddRange([]byte("kiwi"), []byte("orange"), 0, val1) 283 s.AddRange([]byte("kiwi"), []byte("orange"), 0, val2) 284 require.Equal(t, floorVal, s.LookupTimestamp([]byte("apple"))) 285 require.Equal(t, val2, s.LookupTimestamp([]byte("kiwi"))) 286 require.Equal(t, val2, s.LookupTimestamp([]byte("mango"))) 287 require.Equal(t, val2, s.LookupTimestamp([]byte("orange"))) 288 require.Equal(t, floorVal, s.LookupTimestamp([]byte("raspberry"))) 289 290 // Superset range, but with lower timestamp. 291 // val1: [g--------------p] 292 // val2: [k---------o] 293 s.AddRange([]byte("grape"), []byte("pear"), 0, val1) 294 require.Equal(t, floorVal, s.LookupTimestamp([]byte("apple"))) 295 require.Equal(t, val1, s.LookupTimestamp([]byte("grape"))) 296 require.Equal(t, val2, s.LookupTimestamp([]byte("kiwi"))) 297 require.Equal(t, val2, s.LookupTimestamp([]byte("orange"))) 298 require.Equal(t, val1, s.LookupTimestamp([]byte("pear"))) 299 require.Equal(t, floorVal, s.LookupTimestamp([]byte("watermelon"))) 300 301 // Superset range, but with higher timestamp. 302 // val1: [g--------------p] 303 // val2: [k---------o] 304 // val3: [b-------------------r] 305 s.AddRange([]byte("banana"), []byte("raspberry"), 0, val3) 306 require.Equal(t, floorVal, s.LookupTimestamp([]byte("apple"))) 307 require.Equal(t, val3, s.LookupTimestamp([]byte("banana"))) 308 require.Equal(t, val3, s.LookupTimestamp([]byte("grape"))) 309 require.Equal(t, val3, s.LookupTimestamp([]byte("kiwi"))) 310 require.Equal(t, val3, s.LookupTimestamp([]byte("orange"))) 311 require.Equal(t, val3, s.LookupTimestamp([]byte("pear"))) 312 require.Equal(t, val3, s.LookupTimestamp([]byte("raspberry"))) 313 require.Equal(t, floorVal, s.LookupTimestamp([]byte("watermelon"))) 314 315 // Equal range but left-open. 316 // val1: [g--------------p] 317 // val2: [k---------o] 318 // val3: [b-------------------r] 319 // val4: (b-------------------r] 320 s.AddRange([]byte("banana"), []byte("raspberry"), excludeFrom, val4) 321 require.Equal(t, floorVal, s.LookupTimestamp([]byte("apple"))) 322 require.Equal(t, val3, s.LookupTimestamp([]byte("banana"))) 323 require.Equal(t, val4, s.LookupTimestamp([]byte("grape"))) 324 require.Equal(t, val4, s.LookupTimestamp([]byte("kiwi"))) 325 require.Equal(t, val4, s.LookupTimestamp([]byte("orange"))) 326 require.Equal(t, val4, s.LookupTimestamp([]byte("pear"))) 327 require.Equal(t, val4, s.LookupTimestamp([]byte("raspberry"))) 328 require.Equal(t, floorVal, s.LookupTimestamp([]byte("watermelon"))) 329 330 // Equal range but right-open. 331 // val1: [g--------------p] 332 // val2: [k---------o] 333 // val3: [b-------------------r] 334 // val4: (b-------------------r] 335 // val5: [b-------------------r) 336 s.AddRange([]byte("banana"), []byte("raspberry"), excludeTo, val5) 337 require.Equal(t, floorVal, s.LookupTimestamp([]byte("apple"))) 338 require.Equal(t, val5, s.LookupTimestamp([]byte("banana"))) 339 require.Equal(t, val5, s.LookupTimestamp([]byte("grape"))) 340 require.Equal(t, val5, s.LookupTimestamp([]byte("kiwi"))) 341 require.Equal(t, val5, s.LookupTimestamp([]byte("orange"))) 342 require.Equal(t, val5, s.LookupTimestamp([]byte("pear"))) 343 require.Equal(t, val4, s.LookupTimestamp([]byte("raspberry"))) 344 require.Equal(t, floorVal, s.LookupTimestamp([]byte("watermelon"))) 345 346 // Equal range but fully-open. 347 // val1: [g--------------p] 348 // val2: [k---------o] 349 // val3: [b-------------------r] 350 // val4: (b-------------------r] 351 // val5: [b-------------------r) 352 // val6: (b-------------------r) 353 s.AddRange([]byte("banana"), []byte("raspberry"), (excludeFrom | excludeTo), val6) 354 require.Equal(t, floorVal, s.LookupTimestamp([]byte("apple"))) 355 require.Equal(t, val5, s.LookupTimestamp([]byte("banana"))) 356 require.Equal(t, val6, s.LookupTimestamp([]byte("grape"))) 357 require.Equal(t, val6, s.LookupTimestamp([]byte("kiwi"))) 358 require.Equal(t, val6, s.LookupTimestamp([]byte("orange"))) 359 require.Equal(t, val6, s.LookupTimestamp([]byte("pear"))) 360 require.Equal(t, val4, s.LookupTimestamp([]byte("raspberry"))) 361 require.Equal(t, floorVal, s.LookupTimestamp([]byte("watermelon"))) 362 } 363 364 func TestIntervalSklContiguousRanges(t *testing.T) { 365 ts1 := makeTS(201, 0) 366 367 val1 := makeVal(ts1, "1") 368 val2 := makeVal(ts1, "2") 369 val2WithoutID := makeValWithoutID(ts1) 370 371 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 372 s.floorTS = floorTS 373 374 // val1: [b---------k) 375 // val2: [k---------o) 376 s.AddRange([]byte("banana"), []byte("kiwi"), excludeTo, val1) 377 s.AddRange([]byte("kiwi"), []byte("orange"), excludeTo, val2) 378 379 // Test single-key lookups over the contiguous range. 380 require.Equal(t, floorVal, s.LookupTimestamp([]byte("apple"))) 381 require.Equal(t, val1, s.LookupTimestamp([]byte("banana"))) 382 require.Equal(t, val2, s.LookupTimestamp([]byte("kiwi"))) 383 require.Equal(t, val2, s.LookupTimestamp([]byte("mango"))) 384 require.Equal(t, floorVal, s.LookupTimestamp([]byte("orange"))) 385 386 // Test range lookups over the contiguous range. 387 require.Equal(t, floorVal, s.LookupTimestampRange([]byte(""), []byte("banana"), excludeTo)) 388 require.Equal(t, val1, s.LookupTimestampRange([]byte(""), []byte("kiwi"), excludeTo)) 389 require.Equal(t, val2WithoutID, s.LookupTimestampRange([]byte(""), []byte("orange"), excludeTo)) 390 require.Equal(t, val2WithoutID, s.LookupTimestampRange([]byte(""), []byte(nil), excludeTo)) 391 require.Equal(t, val1, s.LookupTimestampRange([]byte("banana"), []byte("kiwi"), excludeTo)) 392 require.Equal(t, val2, s.LookupTimestampRange([]byte("kiwi"), []byte("orange"), excludeTo)) 393 require.Equal(t, val2, s.LookupTimestampRange([]byte("kiwi"), []byte(nil), excludeTo)) 394 require.Equal(t, val2WithoutID, s.LookupTimestampRange([]byte("banana"), []byte("orange"), excludeTo)) 395 require.Equal(t, val2WithoutID, s.LookupTimestampRange([]byte("banana"), []byte(nil), excludeTo)) 396 require.Equal(t, floorVal, s.LookupTimestampRange([]byte("orange"), []byte(nil), excludeTo)) 397 } 398 399 func TestIntervalSklOverlappingRanges(t *testing.T) { 400 val1 := makeVal(makeTS(200, 1), "1") 401 val2 := makeVal(makeTS(201, 0), "2") 402 val3 := makeVal(makeTS(300, 0), "3") 403 val4 := makeVal(makeTS(400, 0), "4") 404 405 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 406 s.floorTS = floorTS 407 408 // val1: [b---------k] 409 // val2: [g------------r) 410 s.AddRange([]byte("banana"), []byte("kiwi"), 0, val1) 411 s.AddRange([]byte("grape"), []byte("raspberry"), excludeTo, val2) 412 require.Equal(t, floorVal, s.LookupTimestamp([]byte("apple"))) 413 require.Equal(t, val1, s.LookupTimestamp([]byte("banana"))) 414 require.Equal(t, val2, s.LookupTimestamp([]byte("grape"))) 415 require.Equal(t, val2, s.LookupTimestamp([]byte("kiwi"))) 416 require.Equal(t, floorVal, s.LookupTimestamp([]byte("raspberry"))) 417 418 // val1: [b---------k] 419 // val2: [g------------r) 420 // val3: [a---------------o] 421 s.AddRange([]byte("apricot"), []byte("orange"), 0, val3) 422 require.Equal(t, floorVal, s.LookupTimestamp([]byte("apple"))) 423 require.Equal(t, val3, s.LookupTimestamp([]byte("apricot"))) 424 require.Equal(t, val3, s.LookupTimestamp([]byte("banana"))) 425 require.Equal(t, val3, s.LookupTimestamp([]byte("grape"))) 426 require.Equal(t, val3, s.LookupTimestamp([]byte("kiwi"))) 427 require.Equal(t, val3, s.LookupTimestamp([]byte("orange"))) 428 require.Equal(t, val2, s.LookupTimestamp([]byte("pear"))) 429 require.Equal(t, floorVal, s.LookupTimestamp([]byte("raspberry"))) 430 431 // val1: [b---------k] 432 // val2: [g------------r) 433 // val3: [a---------------o] 434 // val4: (k-------------> 435 s.AddRange([]byte("kiwi"), []byte(nil), excludeFrom, val4) 436 require.Equal(t, floorVal, s.LookupTimestamp([]byte("apple"))) 437 require.Equal(t, val3, s.LookupTimestamp([]byte("apricot"))) 438 require.Equal(t, val3, s.LookupTimestamp([]byte("banana"))) 439 require.Equal(t, val3, s.LookupTimestamp([]byte("grape"))) 440 require.Equal(t, val3, s.LookupTimestamp([]byte("kiwi"))) 441 require.Equal(t, val4, s.LookupTimestamp([]byte("orange"))) 442 require.Equal(t, val4, s.LookupTimestamp([]byte("pear"))) 443 require.Equal(t, val4, s.LookupTimestamp([]byte("raspberry"))) 444 } 445 446 func TestIntervalSklSingleKeyRanges(t *testing.T) { 447 val1 := makeVal(makeTS(100, 100), "1") 448 449 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 450 451 // Don't allow inverted ranges. 452 require.Panics(t, func() { s.AddRange([]byte("kiwi"), []byte("apple"), 0, val1) }) 453 require.Equal(t, int64(0), s.frontPage().maxWallTime) 454 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("apple"))) 455 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("banana"))) 456 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("kiwi"))) 457 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("raspberry"))) 458 459 // If from key is same as to key and both are excluded, then range is 460 // zero-length. 461 s.AddRange([]byte("banana"), []byte("banana"), excludeFrom|excludeTo, val1) 462 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("apple"))) 463 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("banana"))) 464 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("kiwi"))) 465 466 // If from key is same as to key and at least one endpoint is included, then 467 // range has length one. 468 s.AddRange([]byte("mango"), []byte("mango"), 0, val1) 469 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("kiwi"))) 470 require.Equal(t, val1, s.LookupTimestamp([]byte("mango"))) 471 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("banana"))) 472 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("cherry"))) 473 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("orange"))) 474 475 s.AddRange([]byte("banana"), []byte("banana"), excludeFrom, val1) 476 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("kiwi"))) 477 require.Equal(t, val1, s.LookupTimestamp([]byte("mango"))) 478 require.Equal(t, val1, s.LookupTimestamp([]byte("banana"))) 479 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("cherry"))) 480 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("orange"))) 481 482 s.AddRange([]byte("cherry"), []byte("cherry"), excludeTo, val1) 483 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("kiwi"))) 484 require.Equal(t, val1, s.LookupTimestamp([]byte("mango"))) 485 require.Equal(t, val1, s.LookupTimestamp([]byte("banana"))) 486 require.Equal(t, val1, s.LookupTimestamp([]byte("cherry"))) 487 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("orange"))) 488 } 489 490 func TestIntervalSklRatchetTxnIDs(t *testing.T) { 491 ts1 := makeTS(100, 100) 492 ts2 := makeTS(250, 50) 493 ts3 := makeTS(350, 50) 494 495 val1 := makeVal(ts1, "1") 496 val2 := makeVal(ts1, "2") 497 val2WithoutID := makeValWithoutID(ts1) 498 val3 := makeVal(ts2, "2") // same txn ID as tsVal2 499 val4 := makeVal(ts2, "3") 500 val4WithoutID := makeValWithoutID(ts2) 501 val5 := makeVal(ts3, "4") 502 val6 := makeVal(ts3, "5") 503 val6WithoutID := makeValWithoutID(ts3) 504 505 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 506 507 s.AddRange([]byte("apricot"), []byte("raspberry"), 0, val1) 508 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("apple"))) 509 require.Equal(t, val1, s.LookupTimestamp([]byte("apricot"))) 510 require.Equal(t, val1, s.LookupTimestamp([]byte("banana"))) 511 require.Equal(t, val1, s.LookupTimestamp([]byte("raspberry"))) 512 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("tomato"))) 513 514 // Ratchet up the txnID with the same timestamp; txnID should be removed. 515 s.AddRange([]byte("apricot"), []byte("tomato"), 0, val2) 516 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("apple"))) 517 require.Equal(t, val2WithoutID, s.LookupTimestamp([]byte("apricot"))) 518 require.Equal(t, val2WithoutID, s.LookupTimestamp([]byte("banana"))) 519 require.Equal(t, val2WithoutID, s.LookupTimestamp([]byte("raspberry"))) 520 require.Equal(t, val2, s.LookupTimestamp([]byte("tomato"))) 521 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("watermelon"))) 522 523 // Ratchet up the timestamp with the same txnID. 524 s.AddRange([]byte("apricot"), []byte("orange"), 0, val3) 525 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("apple"))) 526 require.Equal(t, val3, s.LookupTimestamp([]byte("apricot"))) 527 require.Equal(t, val3, s.LookupTimestamp([]byte("banana"))) 528 require.Equal(t, val3, s.LookupTimestamp([]byte("orange"))) 529 require.Equal(t, val2WithoutID, s.LookupTimestamp([]byte("raspberry"))) 530 require.Equal(t, val2, s.LookupTimestamp([]byte("tomato"))) 531 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("watermelon"))) 532 533 // Ratchet up the txnID with the same timestamp; txnID should be removed. 534 s.AddRange([]byte("apricot"), []byte("banana"), 0, val4) 535 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("apple"))) 536 require.Equal(t, val4WithoutID, s.LookupTimestamp([]byte("apricot"))) 537 require.Equal(t, val4WithoutID, s.LookupTimestamp([]byte("banana"))) 538 require.Equal(t, val3, s.LookupTimestamp([]byte("orange"))) 539 require.Equal(t, val2WithoutID, s.LookupTimestamp([]byte("raspberry"))) 540 require.Equal(t, val2, s.LookupTimestamp([]byte("tomato"))) 541 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("watermelon"))) 542 543 // Ratchet up the timestamp with a new txnID using excludeTo. 544 s.AddRange([]byte("apricot"), []byte("orange"), excludeTo, val5) 545 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("apple"))) 546 require.Equal(t, val5, s.LookupTimestamp([]byte("apricot"))) 547 require.Equal(t, val5, s.LookupTimestamp([]byte("banana"))) 548 require.Equal(t, val3, s.LookupTimestamp([]byte("orange"))) 549 require.Equal(t, val2WithoutID, s.LookupTimestamp([]byte("raspberry"))) 550 551 // Ratchet up the txnID with the same timestamp using excludeTo; txnID should be removed. 552 s.AddRange([]byte("apricot"), []byte("banana"), excludeTo, val6) 553 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("apple"))) 554 require.Equal(t, val6WithoutID, s.LookupTimestamp([]byte("apricot"))) 555 require.Equal(t, val5, s.LookupTimestamp([]byte("banana"))) 556 require.Equal(t, val3, s.LookupTimestamp([]byte("orange"))) 557 require.Equal(t, val2WithoutID, s.LookupTimestamp([]byte("raspberry"))) 558 559 // Ratchet up the txnID with the same timestamp using excludeFrom; txnID should be removed. 560 s.AddRange([]byte("banana"), []byte(nil), excludeFrom, val6) 561 require.Equal(t, emptyVal, s.LookupTimestamp([]byte("apple"))) 562 require.Equal(t, val6WithoutID, s.LookupTimestamp([]byte("apricot"))) 563 require.Equal(t, val5, s.LookupTimestamp([]byte("banana"))) 564 require.Equal(t, val6, s.LookupTimestamp([]byte("orange"))) 565 require.Equal(t, val6, s.LookupTimestamp([]byte("raspberry"))) 566 } 567 568 func TestIntervalSklLookupRange(t *testing.T) { 569 ts1 := makeTS(100, 100) 570 ts2 := makeTS(200, 201) 571 ts3 := makeTS(300, 201) 572 ts4 := makeTS(400, 201) 573 574 val1 := makeVal(ts1, "1") 575 val2 := makeVal(ts2, "2") 576 val3 := makeVal(ts2, "3") 577 val3WithoutID := makeValWithoutID(ts2) 578 val4 := makeVal(ts3, "4") 579 val5 := makeVal(ts4, "5") 580 581 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 582 583 // Perform range lookups over a single key. 584 s.Add([]byte("apricot"), val1) 585 // from = "" and to = nil means that we're scanning over all keys. 586 require.Equal(t, val1, s.LookupTimestampRange([]byte(""), []byte(nil), 0)) 587 require.Equal(t, val1, s.LookupTimestampRange([]byte(""), []byte(nil), excludeFrom)) 588 require.Equal(t, val1, s.LookupTimestampRange([]byte(""), []byte(nil), excludeTo)) 589 require.Equal(t, val1, s.LookupTimestampRange([]byte(""), []byte(nil), (excludeFrom|excludeTo))) 590 591 require.Equal(t, emptyVal, s.LookupTimestampRange([]byte("apple"), []byte("apple"), 0)) 592 require.Equal(t, val1, s.LookupTimestampRange([]byte("apple"), []byte("apricot"), 0)) 593 require.Equal(t, val1, s.LookupTimestampRange([]byte("apple"), []byte("apricot"), excludeFrom)) 594 require.Equal(t, emptyVal, s.LookupTimestampRange([]byte("apple"), []byte("apricot"), excludeTo)) 595 596 require.Equal(t, val1, s.LookupTimestampRange([]byte("apricot"), []byte("apricot"), 0)) 597 require.Equal(t, val1, s.LookupTimestampRange([]byte("apricot"), []byte("apricot"), excludeFrom)) 598 require.Equal(t, val1, s.LookupTimestampRange([]byte("apricot"), []byte("apricot"), excludeTo)) 599 require.Equal(t, emptyVal, s.LookupTimestampRange([]byte("apricot"), []byte("apricot"), (excludeFrom|excludeTo))) 600 601 // Perform range lookups over a series of keys. 602 s.Add([]byte("banana"), val2) 603 s.Add([]byte("cherry"), val3) 604 require.Equal(t, val2, s.LookupTimestampRange([]byte("apricot"), []byte("banana"), 0)) 605 require.Equal(t, val2, s.LookupTimestampRange([]byte("apricot"), []byte("banana"), excludeFrom)) 606 require.Equal(t, val1, s.LookupTimestampRange([]byte("apricot"), []byte("banana"), excludeTo)) 607 require.Equal(t, emptyVal, s.LookupTimestampRange([]byte("apricot"), []byte("banana"), (excludeFrom|excludeTo))) 608 609 require.Equal(t, val3WithoutID, s.LookupTimestampRange([]byte("apricot"), []byte("cherry"), 0)) 610 require.Equal(t, val3WithoutID, s.LookupTimestampRange([]byte("apricot"), []byte("cherry"), excludeFrom)) 611 require.Equal(t, val2, s.LookupTimestampRange([]byte("apricot"), []byte("cherry"), excludeTo)) 612 require.Equal(t, val2, s.LookupTimestampRange([]byte("apricot"), []byte("cherry"), (excludeFrom|excludeTo))) 613 614 require.Equal(t, val3WithoutID, s.LookupTimestampRange([]byte("banana"), []byte("cherry"), 0)) 615 require.Equal(t, val3, s.LookupTimestampRange([]byte("banana"), []byte("cherry"), excludeFrom)) 616 require.Equal(t, val2, s.LookupTimestampRange([]byte("banana"), []byte("cherry"), excludeTo)) 617 require.Equal(t, emptyVal, s.LookupTimestampRange([]byte("banana"), []byte("cherry"), (excludeFrom|excludeTo))) 618 619 // Open ranges should scan until the last key. 620 require.Equal(t, val3WithoutID, s.LookupTimestampRange([]byte("apricot"), []byte(nil), 0)) 621 require.Equal(t, val3WithoutID, s.LookupTimestampRange([]byte("banana"), []byte(nil), 0)) 622 require.Equal(t, val3, s.LookupTimestampRange([]byte("cherry"), []byte(nil), 0)) 623 require.Equal(t, emptyVal, s.LookupTimestampRange([]byte("tomato"), []byte(nil), 0)) 624 625 // Subset lookup range. 626 s.AddRange([]byte("apple"), []byte("cherry"), excludeTo, val4) 627 require.Equal(t, val4, s.LookupTimestampRange([]byte("apple"), []byte("berry"), 0)) 628 require.Equal(t, val4, s.LookupTimestampRange([]byte("apple"), []byte("berry"), excludeFrom)) 629 require.Equal(t, val4, s.LookupTimestampRange([]byte("berry"), []byte("blueberry"), 0)) 630 require.Equal(t, val4, s.LookupTimestampRange([]byte("berry"), []byte("cherry"), 0)) 631 require.Equal(t, val4, s.LookupTimestampRange([]byte("berry"), []byte("cherry"), excludeTo)) 632 633 // Overlapping range without endpoints. 634 s.AddRange([]byte("banana"), []byte("cherry"), (excludeFrom | excludeTo), val5) 635 require.Equal(t, val4, s.LookupTimestampRange([]byte("apple"), []byte("banana"), (excludeFrom|excludeTo))) 636 require.Equal(t, val5, s.LookupTimestampRange([]byte("banana"), []byte("cherry"), (excludeFrom|excludeTo))) 637 require.Equal(t, val5, s.LookupTimestampRange([]byte("apple"), []byte("cherry"), (excludeFrom|excludeTo))) 638 } 639 640 func TestIntervalSklLookupRangeSingleKeyRanges(t *testing.T) { 641 ts1 := makeTS(100, 100) 642 ts2 := makeTS(200, 201) 643 644 val1 := makeVal(ts1, "1") 645 val2 := makeVal(ts2, "2") 646 val3 := makeVal(ts2, "3") 647 val3WithoutID := makeValWithoutID(ts2) 648 649 key1 := []byte("a") 650 key2 := append(key1, 0x0) 651 key3 := append(key2, 0x0) 652 key4 := append(key3, 0x0) 653 654 // Perform range lookups over [key, key.Next()) ranges. 655 t.Run("[key, key.Next())", func(t *testing.T) { 656 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 657 658 s.AddRange(key1, key2, excludeTo, val1) 659 s.AddRange(key2, key3, excludeTo, val2) 660 s.AddRange(key3, key4, excludeTo, val3) 661 662 require.Equal(t, val1, s.LookupTimestampRange([]byte(""), key1, 0)) 663 require.Equal(t, emptyVal, s.LookupTimestampRange([]byte(""), key1, excludeTo)) 664 require.Equal(t, val2, s.LookupTimestampRange([]byte(""), key2, 0)) 665 require.Equal(t, val1, s.LookupTimestampRange([]byte(""), key2, excludeTo)) 666 667 require.Equal(t, val2, s.LookupTimestampRange(key1, key2, 0)) 668 require.Equal(t, val2, s.LookupTimestampRange(key1, key2, excludeFrom)) 669 require.Equal(t, val1, s.LookupTimestampRange(key1, key2, excludeTo)) 670 // This may be surprising. We actually return the gapVal of the first range 671 // even though there isn't a discrete byte value between key1 and key2 (this 672 // is a feature, not a bug!). It demonstrates the difference between the 673 // first two options (which behave exactly the same) and the third: 674 // a) Add(key, val) 675 // b) AddRange(key, key, 0, val) 676 // c) AddRange(key, key.Next(), excludeTo, val) 677 // 678 // NB: If the behavior is not needed, it's better to use one of the 679 // first two options because they allow us to avoid storing a gap value. 680 require.Equal(t, val1, s.LookupTimestampRange(key1, key2, (excludeFrom|excludeTo))) 681 682 // val1 and val2 both have the same timestamp but have different IDs, so 683 // the cacheValue ratcheting policy enforces that the result of scanning 684 // over both should be a value with their timestamp but with no txnID. 685 require.Equal(t, val3WithoutID, s.LookupTimestampRange(key1, key3, 0)) 686 require.Equal(t, val3WithoutID, s.LookupTimestampRange(key1, key3, excludeFrom)) 687 require.Equal(t, val2, s.LookupTimestampRange(key1, key3, excludeTo)) 688 require.Equal(t, val2, s.LookupTimestampRange(key1, key3, (excludeFrom|excludeTo))) 689 690 require.Equal(t, val3WithoutID, s.LookupTimestampRange(key2, key3, 0)) 691 // Again, this may be surprising. The logic is the same as above. 692 require.Equal(t, val3WithoutID, s.LookupTimestampRange(key2, key3, excludeFrom)) 693 require.Equal(t, val2, s.LookupTimestampRange(key2, key3, excludeTo)) 694 require.Equal(t, val2, s.LookupTimestampRange(key2, key3, (excludeFrom|excludeTo))) 695 696 require.Equal(t, val3, s.LookupTimestampRange(key3, []byte(nil), 0)) 697 require.Equal(t, val3, s.LookupTimestampRange(key3, []byte(nil), excludeFrom)) 698 }) 699 700 // Perform the same lookups, but this time use single key ranges. 701 t.Run("[key, key]", func(t *testing.T) { 702 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 703 704 s.AddRange(key1, key1, 0, val1) // same as Add(key1, val1) 705 s.AddRange(key2, key2, 0, val2) // ... Add(key2, val2) 706 s.AddRange(key3, key3, 0, val3) // ... Add(key3, val3) 707 708 require.Equal(t, val1, s.LookupTimestampRange([]byte(""), key1, 0)) 709 require.Equal(t, emptyVal, s.LookupTimestampRange([]byte(""), key1, excludeTo)) 710 require.Equal(t, val2, s.LookupTimestampRange([]byte(""), key2, 0)) 711 require.Equal(t, val1, s.LookupTimestampRange([]byte(""), key2, excludeTo)) 712 713 require.Equal(t, val2, s.LookupTimestampRange(key1, key2, 0)) 714 require.Equal(t, val2, s.LookupTimestampRange(key1, key2, excludeFrom)) 715 require.Equal(t, val1, s.LookupTimestampRange(key1, key2, excludeTo)) 716 // DIFFERENT! 717 require.Equal(t, emptyVal, s.LookupTimestampRange(key1, key2, (excludeFrom|excludeTo))) 718 719 require.Equal(t, val3WithoutID, s.LookupTimestampRange(key1, key3, 0)) 720 require.Equal(t, val3WithoutID, s.LookupTimestampRange(key1, key3, excludeFrom)) 721 require.Equal(t, val2, s.LookupTimestampRange(key1, key3, excludeTo)) 722 require.Equal(t, val2, s.LookupTimestampRange(key1, key3, (excludeFrom|excludeTo))) 723 724 require.Equal(t, val3WithoutID, s.LookupTimestampRange(key2, key3, 0)) 725 // DIFFERENT! 726 require.Equal(t, val3, s.LookupTimestampRange(key2, key3, excludeFrom)) 727 require.Equal(t, val2, s.LookupTimestampRange(key2, key3, excludeTo)) 728 // DIFFERENT! 729 require.Equal(t, emptyVal, s.LookupTimestampRange(key2, key3, (excludeFrom|excludeTo))) 730 731 require.Equal(t, val3, s.LookupTimestampRange(key3, []byte(nil), 0)) 732 // DIFFERENT! 733 require.Equal(t, emptyVal, s.LookupTimestampRange(key3, []byte(nil), excludeFrom)) 734 }) 735 } 736 737 // TestIntervalSklLookupEqualsEarlierMaxWallTime tests that we properly handle 738 // the lookup when the timestamp for a range found in the later page is equal to 739 // the maxWallTime of the earlier page. 740 func TestIntervalSklLookupEqualsEarlierMaxWallTime(t *testing.T) { 741 ts1 := makeTS(200, 0) // without Logical part 742 ts2 := makeTS(200, 1) // with Logical part 743 ts2Ceil := makeTS(201, 0) 744 745 txnID1 := "1" 746 txnID2 := "2" 747 748 testutils.RunTrueAndFalse(t, "tsWithLogicalPart", func(t *testing.T, logicalPart bool) { 749 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 750 s.floorTS = floorTS 751 752 // Insert an initial value into intervalSkl. 753 initTS := ts1 754 if logicalPart { 755 initTS = ts2 756 } 757 origVal := makeVal(initTS, txnID1) 758 s.AddRange([]byte("banana"), []byte("orange"), 0, origVal) 759 760 // Verify the later page's maxWallTime is what we expect. 761 expMaxTS := ts1 762 if logicalPart { 763 expMaxTS = ts2Ceil 764 } 765 require.Equal(t, expMaxTS.WallTime, s.frontPage().maxWallTime) 766 767 // Rotate the page so that new writes will go to a different page. 768 s.rotatePages(s.frontPage()) 769 770 // Write to overlapping and non-overlapping parts of the new page with 771 // the values that have the same timestamp as the maxWallTime of the 772 // earlier page. One value has the same txnID as the previous write in 773 // the earlier page and one has a different txnID. 774 valSameID := makeVal(expMaxTS, txnID1) 775 valDiffID := makeVal(expMaxTS, txnID2) 776 valNoID := makeValWithoutID(expMaxTS) 777 s.Add([]byte("apricot"), valSameID) 778 s.Add([]byte("banana"), valSameID) 779 s.Add([]byte("orange"), valDiffID) 780 s.Add([]byte("raspberry"), valDiffID) 781 782 require.Equal(t, valSameID, s.LookupTimestamp([]byte("apricot"))) 783 require.Equal(t, valSameID, s.LookupTimestamp([]byte("banana"))) 784 if logicalPart { 785 // If the initial timestamp had a logical part then 786 // s.earlier.maxWallTime is inexact (see ratchetMaxTimestamp). When 787 // we search in the earlier page, we'll find the exact timestamp of 788 // the overlapping range and realize that its not the same as the 789 // timestamp of the range in the later page. Because of this, 790 // ratchetValue WON'T remove the txnID. 791 require.Equal(t, valDiffID, s.LookupTimestamp([]byte("orange"))) 792 } else { 793 // If the initial timestamp did not have a logical part then 794 // s.earlier.maxWallTime is exact. When we search in the earlier 795 // page, we'll find the overlapping range and realize that it is the 796 // same as the timestamp of the range in the later page. Because of 797 // this, ratchetValue WILL remove the txnID. 798 require.Equal(t, valNoID, s.LookupTimestamp([]byte("orange"))) 799 } 800 require.Equal(t, valDiffID, s.LookupTimestamp([]byte("raspberry"))) 801 require.Equal(t, floorVal, s.LookupTimestamp([]byte("tomato"))) 802 }) 803 } 804 805 func TestIntervalSklFill(t *testing.T) { 806 const n = 200 807 const txnID = "123" 808 809 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 810 s.setFixedPageSize(1500) 811 812 for i := 0; i < n; i++ { 813 key := []byte(fmt.Sprintf("%05d", i)) 814 s.AddRange(key, key, 0, makeVal(makeTS(int64(100+i), int32(i)), txnID)) 815 } 816 817 require.True(t, makeTS(100, 0).Less(s.floorTS)) 818 819 // Verify that the last key inserted is still in the intervalSkl and has not 820 // been rotated out. 821 lastKey := []byte(fmt.Sprintf("%05d", n-1)) 822 expVal := makeVal(makeTS(int64(100+n-1), int32(n-1)), txnID) 823 require.Equal(t, expVal, s.LookupTimestamp(lastKey)) 824 825 // Verify that all keys inserted are either still in the intervalSkl or have 826 // been rotated out and used to ratchet the floorTS. 827 for i := 0; i < n; i++ { 828 key := []byte(fmt.Sprintf("%05d", i)) 829 require.False(t, s.LookupTimestamp(key).ts.Less(s.floorTS)) 830 } 831 } 832 833 // Repeatedly fill the structure and make sure timestamp lookups always increase. 834 func TestIntervalSklFill2(t *testing.T) { 835 const n = 10000 836 const txnID = "123" 837 838 // n >> 1000 so the intervalSkl's pages will be filled. 839 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 840 s.setFixedPageSize(1000) 841 842 key := []byte("some key") 843 for i := 0; i < n; i++ { 844 val := makeVal(makeTS(int64(i), int32(i)), txnID) 845 s.Add(key, val) 846 require.True(t, val.ts.LessEq(s.LookupTimestamp(key).ts)) 847 } 848 } 849 850 // TestIntervalSklMinRetentionWindow tests that if a value is within the minimum 851 // retention window, its page will never be evicted and it will never be subsumed 852 // by the floor timestamp. 853 func TestIntervalSklMinRetentionWindow(t *testing.T) { 854 manual := hlc.NewManualClock(200) 855 clock := hlc.NewClock(manual.UnixNano, time.Nanosecond) 856 857 const minRet = 500 858 s := newIntervalSkl(clock, minRet, makeSklMetrics()) 859 s.setFixedPageSize(1500) 860 s.floorTS = floorTS 861 862 // Add an initial value. Rotate the page so it's alone. 863 origKey := []byte("banana") 864 origVal := makeVal(clock.Now(), "1") 865 s.Add(origKey, origVal) 866 s.rotatePages(s.frontPage()) 867 868 // Add a large number of other values, forcing rotations. Continue until 869 // there are more pages than s.minPages. 870 manual.Increment(300) 871 for i := 0; s.pages.Len() <= s.minPages; i++ { 872 key := []byte(fmt.Sprintf("%05d", i)) 873 s.Add(key, makeVal(clock.Now(), "2")) 874 } 875 876 // We should still be able to look up the initial value. 877 require.Equal(t, origVal, s.LookupTimestamp(origKey)) 878 879 // Even if we rotate the pages, we should still be able to look up the 880 // value. No pages should be evicted. 881 pagesBefore := s.pages.Len() 882 s.rotatePages(s.frontPage()) 883 require.Equal(t, pagesBefore+1, s.pages.Len(), "page should not be evicted") 884 require.Equal(t, origVal, s.LookupTimestamp(origKey)) 885 886 // Increment the clock so that the original value is not in the minimum 887 // retention window. Rotate the pages and the back page should be evicted. 888 manual.Increment(300) 889 s.rotatePages(s.frontPage()) 890 891 newVal := s.LookupTimestamp(origKey) 892 require.NotEqual(t, origVal, newVal, "the original value should be evicted") 893 894 _, update := ratchetValue(origVal, newVal) 895 require.True(t, update, "the original value should have been ratcheted to the new value") 896 897 // Increment the clock again so that all the other values can be evicted. 898 // The pages should collapse back down to s.minPages. 899 manual.Increment(300) 900 s.rotatePages(s.frontPage()) 901 require.Equal(t, s.pages.Len(), s.minPages) 902 } 903 904 func TestIntervalSklConcurrency(t *testing.T) { 905 defer leaktest.AfterTest(t)() 906 defer util.EnableRacePreemptionPoints()() 907 908 testCases := []struct { 909 name string 910 pageSize uint32 911 minPages int 912 }{ 913 // Test concurrency with a small page size in order to force lots of 914 // page rotations. 915 {name: "Rotates", pageSize: 4096}, 916 // Test concurrency with a small page size and a large number of pages 917 // in order to force lots of page growth. 918 {name: "Pages", pageSize: 4096, minPages: 16}, 919 // Test concurrency with a larger page size in order to test slot 920 // concurrency without the added complication of page rotations. 921 {name: "Slots", pageSize: initialSklPageSize}, 922 } 923 for _, tc := range testCases { 924 t.Run(tc.name, func(t *testing.T) { 925 // Run one subtest using a real clock to generate timestamps and one 926 // subtest using a fake clock to generate timestamps. The former is 927 // good for simulating real conditions while the latter is good for 928 // testing timestamp collisions. 929 testutils.RunTrueAndFalse(t, "useClock", func(t *testing.T, useClock bool) { 930 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 931 s := newIntervalSkl(clock, 0 /* minRet */, makeSklMetrics()) 932 s.setFixedPageSize(tc.pageSize) 933 if tc.minPages != 0 { 934 s.setMinPages(tc.minPages) 935 } 936 937 // We run a goroutine for each slot. Goroutines insert new value 938 // over random intervals, but verify that the value in their 939 // slot always ratchets. 940 slots := 4 * runtime.NumCPU() 941 if util.RaceEnabled { 942 // We add in a lot of preemption points when race detection 943 // is enabled, so things will already be very slow. Reduce 944 // the concurrency to that we don't time out. 945 slots /= 2 946 } 947 948 var wg sync.WaitGroup 949 for i := 0; i < slots; i++ { 950 wg.Add(1) 951 952 // Each goroutine gets its own key to watch as it performs 953 // random operations. 954 go func(i int) { 955 defer wg.Done() 956 957 rng := rand.New(rand.NewSource(timeutil.Now().UnixNano())) 958 key := []byte(fmt.Sprintf("%05d", i)) 959 txnID := uuid.MakeV4() 960 maxVal := cacheValue{} 961 962 rounds := 1000 963 if util.RaceEnabled { 964 // Reduce the number of rounds for race builds. 965 rounds /= 2 966 } 967 for j := 0; j < rounds; j++ { 968 // Choose a random range. 969 from, middle, to := randRange(rng, slots) 970 opt := randRangeOpt(rng) 971 972 // Add a new value to the range. 973 ts := hlc.Timestamp{WallTime: int64(j)} 974 if useClock { 975 ts = clock.Now() 976 } 977 nowVal := cacheValue{ts: ts, txnID: txnID} 978 s.AddRange(from, to, opt, nowVal) 979 980 // Test single-key lookup at from, if possible. 981 if (opt & excludeFrom) == 0 { 982 val := s.LookupTimestamp(from) 983 assertRatchet(t, nowVal, val) 984 } 985 986 // Test single-key lookup between from and to, if possible. 987 if middle != nil { 988 val := s.LookupTimestamp(middle) 989 assertRatchet(t, nowVal, val) 990 } 991 992 // Test single-key lookup at to, if possible. 993 if (opt & excludeTo) == 0 { 994 val := s.LookupTimestamp(to) 995 assertRatchet(t, nowVal, val) 996 } 997 998 // Test range lookup between from and to, if possible. 999 if !(bytes.Equal(from, to) && opt == (excludeFrom|excludeTo)) { 1000 val := s.LookupTimestampRange(from, to, opt) 1001 assertRatchet(t, nowVal, val) 1002 } 1003 1004 // Make sure the value at our key did not decrease. 1005 val := s.LookupTimestamp(key) 1006 assertRatchet(t, maxVal, val) 1007 maxVal = val 1008 } 1009 }(i) 1010 } 1011 1012 wg.Wait() 1013 }) 1014 }) 1015 } 1016 } 1017 1018 func TestIntervalSklConcurrentVsSequential(t *testing.T) { 1019 defer leaktest.AfterTest(t)() 1020 defer util.EnableRacePreemptionPoints()() 1021 1022 // Run one subtest using a real clock to generate timestamps and one subtest 1023 // using a fake clock to generate timestamps. The former is good for 1024 // simulating real conditions while the latter is good for testing timestamp 1025 // collisions. 1026 testutils.RunTrueAndFalse(t, "useClock", func(t *testing.T, useClock bool) { 1027 rng := rand.New(rand.NewSource(timeutil.Now().UnixNano())) 1028 clock := hlc.NewClock(hlc.UnixNano, time.Nanosecond) 1029 1030 const smallPageSize = 32 * 1024 // 32 KB 1031 const retainForever = math.MaxInt64 1032 sequentialS := newIntervalSkl(clock, retainForever, makeSklMetrics()) 1033 sequentialS.setFixedPageSize(smallPageSize) 1034 concurrentS := newIntervalSkl(clock, retainForever, makeSklMetrics()) 1035 concurrentS.setFixedPageSize(smallPageSize) 1036 1037 // We run a goroutine for each slot. Goroutines insert new value 1038 // over random intervals, but verify that the value in their 1039 // slot always ratchets. 1040 slots := 4 * runtime.NumCPU() 1041 if util.RaceEnabled { 1042 // We add in a lot of preemption points when race detection 1043 // is enabled, so things will already be very slow. Reduce 1044 // the concurrency to that we don't time out. 1045 slots /= 2 1046 } 1047 1048 txnIDs := make([]uuid.UUID, slots) 1049 for i := range txnIDs { 1050 txnIDs[i] = uuid.MakeV4() 1051 } 1052 1053 rounds := 1000 1054 if util.RaceEnabled { 1055 // Reduce the number of rounds for race builds. 1056 rounds /= 2 1057 } 1058 for j := 0; j < rounds; j++ { 1059 // This is a lot of log output so only un-comment to debug. 1060 // t.Logf("round %d", j) 1061 1062 // Create a set of actions to perform. 1063 type action struct { 1064 from, middle, to []byte 1065 opt rangeOptions 1066 val cacheValue 1067 } 1068 actions := make([]action, slots) 1069 for i := range actions { 1070 var a action 1071 a.from, a.middle, a.to = randRange(rng, slots) 1072 a.opt = randRangeOpt(rng) 1073 1074 ts := hlc.Timestamp{WallTime: int64(j)} 1075 if useClock { 1076 ts = clock.Now() 1077 } 1078 a.val = cacheValue{ts: ts, txnID: txnIDs[i]} 1079 1080 // This is a lot of log output so only un-comment to debug. 1081 // t.Logf("action (%s,%s)[%d] = %s", string(a.from), string(a.to), a.opt, a.val) 1082 actions[i] = a 1083 } 1084 1085 // Perform each action, first in order on the "sequential" 1086 // intervalSkl, then in parallel on the "concurrent" intervalSkl. 1087 // t.Log("sequential actions") 1088 for _, a := range actions { 1089 sequentialS.AddRange(a.from, a.to, a.opt, a.val) 1090 } 1091 1092 // t.Log("concurrent actions") 1093 var wg sync.WaitGroup 1094 for _, a := range actions { 1095 wg.Add(1) 1096 go func(a action) { 1097 concurrentS.AddRange(a.from, a.to, a.opt, a.val) 1098 wg.Done() 1099 }(a) 1100 } 1101 wg.Wait() 1102 1103 // Ask each intervalSkl the same questions. We should get the same 1104 // answers. 1105 for _, a := range actions { 1106 // Test single-key lookup at from, if possible. 1107 if (a.opt & excludeFrom) == 0 { 1108 valS := sequentialS.LookupTimestamp(a.from) 1109 valC := concurrentS.LookupTimestamp(a.from) 1110 require.Equal(t, valS, valC, "key=%s", string(a.from)) 1111 assertRatchet(t, a.val, valS) 1112 } 1113 1114 // Test single-key lookup between from and to, if possible. 1115 if a.middle != nil { 1116 valS := sequentialS.LookupTimestamp(a.middle) 1117 valC := concurrentS.LookupTimestamp(a.middle) 1118 require.Equal(t, valS, valC, "key=%s", string(a.middle)) 1119 assertRatchet(t, a.val, valS) 1120 } 1121 1122 // Test single-key lookup at to, if possible. 1123 if (a.opt & excludeTo) == 0 { 1124 valS := sequentialS.LookupTimestamp(a.to) 1125 valC := concurrentS.LookupTimestamp(a.to) 1126 require.Equal(t, valS, valC, "key=%s", string(a.to)) 1127 assertRatchet(t, a.val, valS) 1128 } 1129 1130 // Test range lookup between from and to, if possible. 1131 if !(bytes.Equal(a.from, a.to) && a.opt == (excludeFrom|excludeTo)) { 1132 valS := sequentialS.LookupTimestampRange(a.from, a.to, a.opt) 1133 valC := concurrentS.LookupTimestampRange(a.from, a.to, a.opt) 1134 require.Equal(t, valS, valC, "range=(%s,%s)[%d]", 1135 string(a.from), string(a.to), a.opt) 1136 assertRatchet(t, a.val, valS) 1137 } 1138 } 1139 } 1140 }) 1141 } 1142 1143 // assertRatchet asserts that it would be possible for the first cacheValue 1144 // (before) to be ratcheted to the second cacheValue (after). 1145 func assertRatchet(t *testing.T, before, after cacheValue) { 1146 // Value ratcheting is an anti-symmetric relation R, so if R(before, after) 1147 // holds with before != after, then R(after, before) must not hold. Another 1148 // way to look at this is that ratcheting is a monotonically increasing 1149 // function, so if after comes later than before and the two are not equal, 1150 // then before could not also come later than after. 1151 // 1152 // If before == after, ratcheting will be a no-op, so the assertion will 1153 // still hold. 1154 _, upgrade := ratchetValue(after, before) 1155 require.False(t, upgrade, "ratchet inversion from %s to %s", before, after) 1156 } 1157 1158 // TestIntervalSklMaxEncodedSize ensures that we do not enter an infinite page 1159 // rotation loop for ranges that are too large to fit in a single page. Instead, 1160 // we detect this scenario early and panic. 1161 func TestIntervalSklMaxEncodedSize(t *testing.T) { 1162 manual := hlc.NewManualClock(200) 1163 clock := hlc.NewClock(manual.UnixNano, time.Nanosecond) 1164 1165 ts := clock.Now() 1166 val := makeVal(ts, "1") 1167 1168 testutils.RunTrueAndFalse(t, "fit", func(t *testing.T, fit bool) { 1169 testutils.RunTrueAndFalse(t, "fixed", func(t *testing.T, fixed bool) { 1170 var key []byte 1171 var encSize int 1172 if fixed { 1173 // Create an arbitrarily sized key. We'll set the pageSize to 1174 // either exactly accommodate this or to be one byte too small. 1175 key = make([]byte, 65) 1176 encSize = encodedRangeSize(key, nil, 0) 1177 } else { 1178 // Create either the largest possible key that will fit in the 1179 // maximumSklPageSize or a key one byte larger than this. This 1180 // test forces the intervalSkl to quickly grow its page size 1181 // until it is large enough to accommodate the key. 1182 encSize = maximumSklPageSize - initialSklAllocSize 1183 encOverhead := encodedRangeSize(nil, nil, 0) 1184 keySize := encSize - encOverhead 1185 if !fit { 1186 keySize++ 1187 } 1188 key = make([]byte, keySize) 1189 if fit { 1190 require.Equal(t, encSize, encodedRangeSize(key, nil, 0)) 1191 } else { 1192 require.Equal(t, encSize+1, encodedRangeSize(key, nil, 0)) 1193 } 1194 } 1195 1196 s := newIntervalSkl(clock, 1, makeSklMetrics()) 1197 if fixed { 1198 fixedSize := uint32(initialSklAllocSize + encSize) 1199 if !fit { 1200 fixedSize-- 1201 } 1202 s.setFixedPageSize(fixedSize) 1203 } 1204 initPageSize := s.pageSize 1205 1206 if fit { 1207 require.NotPanics(t, func() { s.Add(key, val) }) 1208 } else { 1209 require.Panics(t, func() { s.Add(key, val) }) 1210 } 1211 1212 if fit && !fixed { 1213 // Page size should have grown to maximum. 1214 require.Equal(t, uint32(maximumSklPageSize), s.pageSize) 1215 } else { 1216 // Page size should not have grown. 1217 require.Equal(t, initPageSize, s.pageSize) 1218 } 1219 }) 1220 }) 1221 } 1222 1223 // TestArenaReuse tests that arenas are re-used when possible during page 1224 // rotations. Skiplist memory arenas are only re-used when they have the same 1225 // capacity as the new page. 1226 func TestArenaReuse(t *testing.T) { 1227 s := newIntervalSkl(nil /* clock */, 0 /* minRet */, makeSklMetrics()) 1228 1229 // Track the unique arenas that we observe in use. 1230 arenas := make(map[*arenaskl.Arena]struct{}) 1231 const iters = 256 1232 for i := 0; i < iters; i++ { 1233 for e := s.pages.Front(); e != nil; e = e.Next() { 1234 p := e.Value.(*sklPage) 1235 arenas[p.list.Arena()] = struct{}{} 1236 } 1237 s.rotatePages(s.frontPage()) 1238 } 1239 1240 // We expect to see a single arena with each of the allocation sizes between 1241 // initialSklPageSize and maximumSklPageSize. We then expect to see repeated 1242 // pages with the same size once we hit maximumSklPageSize. Only then do we 1243 // expect to see arena re-use. 1244 // 1245 // Example: 1246 // initSize = 4 1247 // maxSize = 32 1248 // minPages = 2 1249 // 1250 // arena sizes: 1251 // 4 (A1) 1252 // 8 (A2) 1253 // 16 (A3) 1254 // 32 (A4) 1255 // 32 (A5) 1256 // 32 (A4) 1257 // 32 (A5) 1258 // ... 1259 // 1260 intermediatePages := int(math.Log2(maximumSklPageSize) - math.Log2(initialSklPageSize)) 1261 expArenas := defaultMinSklPages + intermediatePages 1262 require.Less(t, expArenas, iters) 1263 require.Equal(t, expArenas, len(arenas)) 1264 } 1265 1266 func BenchmarkIntervalSklAdd(b *testing.B) { 1267 const max = 500000000 // max size of range 1268 const txnID = "123" 1269 1270 clock := hlc.NewClock(hlc.UnixNano, time.Millisecond) 1271 s := newIntervalSkl(clock, MinRetentionWindow, makeSklMetrics()) 1272 rng := rand.New(rand.NewSource(timeutil.Now().UnixNano())) 1273 1274 size := 1 1275 for i := 0; i < 9; i++ { 1276 b.Run(fmt.Sprintf("size_%d", size), func(b *testing.B) { 1277 for iter := 0; iter < b.N; iter++ { 1278 rnd := int64(rng.Int31n(max)) 1279 from := []byte(fmt.Sprintf("%020d", rnd)) 1280 to := []byte(fmt.Sprintf("%020d", rnd+int64(size-1))) 1281 s.AddRange(from, to, 0, makeVal(clock.Now(), txnID)) 1282 } 1283 }) 1284 1285 size *= 10 1286 } 1287 } 1288 1289 func BenchmarkIntervalSklAddAndLookup(b *testing.B) { 1290 const parallel = 1 1291 const max = 1000000000 // max size of range 1292 const data = 500000 // number of ranges 1293 const txnID = "123" 1294 1295 clock := hlc.NewClock(hlc.UnixNano, time.Millisecond) 1296 s := newIntervalSkl(clock, MinRetentionWindow, makeSklMetrics()) 1297 rng := rand.New(rand.NewSource(timeutil.Now().UnixNano())) 1298 1299 for i := 0; i < data; i++ { 1300 from, to := makeRange(rng.Int31n(max)) 1301 nowVal := makeVal(clock.Now(), txnID) 1302 s.AddRange(from, to, excludeFrom|excludeTo, nowVal) 1303 } 1304 1305 for i := 0; i <= 10; i++ { 1306 b.Run(fmt.Sprintf("frac_%d", i), func(b *testing.B) { 1307 var wg sync.WaitGroup 1308 1309 for p := 0; p < parallel; p++ { 1310 wg.Add(1) 1311 1312 go func(i int) { 1313 defer wg.Done() 1314 1315 rng := rand.New(rand.NewSource(timeutil.Now().UnixNano())) 1316 1317 for n := 0; n < b.N/parallel; n++ { 1318 readFrac := rng.Int31n(10) 1319 keyNum := rng.Int31n(max) 1320 1321 if readFrac < int32(i) { 1322 key := []byte(fmt.Sprintf("%020d", keyNum)) 1323 s.LookupTimestamp(key) 1324 } else { 1325 from, to := makeRange(keyNum) 1326 nowVal := makeVal(clock.Now(), txnID) 1327 s.AddRange(from, to, excludeFrom|excludeTo, nowVal) 1328 } 1329 } 1330 }(i) 1331 } 1332 1333 wg.Wait() 1334 }) 1335 } 1336 } 1337 1338 // makeRange creates a key range from the provided input. The range will start 1339 // at the provided key and will have an end key that is a deterministic function 1340 // of the provided key. This means that for a given input, the function will 1341 // always produce the same range. 1342 func makeRange(start int32) (from, to []byte) { 1343 var end int32 1344 1345 // Most ranges are small. Larger ranges are less and less likely. 1346 rem := start % 100 1347 if rem < 80 { 1348 end = start + 0 1349 } else if rem < 90 { 1350 end = start + 100 1351 } else if rem < 95 { 1352 end = start + 10000 1353 } else if rem < 99 { 1354 end = start + 1000000 1355 } else { 1356 end = start + 100000000 1357 } 1358 1359 from = []byte(fmt.Sprintf("%020d", start)) 1360 to = []byte(fmt.Sprintf("%020d", end)) 1361 return 1362 } 1363 1364 // randRange creates a random range with keys within the specified number of 1365 // slots. The function also returns a middle key, if one exists. 1366 func randRange(rng *rand.Rand, slots int) (from, middle, to []byte) { 1367 fromNum := rng.Intn(slots) 1368 toNum := rng.Intn(slots) 1369 if fromNum > toNum { 1370 fromNum, toNum = toNum, fromNum 1371 } 1372 1373 from = []byte(fmt.Sprintf("%05d", fromNum)) 1374 to = []byte(fmt.Sprintf("%05d", toNum)) 1375 if middleNum := fromNum + 1; middleNum < toNum { 1376 middle = []byte(fmt.Sprintf("%05d", middleNum)) 1377 } 1378 return 1379 } 1380 1381 func randRangeOpt(rng *rand.Rand) rangeOptions { 1382 return rangeOptions(rng.Intn(int(excludeFrom|excludeTo) + 1)) 1383 }