@@ -11,6 +11,7 @@ import (
1111 "io"
1212 "reflect"
1313 "sort"
14+ "strings"
1415 "testing"
1516 "unsafe"
1617
@@ -877,6 +878,137 @@ func TestServerDump(t *testing.T) {
877878 }
878879}
879880
881+ func TestServerDumpChildMetrics (t * testing.T ) {
882+ defer leaktest .AfterTest (t )()
883+ defer log .Scope (t ).Close (t )
884+
885+ ctx := context .Background ()
886+
887+ s := serverutils .StartServerOnly (t , base.TestServerArgs {
888+ // For now, direct access to the tsdb is reserved to the storage layer.
889+ DefaultTestTenant : base .TestIsSpecificToStorageLayerAndNeedsASystemTenant ,
890+
891+ Knobs : base.TestingKnobs {
892+ Store : & kvserver.StoreTestingKnobs {
893+ DisableTimeSeriesMaintenanceQueue : true ,
894+ },
895+ },
896+ })
897+ defer s .Stopper ().Stop (ctx )
898+
899+ tsdb := s .TsDB ().(* ts.DB )
900+
901+ // Store parent metric (without labels)
902+ parentMetric := "cr.node.changefeed.emitted_messages"
903+ if err := tsdb .StoreData (ctx , ts .Resolution10s , []tspb.TimeSeriesData {
904+ {
905+ Name : parentMetric ,
906+ Source : "1" ,
907+ Datapoints : []tspb.TimeSeriesDatapoint {
908+ {TimestampNanos : 100 * 1e9 , Value : 1000.0 },
909+ {TimestampNanos : 200 * 1e9 , Value : 2000.0 },
910+ },
911+ },
912+ }); err != nil {
913+ t .Fatal (err )
914+ }
915+
916+ // Store child metrics (with labels encoded in name)
917+ childMetric1 := fmt .Sprintf (`%s{feed_id="123",scope="default"}` , parentMetric )
918+ childMetric2 := fmt .Sprintf (`%s{feed_id="456",scope="system"}` , parentMetric )
919+ if err := tsdb .StoreData (ctx , ts .Resolution1m , []tspb.TimeSeriesData {
920+ {
921+ Name : childMetric1 ,
922+ Source : "1" ,
923+ Datapoints : []tspb.TimeSeriesDatapoint {
924+ {TimestampNanos : 100 * 1e9 , Value : 500.0 },
925+ {TimestampNanos : 200 * 1e9 , Value : 1500.0 },
926+ },
927+ },
928+ {
929+ Name : childMetric2 ,
930+ Source : "1" ,
931+ Datapoints : []tspb.TimeSeriesDatapoint {
932+ {TimestampNanos : 100 * 1e9 , Value : 300.0 },
933+ {TimestampNanos : 200 * 1e9 , Value : 800.0 },
934+ },
935+ },
936+ }); err != nil {
937+ t .Fatal (err )
938+ }
939+
940+ conn := s .RPCClientConn (t , username .RootUserName ())
941+ client := conn .NewTimeSeriesClient ()
942+
943+ t .Run ("includes child metrics" , func (t * testing.T ) {
944+ dumpClient , err := client .Dump (ctx , & tspb.DumpRequest {
945+ Names : []string {parentMetric },
946+ StartNanos : 100 * 1e9 ,
947+ EndNanos : 300 * 1e9 ,
948+ })
949+ require .NoError (t , err )
950+
951+ resultMap := make (map [string ][]tspb.TimeSeriesDatapoint )
952+ for {
953+ msg , err := dumpClient .Recv ()
954+ if err == io .EOF {
955+ break
956+ }
957+ require .NoError (t , err )
958+ resultMap [msg .Name ] = append (resultMap [msg .Name ], msg .Datapoints ... )
959+ }
960+
961+ // Should have parent metric AND both child metrics
962+ require .Contains (t , resultMap , parentMetric , "parent metric should be included" )
963+ require .Contains (t , resultMap , childMetric1 , "child metric 1 should be included" )
964+ require .Contains (t , resultMap , childMetric2 , "child metric 2 should be included" )
965+ require .Equal (t , 3 , len (resultMap ), "should have parent and both child metrics" )
966+
967+ // Verify data correctness for parent metric
968+ require .Len (t , resultMap [parentMetric ], 2 , "parent metric should have 2 datapoints" )
969+ require .Equal (t , 1000.0 , resultMap [parentMetric ][0 ].Value )
970+ require .Equal (t , 2000.0 , resultMap [parentMetric ][1 ].Value )
971+
972+ // Verify data correctness for child metrics
973+ require .Len (t , resultMap [childMetric1 ], 2 , "child metric 1 should have 2 datapoints" )
974+ require .Equal (t , 500.0 , resultMap [childMetric1 ][0 ].Value )
975+ require .Equal (t , 1500.0 , resultMap [childMetric1 ][1 ].Value )
976+
977+ require .Len (t , resultMap [childMetric2 ], 2 , "child metric 2 should have 2 datapoints" )
978+ require .Equal (t , 300.0 , resultMap [childMetric2 ][0 ].Value )
979+ require .Equal (t , 800.0 , resultMap [childMetric2 ][1 ].Value )
980+ })
981+
982+ t .Run ("DumpRaw sees child metrics" , func (t * testing.T ) {
983+ dumpRawClient , err := client .DumpRaw (ctx , & tspb.DumpRequest {
984+ Names : []string {parentMetric },
985+ StartNanos : 100 * 1e9 ,
986+ EndNanos : 300 * 1e9 ,
987+ })
988+ require .NoError (t , err )
989+
990+ var kvCount int
991+ seenMetrics := make (map [string ]bool )
992+ for {
993+ kv , err := dumpRawClient .Recv ()
994+ if err == io .EOF {
995+ break
996+ }
997+ require .NoError (t , err )
998+ kvCount ++
999+ // Decode the key to verify it contains child metrics
1000+ // The key contains the encoded metric name
1001+ keyStr := string (kv .Key )
1002+ if strings .Contains (keyStr , "{" ) {
1003+ seenMetrics ["child" ] = true
1004+ }
1005+ }
1006+
1007+ require .Greater (t , kvCount , 0 , "should have raw KVs" )
1008+ require .True (t , seenMetrics ["child" ], "should have seen child metrics in raw dump" )
1009+ })
1010+ }
1011+
8801012func BenchmarkServerQuery (b * testing.B ) {
8811013 defer log .Scope (b ).Close (b )
8821014
0 commit comments