github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/server/stats_test.go (about)

     1  // Copyright 2020 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 server
    12  
    13  import (
    14  	"context"
    15  	"strings"
    16  	"testing"
    17  	"time"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    20  	"github.com/cockroachdb/cockroach/pkg/server/serverpb"
    21  	"github.com/cockroachdb/cockroach/pkg/settings/cluster"
    22  	"github.com/cockroachdb/cockroach/pkg/sql"
    23  	"github.com/cockroachdb/cockroach/pkg/sql/tests"
    24  	"github.com/cockroachdb/cockroach/pkg/testutils"
    25  	"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
    26  	"github.com/cockroachdb/cockroach/pkg/util/leaktest"
    27  	"github.com/cockroachdb/cockroach/pkg/util/timeutil"
    28  	"github.com/cockroachdb/errors"
    29  )
    30  
    31  func TestTelemetrySQLStatsIndependence(t *testing.T) {
    32  	defer leaktest.AfterTest(t)()
    33  	ctx := context.Background()
    34  	params, _ := tests.CreateTestServerParams()
    35  	s, sqlDB, _ := serverutils.StartServer(t, params)
    36  	defer s.Stopper().Stop(ctx)
    37  
    38  	if _, err := sqlDB.Exec(`
    39  CREATE DATABASE t;
    40  CREATE TABLE t.test (x INT PRIMARY KEY);
    41  `); err != nil {
    42  		t.Fatal(err)
    43  	}
    44  
    45  	sqlServer := s.(*TestServer).Server.sqlServer.pgServer.SQLServer
    46  
    47  	// Flush stats at the beginning of the test.
    48  	sqlServer.ResetSQLStats(ctx)
    49  	sqlServer.ResetReportedStats(ctx)
    50  
    51  	// Run some queries mixed with diagnostics, and ensure that the statistics
    52  	// are unnaffected by the calls to report diagnostics.
    53  	if _, err := sqlDB.Exec(`INSERT INTO t.test VALUES ($1)`, 1); err != nil {
    54  		t.Fatal(err)
    55  	}
    56  	s.ReportDiagnostics(ctx)
    57  	if _, err := sqlDB.Exec(`INSERT INTO t.test VALUES ($1)`, 2); err != nil {
    58  		t.Fatal(err)
    59  	}
    60  	s.ReportDiagnostics(ctx)
    61  
    62  	// Ensure that our SQL statement data was not affected by the telemetry report.
    63  	stats := sqlServer.GetUnscrubbedStmtStats()
    64  	foundStat := false
    65  	for _, stat := range stats {
    66  		if strings.HasPrefix(stat.Key.Query, "INSERT INTO t.test VALUES") {
    67  			foundStat = true
    68  			if stat.Stats.Count != 2 {
    69  				t.Fatal("expected to find 2 invocations, found", stat.Stats.Count)
    70  			}
    71  		}
    72  	}
    73  	if !foundStat {
    74  		t.Fatal("expected to find stats for insert query, but didn't")
    75  	}
    76  }
    77  
    78  func TestEnsureSQLStatsAreFlushedForTelemetry(t *testing.T) {
    79  	defer leaktest.AfterTest(t)()
    80  
    81  	ctx := context.Background()
    82  	params, _ := tests.CreateTestServerParams()
    83  	params.Settings = cluster.MakeClusterSettings()
    84  	// Set the SQL stat refresh rate very low so that SQL stats are continuously
    85  	// flushed into the telemetry reporting stats pool.
    86  	sql.SQLStatReset.Override(&params.Settings.SV, 10*time.Millisecond)
    87  	s, sqlDB, _ := serverutils.StartServer(t, params)
    88  	defer s.Stopper().Stop(ctx)
    89  
    90  	// Run some queries against the database.
    91  	if _, err := sqlDB.Exec(`
    92  CREATE DATABASE t;
    93  CREATE TABLE t.test (x INT PRIMARY KEY);
    94  INSERT INTO t.test VALUES (1);
    95  INSERT INTO t.test VALUES (2);
    96  `); err != nil {
    97  		t.Fatal(err)
    98  	}
    99  
   100  	statusServer := s.(*TestServer).status
   101  	sqlServer := s.(*TestServer).Server.sqlServer.pgServer.SQLServer
   102  	testutils.SucceedsSoon(t, func() error {
   103  		// Get the diagnostic info.
   104  		res, err := statusServer.Diagnostics(ctx, &serverpb.DiagnosticsRequest{NodeId: "local"})
   105  		if err != nil {
   106  			t.Fatal(err)
   107  		}
   108  
   109  		found := false
   110  		for _, stat := range res.SqlStats {
   111  			// These stats are scrubbed, so look for our scrubbed statement.
   112  			if strings.HasPrefix(stat.Key.Query, "INSERT INTO _ VALUES (_)") {
   113  				found = true
   114  			}
   115  		}
   116  
   117  		if !found {
   118  			return errors.New("expected to find query stats, but didn't")
   119  		}
   120  
   121  		// We should also not find the stat in the SQL stats pool, since the SQL
   122  		// stats are getting flushed.
   123  		stats := sqlServer.GetScrubbedStmtStats()
   124  		for _, stat := range stats {
   125  			// These stats are scrubbed, so look for our scrubbed statement.
   126  			if strings.HasPrefix(stat.Key.Query, "INSERT INTO _ VALUES (_)") {
   127  				t.Error("expected to not find stat, but did")
   128  			}
   129  		}
   130  		return nil
   131  	})
   132  }
   133  
   134  func TestSQLStatCollection(t *testing.T) {
   135  	defer leaktest.AfterTest(t)()
   136  	ctx := context.Background()
   137  	params, _ := tests.CreateTestServerParams()
   138  	s, sqlDB, _ := serverutils.StartServer(t, params)
   139  	defer s.Stopper().Stop(ctx)
   140  
   141  	sqlServer := s.(*TestServer).Server.sqlServer.pgServer.SQLServer
   142  
   143  	// Flush stats at the beginning of the test.
   144  	sqlServer.ResetSQLStats(ctx)
   145  	sqlServer.ResetReportedStats(ctx)
   146  
   147  	// Execute some queries against the sqlDB to build up some stats.
   148  	if _, err := sqlDB.Exec(`
   149  	CREATE DATABASE t;
   150  	CREATE TABLE t.test (x INT PRIMARY KEY);
   151  	INSERT INTO t.test VALUES (1);
   152  	INSERT INTO t.test VALUES (2);
   153  	INSERT INTO t.test VALUES (3);
   154  `); err != nil {
   155  		t.Fatal(err)
   156  	}
   157  
   158  	// Collect stats from the SQL server and ensure our queries are present.
   159  	stats := sqlServer.GetUnscrubbedStmtStats()
   160  	foundStat := false
   161  	var sqlStatData roachpb.StatementStatistics
   162  
   163  	for _, stat := range stats {
   164  		if strings.HasPrefix(stat.Key.Query, "INSERT INTO t.test VALUES") {
   165  			foundStat = true
   166  			sqlStatData = stat.Stats
   167  		}
   168  	}
   169  	if !foundStat {
   170  		t.Fatal("expected to find stats for insert query, but didn't")
   171  	}
   172  
   173  	const epsilon = 0.00001
   174  
   175  	// Reset the SQL statistics, which will dump stats into the
   176  	// reported statistics pool.
   177  	sqlServer.ResetSQLStats(ctx)
   178  
   179  	// Query the reported statistics.
   180  	stats = sqlServer.GetUnscrubbedReportingStats()
   181  	foundStat = false
   182  	for _, stat := range stats {
   183  		if strings.HasPrefix(stat.Key.Query, "INSERT INTO t.test VALUES") {
   184  			foundStat = true
   185  			if !stat.Stats.AlmostEqual(&sqlStatData, epsilon) {
   186  				t.Fatal("expected stats", sqlStatData.String(), "found", stat.Stats.String())
   187  			}
   188  		}
   189  	}
   190  
   191  	if !foundStat {
   192  		t.Fatal("expected to find stats for insert query in reported pool, but didn't")
   193  	}
   194  
   195  	// Make another query to the db.
   196  	if _, err := sqlDB.Exec(`
   197  	INSERT INTO t.test VALUES (4);
   198  	INSERT INTO t.test VALUES (5);
   199  	INSERT INTO t.test VALUES (6);
   200  `); err != nil {
   201  		t.Fatal(err)
   202  	}
   203  
   204  	// Find and record the stats for our second query.
   205  	stats = sqlServer.GetUnscrubbedStmtStats()
   206  	foundStat = false
   207  	for _, stat := range stats {
   208  		if strings.HasPrefix(stat.Key.Query, "INSERT INTO t.test VALUES") {
   209  			foundStat = true
   210  			// Add into the current stat data the collected data.
   211  			sqlStatData.Add(&stat.Stats)
   212  		}
   213  	}
   214  	if !foundStat {
   215  		t.Fatal("expected to find stats for insert query, but didn't")
   216  	}
   217  
   218  	// Flush the SQL stats again.
   219  	sqlServer.ResetSQLStats(ctx)
   220  
   221  	// Find our statement stat from the reported stats pool.
   222  	stats = sqlServer.GetUnscrubbedReportingStats()
   223  	foundStat = false
   224  	for _, stat := range stats {
   225  		if strings.HasPrefix(stat.Key.Query, "INSERT INTO t.test VALUES") {
   226  			foundStat = true
   227  			// The new value for the stat should be the aggregate of the previous stat
   228  			// value, and the old stat value. Additionally, zero out the timestamps for
   229  			// the logical plans, as they won't be the same.
   230  			now := timeutil.Now()
   231  			stat.Stats.SensitiveInfo.MostRecentPlanTimestamp, sqlStatData.SensitiveInfo.MostRecentPlanTimestamp = now, now
   232  			if !stat.Stats.AlmostEqual(&sqlStatData, epsilon) {
   233  				t.Fatal("expected stats", sqlStatData, "found", stat.Stats)
   234  			}
   235  		}
   236  	}
   237  
   238  	if !foundStat {
   239  		t.Fatal("expected to find stats for insert query in reported pool, but didn't")
   240  	}
   241  }