github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/stats/automatic_stats_manual_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 stats_test
    12  
    13  import (
    14  	"context"
    15  	"flag"
    16  	"fmt"
    17  	"runtime"
    18  	"sync"
    19  	"testing"
    20  	"time"
    21  
    22  	"github.com/cockroachdb/cockroach/pkg/base"
    23  	"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
    24  	"github.com/cockroachdb/cockroach/pkg/testutils/sqlutils"
    25  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    26  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    27  )
    28  
    29  var runManual = flag.Bool(
    30  	"run-manual", false,
    31  	"run manual tests",
    32  )
    33  
    34  // TestAdaptiveThrottling is a "manual" test: it runs automatic statistics with
    35  // varying load on the system and prints out the times. It should be run on a
    36  // lightly loaded system using:
    37  //
    38  //   make test PKG=./pkg/sql/stats TESTS=AdaptiveThrottling TESTFLAGS='-v --run-manual -logtostderr NONE'
    39  //
    40  // Sample output:
    41  //
    42  // --- PASS: TestAdaptiveThrottling (114.51s)
    43  //     automatic_stats_manual_test.go:72: Populate table took 7.639067726s
    44  //     automatic_stats_manual_test.go:72: --- Load 0% ---
    45  //     automatic_stats_manual_test.go:72: Create stats took 1.198634729s
    46  //     automatic_stats_manual_test.go:72: --- Load 30% ---
    47  //     automatic_stats_manual_test.go:72: Create stats took 2.270165784s
    48  //     automatic_stats_manual_test.go:72: --- Load 50% ---
    49  //     automatic_stats_manual_test.go:72: Create stats took 7.324599981s
    50  //     automatic_stats_manual_test.go:72: --- Load 70% ---
    51  //     automatic_stats_manual_test.go:72: Create stats took 15.886412857s
    52  //
    53  func TestAdaptiveThrottling(t *testing.T) {
    54  	defer leaktest.AfterTest(t)()
    55  	ctx := context.Background()
    56  	if !*runManual {
    57  		t.Skip("manual test with no --run-manual")
    58  	}
    59  	s, sqlDB, _ := serverutils.StartServer(t, base.TestServerArgs{})
    60  	defer s.Stopper().Stop(ctx)
    61  
    62  	r := sqlutils.MakeSQLRunner(sqlDB)
    63  	r.Exec(t, "SET CLUSTER SETTING sql.stats.automatic_collection.enabled = false")
    64  	r.Exec(t, "CREATE TABLE xyz (x INT, y INT, z INT)")
    65  
    66  	// log prints the message to stdout and to the test log.
    67  	log := func(msg string) {
    68  		fmt.Println(msg)
    69  		t.Log(msg)
    70  	}
    71  
    72  	step := func(msg string, fn func()) {
    73  		fmt.Println(msg)
    74  		before := timeutil.Now()
    75  		fn()
    76  		log(fmt.Sprintf("%s took %s", msg, timeutil.Now().Sub(before)))
    77  	}
    78  
    79  	step("Populate table", func() {
    80  		for i := 0; i < 200; i++ {
    81  			r.Exec(t, "INSERT INTO xyz SELECT x, 2*x, 3*x FROM generate_series(1, 1000) AS g(x)")
    82  		}
    83  	})
    84  
    85  	for _, load := range []int{0, 3, 5, 7} {
    86  		log(fmt.Sprintf("--- Load %d%% ---", load*10))
    87  		// Set up a load on each CPU.
    88  		cancel := make(chan struct{})
    89  		var wg sync.WaitGroup
    90  		for i := 0; i < runtime.NumCPU(); i++ {
    91  			wg.Add(1)
    92  			go func() {
    93  				runLoad(load, cancel)
    94  				wg.Done()
    95  			}()
    96  		}
    97  
    98  		// Sleep for 2 * DefaultMetricsSampleInterval, to make sure the runtime
    99  		// stats reflect the load we want.
   100  		sleep := 2 * base.DefaultMetricsSampleInterval
   101  		fmt.Printf("Sleeping for %s\n", sleep)
   102  		time.Sleep(sleep)
   103  		step("Create stats", func() {
   104  			r.Exec(t, "CREATE STATISTICS __auto__ FROM xyz")
   105  		})
   106  		close(cancel)
   107  		wg.Wait()
   108  	}
   109  }
   110  
   111  // runLoad runs a goroutine that runs a load for (<load> / 10) of the time.
   112  // It stops when the cancel channel is closed.
   113  func runLoad(load int, cancel chan struct{}) {
   114  	if load == 0 {
   115  		<-cancel
   116  		return
   117  	}
   118  	ticker := time.NewTicker(time.Millisecond)
   119  	defer ticker.Stop()
   120  	for idx := 0; ; idx = (idx + 1) % 10 {
   121  		if idx >= load {
   122  			// No work this time slice; just wait until the ticker fires.
   123  			select {
   124  			case <-cancel:
   125  				return
   126  			case <-ticker.C:
   127  			}
   128  			continue
   129  		}
   130  		for done := false; !done; {
   131  			select {
   132  			case <-cancel:
   133  				return
   134  			case <-ticker.C:
   135  				done = true
   136  			default:
   137  				// Do some work: find the first 100 prime numbers.
   138  				for x, count := 2, 0; count < 100; x++ {
   139  					prime := true
   140  					for i := 2; i*i < x; i++ {
   141  						if x%i == 0 {
   142  							prime = false
   143  							break
   144  						}
   145  					}
   146  					if prime {
   147  						count++
   148  					}
   149  				}
   150  			}
   151  		}
   152  	}
   153  }