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(¶ms.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 }