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  }