github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/rowcontainer/hash_row_container_test.go (about) 1 // Copyright 2019 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package rowcontainer 12 13 import ( 14 "context" 15 "fmt" 16 "math" 17 "testing" 18 19 "github.com/cockroachdb/cockroach/pkg/base" 20 "github.com/cockroachdb/cockroach/pkg/settings/cluster" 21 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 22 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 23 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 24 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 25 "github.com/cockroachdb/cockroach/pkg/sql/types" 26 "github.com/cockroachdb/cockroach/pkg/storage" 27 "github.com/cockroachdb/cockroach/pkg/util/encoding" 28 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 29 "github.com/cockroachdb/cockroach/pkg/util/mon" 30 ) 31 32 func TestHashDiskBackedRowContainer(t *testing.T) { 33 defer leaktest.AfterTest(t)() 34 35 ctx := context.Background() 36 st := cluster.MakeTestingClusterSettings() 37 evalCtx := tree.MakeTestingEvalContext(st) 38 tempEngine, _, err := storage.NewTempEngine(ctx, storage.DefaultStorageEngine, base.DefaultTestTempStorageConfig(st), base.DefaultTestStoreSpec) 39 if err != nil { 40 t.Fatal(err) 41 } 42 defer tempEngine.Close() 43 44 // These monitors are started and stopped by subtests. 45 memoryMonitor := mon.MakeMonitor( 46 "test-mem", 47 mon.MemoryResource, 48 nil, /* curCount */ 49 nil, /* maxHist */ 50 -1, /* increment */ 51 math.MaxInt64, /* noteworthy */ 52 st, 53 ) 54 diskMonitor := mon.MakeMonitor( 55 "test-disk", 56 mon.DiskResource, 57 nil, /* curCount */ 58 nil, /* maxHist */ 59 -1, /* increment */ 60 math.MaxInt64, /* noteworthy */ 61 st, 62 ) 63 64 const numRows = 10 65 const numCols = 1 66 rows := sqlbase.MakeIntRows(numRows, numCols) 67 storedEqColumns := columns{0} 68 types := sqlbase.OneIntCol 69 ordering := sqlbase.ColumnOrdering{{ColIdx: 0, Direction: encoding.Ascending}} 70 71 rc := NewHashDiskBackedRowContainer(nil, &evalCtx, &memoryMonitor, &diskMonitor, tempEngine) 72 err = rc.Init( 73 ctx, 74 false, /* shouldMark */ 75 types, 76 storedEqColumns, 77 true, /*encodeNull */ 78 ) 79 if err != nil { 80 t.Fatalf("unexpected error while initializing hashDiskBackedRowContainer: %s", err.Error()) 81 } 82 defer rc.Close(ctx) 83 84 // NormalRun adds rows to a hashDiskBackedRowContainer, makes it spill to 85 // disk halfway through, keeps on adding rows, and then verifies that all 86 // rows were properly added to the hashDiskBackedRowContainer. 87 t.Run("NormalRun", func(t *testing.T) { 88 memoryMonitor.Start(ctx, nil, mon.MakeStandaloneBudget(math.MaxInt64)) 89 defer memoryMonitor.Stop(ctx) 90 diskMonitor.Start(ctx, nil, mon.MakeStandaloneBudget(math.MaxInt64)) 91 defer diskMonitor.Stop(ctx) 92 93 defer func() { 94 if err := rc.UnsafeReset(ctx); err != nil { 95 t.Fatal(err) 96 } 97 }() 98 99 mid := len(rows) / 2 100 for i := 0; i < mid; i++ { 101 if err := rc.AddRow(ctx, rows[i]); err != nil { 102 t.Fatal(err) 103 } 104 } 105 if rc.UsingDisk() { 106 t.Fatal("unexpectedly using disk") 107 } 108 func() { 109 // We haven't marked any rows, so the unmarked iterator should iterate 110 // over all rows added so far. 111 i := rc.NewUnmarkedIterator(ctx) 112 defer i.Close() 113 if err := verifyRows(ctx, i, rows[:mid], &evalCtx, ordering); err != nil { 114 t.Fatalf("verifying memory rows failed with: %s", err) 115 } 116 }() 117 if err := rc.SpillToDisk(ctx); err != nil { 118 t.Fatal(err) 119 } 120 if !rc.UsingDisk() { 121 t.Fatal("unexpectedly using memory") 122 } 123 for i := mid; i < len(rows); i++ { 124 if err := rc.AddRow(ctx, rows[i]); err != nil { 125 t.Fatal(err) 126 } 127 } 128 func() { 129 i := rc.NewUnmarkedIterator(ctx) 130 defer i.Close() 131 if err := verifyRows(ctx, i, rows, &evalCtx, ordering); err != nil { 132 t.Fatalf("verifying disk rows failed with: %s", err) 133 } 134 }() 135 }) 136 137 t.Run("AddRowOutOfMem", func(t *testing.T) { 138 memoryMonitor.Start(ctx, nil, mon.MakeStandaloneBudget(1)) 139 defer memoryMonitor.Stop(ctx) 140 diskMonitor.Start(ctx, nil, mon.MakeStandaloneBudget(math.MaxInt64)) 141 defer diskMonitor.Stop(ctx) 142 143 defer func() { 144 if err := rc.UnsafeReset(ctx); err != nil { 145 t.Fatal(err) 146 } 147 }() 148 149 if err := rc.AddRow(ctx, rows[0]); err != nil { 150 t.Fatal(err) 151 } 152 if !rc.UsingDisk() { 153 t.Fatal("expected to have spilled to disk") 154 } 155 if diskMonitor.AllocBytes() == 0 { 156 t.Fatal("disk monitor reports no disk usage") 157 } 158 if memoryMonitor.AllocBytes() > 0 { 159 t.Fatal("memory monitor reports unexpected usage") 160 } 161 }) 162 163 t.Run("AddRowOutOfDisk", func(t *testing.T) { 164 memoryMonitor.Start(ctx, nil, mon.MakeStandaloneBudget(1)) 165 defer memoryMonitor.Stop(ctx) 166 diskMonitor.Start(ctx, nil, mon.MakeStandaloneBudget(1)) 167 168 defer func() { 169 if err := rc.UnsafeReset(ctx); err != nil { 170 t.Fatal(err) 171 } 172 }() 173 174 err := rc.AddRow(ctx, rows[0]) 175 if code := pgerror.GetPGCode(err); code != pgcode.DiskFull { 176 t.Fatalf( 177 "unexpected error %v, expected disk full error %s", err, pgcode.DiskFull, 178 ) 179 } 180 if !rc.UsingDisk() { 181 t.Fatal("expected to have tried to spill to disk") 182 } 183 if diskMonitor.AllocBytes() != 0 { 184 t.Fatal("disk monitor reports unexpected usage") 185 } 186 if memoryMonitor.AllocBytes() != 0 { 187 t.Fatal("memory monitor reports unexpected usage") 188 } 189 }) 190 191 // VerifyIteratorRecreation adds all rows to the container, creates a 192 // recreatable unmarked iterator, iterates over half of the rows, spills the 193 // container to disk, and verifies that the iterator was recreated and points 194 // to the appropriate row. 195 t.Run("VerifyIteratorRecreation", func(t *testing.T) { 196 memoryMonitor.Start(ctx, nil, mon.MakeStandaloneBudget(math.MaxInt64)) 197 defer memoryMonitor.Stop(ctx) 198 diskMonitor.Start(ctx, nil, mon.MakeStandaloneBudget(math.MaxInt64)) 199 defer diskMonitor.Stop(ctx) 200 201 defer func() { 202 if err := rc.UnsafeReset(ctx); err != nil { 203 t.Fatal(err) 204 } 205 }() 206 207 for i := 0; i < len(rows); i++ { 208 if err := rc.AddRow(ctx, rows[i]); err != nil { 209 t.Fatal(err) 210 } 211 } 212 if rc.UsingDisk() { 213 t.Fatal("unexpectedly using disk") 214 } 215 i, err := rc.NewAllRowsIterator(ctx) 216 if err != nil { 217 t.Fatal(err) 218 } 219 defer i.Close() 220 counter := 0 221 for i.Rewind(); counter < len(rows)/2; i.Next() { 222 if ok, err := i.Valid(); err != nil { 223 t.Fatal(err) 224 } else if !ok { 225 break 226 } 227 row, err := i.Row() 228 if err != nil { 229 t.Fatal(err) 230 } 231 if cmp, err := compareRows( 232 sqlbase.OneIntCol, row, rows[counter], &evalCtx, &sqlbase.DatumAlloc{}, ordering, 233 ); err != nil { 234 t.Fatal(err) 235 } else if cmp != 0 { 236 t.Fatal(fmt.Errorf("unexpected row %v, expected %v", row, rows[counter])) 237 } 238 counter++ 239 } 240 if err := rc.SpillToDisk(ctx); err != nil { 241 t.Fatal(err) 242 } 243 if !rc.UsingDisk() { 244 t.Fatal("unexpectedly using memory") 245 } 246 for ; ; i.Next() { 247 if ok, err := i.Valid(); err != nil { 248 t.Fatal(err) 249 } else if !ok { 250 break 251 } 252 row, err := i.Row() 253 if err != nil { 254 t.Fatal(err) 255 } 256 if cmp, err := compareRows( 257 sqlbase.OneIntCol, row, rows[counter], &evalCtx, &sqlbase.DatumAlloc{}, ordering, 258 ); err != nil { 259 t.Fatal(err) 260 } else if cmp != 0 { 261 t.Fatal(fmt.Errorf("unexpected row %v, expected %v", row, rows[counter])) 262 } 263 counter++ 264 } 265 if counter != len(rows) { 266 t.Fatal(fmt.Errorf("iterator returned %d rows but %d were expected", counter, len(rows))) 267 } 268 }) 269 270 // VerifyIteratorRecreationAfterExhaustion adds all rows to the container, 271 // creates a recreatable unmarked iterator, iterates over all of the rows, 272 // spills the container to disk, and verifies that the iterator was recreated 273 // and is not valid. 274 t.Run("VerifyIteratorRecreationAfterExhaustion", func(t *testing.T) { 275 memoryMonitor.Start(ctx, nil, mon.MakeStandaloneBudget(math.MaxInt64)) 276 defer memoryMonitor.Stop(ctx) 277 diskMonitor.Start(ctx, nil, mon.MakeStandaloneBudget(math.MaxInt64)) 278 defer diskMonitor.Stop(ctx) 279 280 defer func() { 281 if err := rc.UnsafeReset(ctx); err != nil { 282 t.Fatal(err) 283 } 284 }() 285 286 for i := 0; i < len(rows); i++ { 287 if err := rc.AddRow(ctx, rows[i]); err != nil { 288 t.Fatal(err) 289 } 290 } 291 if rc.UsingDisk() { 292 t.Fatal("unexpectedly using disk") 293 } 294 i, err := rc.NewAllRowsIterator(ctx) 295 if err != nil { 296 t.Fatal(err) 297 } 298 defer i.Close() 299 counter := 0 300 for i.Rewind(); ; i.Next() { 301 if ok, err := i.Valid(); err != nil { 302 t.Fatal(err) 303 } else if !ok { 304 break 305 } 306 row, err := i.Row() 307 if err != nil { 308 t.Fatal(err) 309 } 310 if cmp, err := compareRows( 311 sqlbase.OneIntCol, row, rows[counter], &evalCtx, &sqlbase.DatumAlloc{}, ordering, 312 ); err != nil { 313 t.Fatal(err) 314 } else if cmp != 0 { 315 t.Fatal(fmt.Errorf("unexpected row %v, expected %v", row, rows[counter])) 316 } 317 counter++ 318 } 319 if counter != len(rows) { 320 t.Fatal(fmt.Errorf("iterator returned %d rows but %d were expected", counter, len(rows))) 321 } 322 if err := rc.SpillToDisk(ctx); err != nil { 323 t.Fatal(err) 324 } 325 if !rc.UsingDisk() { 326 t.Fatal("unexpectedly using memory") 327 } 328 if valid, err := i.Valid(); err != nil { 329 t.Fatal(err) 330 } else if valid { 331 t.Fatal("iterator is unexpectedly valid after recreating an exhausted iterator") 332 } 333 }) 334 } 335 336 func TestHashDiskBackedRowContainerPreservesMatchesAndMarks(t *testing.T) { 337 defer leaktest.AfterTest(t)() 338 339 ctx := context.Background() 340 st := cluster.MakeTestingClusterSettings() 341 evalCtx := tree.MakeTestingEvalContext(st) 342 tempEngine, _, err := storage.NewTempEngine(ctx, storage.DefaultStorageEngine, base.DefaultTestTempStorageConfig(st), base.DefaultTestStoreSpec) 343 if err != nil { 344 t.Fatal(err) 345 } 346 defer tempEngine.Close() 347 348 // These monitors are started and stopped by subtests. 349 memoryMonitor := mon.MakeMonitor( 350 "test-mem", 351 mon.MemoryResource, 352 nil, /* curCount */ 353 nil, /* maxHist */ 354 -1, /* increment */ 355 math.MaxInt64, /* noteworthy */ 356 st, 357 ) 358 diskMonitor := mon.MakeMonitor( 359 "test-disk", 360 mon.DiskResource, 361 nil, /* curCount */ 362 nil, /* maxHist */ 363 -1, /* increment */ 364 math.MaxInt64, /* noteworthy */ 365 st, 366 ) 367 368 const numRowsInBucket = 4 369 const numRows = 12 370 const numCols = 2 371 rows := sqlbase.MakeRepeatedIntRows(numRowsInBucket, numRows, numCols) 372 storedEqColumns := columns{0} 373 types := []*types.T{types.Int, types.Int} 374 ordering := sqlbase.ColumnOrdering{{ColIdx: 0, Direction: encoding.Ascending}} 375 376 rc := NewHashDiskBackedRowContainer(nil, &evalCtx, &memoryMonitor, &diskMonitor, tempEngine) 377 err = rc.Init( 378 ctx, 379 true, /* shouldMark */ 380 types, 381 storedEqColumns, 382 true, /*encodeNull */ 383 ) 384 if err != nil { 385 t.Fatalf("unexpected error while initializing hashDiskBackedRowContainer: %s", err.Error()) 386 } 387 defer rc.Close(ctx) 388 389 // PreservingMatches adds rows from three different buckets to a 390 // hashDiskBackedRowContainer, makes it spill to disk, keeps on adding rows 391 // from the same buckets, and then verifies that all rows were properly added 392 // to the hashDiskBackedRowContainer. 393 t.Run("PreservingMatches", func(t *testing.T) { 394 memoryMonitor.Start(ctx, nil, mon.MakeStandaloneBudget(math.MaxInt64)) 395 defer memoryMonitor.Stop(ctx) 396 diskMonitor.Start(ctx, nil, mon.MakeStandaloneBudget(math.MaxInt64)) 397 defer diskMonitor.Stop(ctx) 398 399 defer func() { 400 if err := rc.UnsafeReset(ctx); err != nil { 401 t.Fatal(err) 402 } 403 }() 404 405 mid := len(rows) / 2 406 for i := 0; i < mid; i++ { 407 if err := rc.AddRow(ctx, rows[i]); err != nil { 408 t.Fatal(err) 409 } 410 } 411 if rc.UsingDisk() { 412 t.Fatal("unexpectedly using disk") 413 } 414 func() { 415 // We haven't marked any rows, so the unmarked iterator should iterate 416 // over all rows added so far. 417 i := rc.NewUnmarkedIterator(ctx) 418 defer i.Close() 419 if err := verifyRows(ctx, i, rows[:mid], &evalCtx, ordering); err != nil { 420 t.Fatalf("verifying memory rows failed with: %s", err) 421 } 422 }() 423 if err := rc.SpillToDisk(ctx); err != nil { 424 t.Fatal(err) 425 } 426 if !rc.UsingDisk() { 427 t.Fatal("unexpectedly using memory") 428 } 429 for i := mid; i < len(rows); i++ { 430 if err := rc.AddRow(ctx, rows[i]); err != nil { 431 t.Fatal(err) 432 } 433 } 434 func() { 435 i := rc.NewUnmarkedIterator(ctx) 436 defer i.Close() 437 if err := verifyRows(ctx, i, rows, &evalCtx, ordering); err != nil { 438 t.Fatalf("verifying disk rows failed with: %s", err) 439 } 440 }() 441 }) 442 443 // PreservingMarks adds rows from three buckets to a 444 // hashDiskBackedRowContainer, marks all rows belonging to the first bucket, 445 // spills to disk, and checks that marks are preserved correctly. 446 t.Run("PreservingMarks", func(t *testing.T) { 447 memoryMonitor.Start(ctx, nil, mon.MakeStandaloneBudget(math.MaxInt64)) 448 defer memoryMonitor.Stop(ctx) 449 diskMonitor.Start(ctx, nil, mon.MakeStandaloneBudget(math.MaxInt64)) 450 defer diskMonitor.Stop(ctx) 451 452 defer func() { 453 if err := rc.UnsafeReset(ctx); err != nil { 454 t.Fatal(err) 455 } 456 }() 457 458 for i := 0; i < len(rows); i++ { 459 if err := rc.AddRow(ctx, rows[i]); err != nil { 460 t.Fatal(err) 461 } 462 } 463 if rc.UsingDisk() { 464 t.Fatal("unexpectedly using disk") 465 } 466 func() { 467 // We haven't marked any rows, so the unmarked iterator should iterate 468 // over all rows added so far. 469 i := rc.NewUnmarkedIterator(ctx) 470 defer i.Close() 471 if err := verifyRows(ctx, i, rows, &evalCtx, ordering); err != nil { 472 t.Fatalf("verifying memory rows failed with: %s", err) 473 } 474 }() 475 if err := rc.ReserveMarkMemoryMaybe(ctx); err != nil { 476 t.Fatal(err) 477 } 478 func() { 479 i, err := rc.NewBucketIterator(ctx, rows[0], storedEqColumns) 480 if err != nil { 481 t.Fatal(err) 482 } 483 defer i.Close() 484 for i.Rewind(); ; i.Next() { 485 if ok, err := i.Valid(); err != nil { 486 t.Fatal(err) 487 } else if !ok { 488 break 489 } 490 if err := i.Mark(ctx, true); err != nil { 491 t.Fatal(err) 492 } 493 } 494 }() 495 if err := rc.SpillToDisk(ctx); err != nil { 496 t.Fatal(err) 497 } 498 if !rc.UsingDisk() { 499 t.Fatal("unexpectedly using memory") 500 } 501 func() { 502 i, err := rc.NewBucketIterator(ctx, rows[0], storedEqColumns) 503 if err != nil { 504 t.Fatal(err) 505 } 506 defer i.Close() 507 for i.Rewind(); ; i.Next() { 508 if ok, err := i.Valid(); err != nil { 509 t.Fatal(err) 510 } else if !ok { 511 break 512 } 513 if !i.IsMarked(ctx) { 514 t.Fatal("Mark is not preserved during spilling to disk") 515 } 516 } 517 }() 518 }) 519 }