@@ -22,15 +22,19 @@ import (
2222
2323 "github.com/cockroachdb/cockroach/pkg/base"
2424 "github.com/cockroachdb/cockroach/pkg/security/username"
25+ "github.com/cockroachdb/cockroach/pkg/server"
2526 "github.com/cockroachdb/cockroach/pkg/server/apiconstants"
2627 "github.com/cockroachdb/cockroach/pkg/server/serverpb"
2728 "github.com/cockroachdb/cockroach/pkg/server/srvtestutils"
29+ "github.com/cockroachdb/cockroach/pkg/settings/cluster"
2830 "github.com/cockroachdb/cockroach/pkg/spanconfig"
2931 "github.com/cockroachdb/cockroach/pkg/sql"
3032 "github.com/cockroachdb/cockroach/pkg/sql/appstatspb"
3133 "github.com/cockroachdb/cockroach/pkg/sql/sem/catconstants"
3234 "github.com/cockroachdb/cockroach/pkg/sql/sessiondata"
3335 "github.com/cockroachdb/cockroach/pkg/sql/sqlstats"
36+ "github.com/cockroachdb/cockroach/pkg/sql/sqlstats/persistedsqlstats"
37+ "github.com/cockroachdb/cockroach/pkg/sql/sqlstats/persistedsqlstats/sqlstatstestutil"
3438 "github.com/cockroachdb/cockroach/pkg/testutils"
3539 "github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
3640 "github.com/cockroachdb/cockroach/pkg/testutils/skip"
@@ -1129,3 +1133,273 @@ func TestUnprivilegedUserResetIndexUsageStats(t *testing.T) {
11291133
11301134 require .Contains (t , err .Error (), "requires admin privilege" )
11311135}
1136+
1137+ // TestCombinedStatementUsesCorrectSourceTable tests that requests read from
1138+ // the expected crdb_internal table given the table states. We have a lot of
1139+ // different tables that requests could potentially read from (in-memory, cached,
1140+ // system tables etc.), so we should sanity check that we are using the expected ones.
1141+ // given some simple table states.
1142+ func TestCombinedStatementUsesCorrectSourceTable (t * testing.T ) {
1143+ defer leaktest .AfterTest (t )()
1144+ defer log .Scope (t ).Close (t )
1145+
1146+ ctx := context .Background ()
1147+
1148+ // Disable flushing sql stats so we can manually set the table states
1149+ // without worrying about unexpected stats appearing.
1150+ settings := cluster .MakeTestingClusterSettings ()
1151+ statsKnobs := sqlstats .CreateTestingKnobs ()
1152+ defaultMockInsertedAggTs := timeutil .Unix (1696906800 , 0 )
1153+ statsKnobs .StubTimeNow = func () time.Time { return defaultMockInsertedAggTs }
1154+ persistedsqlstats .SQLStatsFlushEnabled .Override (ctx , & settings .SV , false )
1155+ srv := serverutils .StartServerOnly (t , base.TestServerArgs {
1156+ Settings : settings ,
1157+ Knobs : base.TestingKnobs {
1158+ SQLStatsKnobs : statsKnobs ,
1159+ },
1160+ })
1161+ defer srv .Stopper ().Stop (ctx )
1162+
1163+ conn := sqlutils .MakeSQLRunner (srv .ApplicationLayer ().SQLConn (t ))
1164+ conn .Exec (t , "SET application_name = $1" , server .CrdbInternalStmtStatsCombined )
1165+ conn .Exec (t , "SET CLUSTER SETTING sql.stats.activity.flush.enabled = 'f'" )
1166+ // Clear the in-memory stats so we only have the above app name.
1167+ // Then populate it with 1 query.
1168+ conn .Exec (t , "SELECT crdb_internal.reset_sql_stats()" )
1169+ conn .Exec (t , "SELECT 1" )
1170+
1171+ type testCase struct {
1172+ name string
1173+ tableSetupFn func () error
1174+ expectedStmtsTable string
1175+ expectedTxnsTable string
1176+ reqs []serverpb.CombinedStatementsStatsRequest
1177+ isEmpty bool
1178+ returnInternal bool
1179+ }
1180+
1181+ ie := srv .InternalExecutor ().(* sql.InternalExecutor )
1182+
1183+ defaultMockOneEach := func () error {
1184+ startTs := defaultMockInsertedAggTs
1185+ stmt := sqlstatstestutil .GetRandomizedCollectedStatementStatisticsForTest (t )
1186+ stmt .ID = 1
1187+ stmt .AggregatedTs = startTs
1188+ stmt .Key .App = server .CrdbInternalStmtStatsPersisted
1189+ stmt .Key .TransactionFingerprintID = 1
1190+ require .NoError (t , sqlstatstestutil .InsertMockedIntoSystemStmtStats (ctx , ie , & stmt , 1 /* nodeId */ , nil ))
1191+
1192+ stmt .Key .App = server .CrdbInternalStmtStatsCached
1193+ require .NoError (t , sqlstatstestutil .InsertMockedIntoSystemStmtActivity (ctx , ie , & stmt , nil ))
1194+
1195+ txn := sqlstatstestutil .GetRandomizedCollectedTransactionStatisticsForTest (t )
1196+ txn .StatementFingerprintIDs = []appstatspb.StmtFingerprintID {1 }
1197+ txn .TransactionFingerprintID = 1
1198+ txn .AggregatedTs = startTs
1199+ txn .App = server .CrdbInternalTxnStatsPersisted
1200+ require .NoError (t , sqlstatstestutil .InsertMockedIntoSystemTxnStats (ctx , ie , & txn , 1 , nil ))
1201+ txn .App = server .CrdbInternalTxnStatsCached
1202+ require .NoError (t , sqlstatstestutil .InsertMockedIntoSystemTxnActivity (ctx , ie , & txn , nil ))
1203+
1204+ return nil
1205+ }
1206+ testCases := []testCase {
1207+ {
1208+ name : "activity and persisted tables empty" ,
1209+ tableSetupFn : func () error { return nil },
1210+ // We should attempt to read from the in-memory tables, since
1211+ // they are the last resort.
1212+ expectedStmtsTable : server .CrdbInternalStmtStatsCombined ,
1213+ expectedTxnsTable : server .CrdbInternalTxnStatsCombined ,
1214+ reqs : []serverpb.CombinedStatementsStatsRequest {
1215+ {FetchMode : createTxnFetchMode (0 )},
1216+ },
1217+ returnInternal : false ,
1218+ },
1219+ {
1220+ name : "all tables have data in selected range" ,
1221+ tableSetupFn : defaultMockOneEach ,
1222+ expectedStmtsTable : server .CrdbInternalStmtStatsCached ,
1223+ expectedTxnsTable : server .CrdbInternalTxnStatsCached ,
1224+ reqs : []serverpb.CombinedStatementsStatsRequest {
1225+ {Start : defaultMockInsertedAggTs .Unix ()},
1226+ {
1227+ Start : defaultMockInsertedAggTs .Unix (),
1228+ End : defaultMockInsertedAggTs .Unix (),
1229+ },
1230+ },
1231+ returnInternal : false ,
1232+ },
1233+ {
1234+ name : "all tables have data but no start range is provided" ,
1235+ tableSetupFn : defaultMockOneEach ,
1236+ // When no date range is provided, we should default to reading from
1237+ // persisted or in-memory (whichever has data first). In this case the
1238+ // persisted table has data.
1239+ expectedStmtsTable : server .CrdbInternalStmtStatsPersisted ,
1240+ expectedTxnsTable : server .CrdbInternalTxnStatsPersisted ,
1241+ reqs : []serverpb.CombinedStatementsStatsRequest {
1242+ {},
1243+ {End : defaultMockInsertedAggTs .Unix ()},
1244+ },
1245+ returnInternal : false ,
1246+ },
1247+ {
1248+ name : "all tables have data but not in the selected range" ,
1249+ tableSetupFn : defaultMockOneEach ,
1250+ expectedStmtsTable : server .CrdbInternalStmtStatsCombined ,
1251+ expectedTxnsTable : server .CrdbInternalTxnStatsCombined ,
1252+ reqs : []serverpb.CombinedStatementsStatsRequest {
1253+ {Start : defaultMockInsertedAggTs .Add (time .Hour ).Unix ()},
1254+ {End : defaultMockInsertedAggTs .Truncate (time .Hour * 2 ).Unix ()},
1255+ },
1256+ isEmpty : true ,
1257+ returnInternal : false ,
1258+ },
1259+ {
1260+ name : "activity table has data in range with specified sort, fetchmode=txns" ,
1261+ tableSetupFn : defaultMockOneEach ,
1262+ // For txn mode, we should not use the activity table for stmts.
1263+ expectedStmtsTable : server .CrdbInternalStmtStatsPersisted ,
1264+ expectedTxnsTable : server .CrdbInternalTxnStatsCached ,
1265+ // These sort options do exist on the activity table.
1266+ reqs : []serverpb.CombinedStatementsStatsRequest {
1267+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (0 )},
1268+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (1 )},
1269+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (2 )},
1270+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (3 )},
1271+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (4 )},
1272+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (5 )},
1273+ },
1274+ returnInternal : false ,
1275+ },
1276+ {
1277+ name : "activity table has data in range with specified sort, fetchmode=stmts" ,
1278+ tableSetupFn : defaultMockOneEach ,
1279+ expectedStmtsTable : server .CrdbInternalStmtStatsCached ,
1280+ expectedTxnsTable : "" ,
1281+ // These sort options do exist on the activity table.
1282+ reqs : []serverpb.CombinedStatementsStatsRequest {
1283+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (0 )},
1284+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (1 )},
1285+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (2 )},
1286+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (3 )},
1287+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (4 )},
1288+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (5 )},
1289+ },
1290+ returnInternal : false ,
1291+ },
1292+ {
1293+ name : "activity table has data in range, but selected sort is not on it, fetchmode=txns" ,
1294+ tableSetupFn : defaultMockOneEach ,
1295+ expectedStmtsTable : server .CrdbInternalStmtStatsPersisted ,
1296+ expectedTxnsTable : server .CrdbInternalTxnStatsPersisted ,
1297+ // These sort options do not exist on the activity table.
1298+ reqs : []serverpb.CombinedStatementsStatsRequest {
1299+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (6 )},
1300+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (7 )},
1301+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (8 )},
1302+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (9 )},
1303+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (10 )},
1304+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (11 )},
1305+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (12 )},
1306+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (13 )},
1307+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createTxnFetchMode (14 )},
1308+ },
1309+ returnInternal : false ,
1310+ },
1311+ {
1312+ name : "activity table has data in range, but selected sort is not on it, fetchmode=stmts" ,
1313+ tableSetupFn : defaultMockOneEach ,
1314+ expectedStmtsTable : server .CrdbInternalStmtStatsPersisted ,
1315+ expectedTxnsTable : "" ,
1316+ // These sort options do not exist on the activity table.
1317+ reqs : []serverpb.CombinedStatementsStatsRequest {
1318+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (6 )},
1319+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (7 )},
1320+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (8 )},
1321+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (9 )},
1322+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (10 )},
1323+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (11 )},
1324+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (12 )},
1325+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (13 )},
1326+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (14 )},
1327+ },
1328+ returnInternal : false ,
1329+ },
1330+ {
1331+ name : "activity table has data in range with specified sort, fetchmode=stmts" ,
1332+ tableSetupFn : defaultMockOneEach ,
1333+ expectedStmtsTable : server .CrdbInternalStmtStatsPersisted ,
1334+ expectedTxnsTable : "" ,
1335+ // These sort options do exist on the activity table.
1336+ reqs : []serverpb.CombinedStatementsStatsRequest {
1337+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (0 )},
1338+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (1 )},
1339+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (2 )},
1340+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (3 )},
1341+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (4 )},
1342+ {Start : defaultMockInsertedAggTs .Unix (), FetchMode : createStmtFetchMode (5 )},
1343+ },
1344+ returnInternal : true ,
1345+ },
1346+ }
1347+
1348+ client := srv .ApplicationLayer ().GetStatusClient (t )
1349+
1350+ for _ , tc := range testCases {
1351+ t .Run (tc .name , func (t * testing.T ) {
1352+ require .NoError (t , tc .tableSetupFn ())
1353+
1354+ defer func () {
1355+ // Reset tables.
1356+ conn .Exec (t , "SELECT crdb_internal.reset_sql_stats()" )
1357+ conn .Exec (t , "SELECT crdb_internal.reset_activity_tables()" )
1358+ conn .Exec (t , "SELECT 1" )
1359+ }()
1360+
1361+ for _ , r := range tc .reqs {
1362+ conn .Exec (t , fmt .Sprintf ("SET CLUSTER SETTING sql.stats.response.show_internal.enabled=%v" , tc .returnInternal ))
1363+ resp , err := client .CombinedStatementStats (ctx , & r )
1364+ require .NoError (t , err )
1365+
1366+ require .Equal (t , tc .expectedStmtsTable , resp .StmtsSourceTable , "req: %v" , r )
1367+ require .Equal (t , tc .expectedTxnsTable , resp .TxnsSourceTable , "req: %v" , r )
1368+
1369+ if tc .isEmpty {
1370+ continue
1371+ }
1372+
1373+ require .NotZero (t , len (resp .Statements ), "req: %v" , r )
1374+ // Verify we used the correct queries to return data.
1375+ require .Equal (t , tc .expectedStmtsTable , resp .Statements [0 ].Key .KeyData .App , "req: %v" , r )
1376+ if tc .expectedTxnsTable == server .CrdbInternalTxnStatsCombined {
1377+ // For the combined query, we're using in-mem data and we set the
1378+ // app name there to the in-memory stmts table.
1379+ require .Equal (t , server .CrdbInternalStmtStatsCombined , resp .Transactions [0 ].StatsData .App , "req: %v" , r )
1380+ } else if tc .expectedTxnsTable != "" {
1381+ require .NotZero (t , len (resp .Transactions ))
1382+ require .Equal (t , tc .expectedTxnsTable , resp .Transactions [0 ].StatsData .App , "req: %v" , r )
1383+ }
1384+ }
1385+
1386+ })
1387+ }
1388+ }
1389+
1390+ func createStmtFetchMode (
1391+ sort serverpb.StatsSortOptions ,
1392+ ) * serverpb.CombinedStatementsStatsRequest_FetchMode {
1393+ return & serverpb.CombinedStatementsStatsRequest_FetchMode {
1394+ StatsType : serverpb .CombinedStatementsStatsRequest_StmtStatsOnly ,
1395+ Sort : sort ,
1396+ }
1397+ }
1398+ func createTxnFetchMode (
1399+ sort serverpb.StatsSortOptions ,
1400+ ) * serverpb.CombinedStatementsStatsRequest_FetchMode {
1401+ return & serverpb.CombinedStatementsStatsRequest_FetchMode {
1402+ StatsType : serverpb .CombinedStatementsStatsRequest_TxnStatsOnly ,
1403+ Sort : sort ,
1404+ }
1405+ }
0 commit comments