go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/tsmon/monitor/serialize_test.go (about)

     1  // Copyright 2016 The LUCI Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package monitor
    16  
    17  import (
    18  	"testing"
    19  	"time"
    20  
    21  	"github.com/golang/protobuf/proto"
    22  	"go.chromium.org/luci/common/tsmon/distribution"
    23  	"go.chromium.org/luci/common/tsmon/field"
    24  	"go.chromium.org/luci/common/tsmon/target"
    25  	"go.chromium.org/luci/common/tsmon/types"
    26  	"google.golang.org/protobuf/types/known/timestamppb"
    27  
    28  	. "github.com/smartystreets/goconvey/convey"
    29  	. "go.chromium.org/luci/common/testing/assertions"
    30  	pb "go.chromium.org/luci/common/tsmon/ts_mon_proto"
    31  )
    32  
    33  func TestSerializeDistribution(t *testing.T) {
    34  	Convey("Fixed width params", t, func() {
    35  		d := distribution.New(distribution.FixedWidthBucketer(10, 20))
    36  		dpb := serializeDistribution(d)
    37  
    38  		So(dpb, ShouldResembleProto, &pb.MetricsData_Distribution{
    39  			Count: proto.Int64(0),
    40  			BucketOptions: &pb.MetricsData_Distribution_LinearBuckets{
    41  				LinearBuckets: &pb.MetricsData_Distribution_LinearOptions{
    42  					NumFiniteBuckets: proto.Int32(20),
    43  					Width:            proto.Float64(10),
    44  					Offset:           proto.Float64(0),
    45  				},
    46  			},
    47  		})
    48  	})
    49  
    50  	Convey("Exponential buckets", t, func() {
    51  		d := distribution.New(distribution.GeometricBucketer(2, 20))
    52  		d.Add(0)
    53  		d.Add(4)
    54  		d.Add(1024)
    55  		d.Add(1536)
    56  		d.Add(1048576)
    57  
    58  		dpb := serializeDistribution(d)
    59  		So(dpb, ShouldResembleProto, &pb.MetricsData_Distribution{
    60  			Count: proto.Int64(5),
    61  			Mean:  proto.Float64(210228),
    62  			BucketOptions: &pb.MetricsData_Distribution_ExponentialBuckets{
    63  				ExponentialBuckets: &pb.MetricsData_Distribution_ExponentialOptions{
    64  					NumFiniteBuckets: proto.Int32(20),
    65  					GrowthFactor:     proto.Float64(2),
    66  					Scale:            proto.Float64(1),
    67  				},
    68  			},
    69  			BucketCount: []int64{1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0,
    70  				0, 0, 0, 0, 0, 1},
    71  		})
    72  	})
    73  
    74  	Convey("Linear buckets", t, func() {
    75  		d := distribution.New(distribution.FixedWidthBucketer(10, 2))
    76  		d.Add(0)
    77  		d.Add(1)
    78  		d.Add(2)
    79  		d.Add(20)
    80  
    81  		dpb := serializeDistribution(d)
    82  		So(dpb, ShouldResembleProto, &pb.MetricsData_Distribution{
    83  			Count: proto.Int64(4),
    84  			Mean:  proto.Float64(5.75),
    85  			BucketOptions: &pb.MetricsData_Distribution_LinearBuckets{
    86  				LinearBuckets: &pb.MetricsData_Distribution_LinearOptions{
    87  					NumFiniteBuckets: proto.Int32(2),
    88  					Width:            proto.Float64(10),
    89  					Offset:           proto.Float64(0),
    90  				},
    91  			},
    92  			BucketCount: []int64{0, 3, 0, 1},
    93  		})
    94  	})
    95  }
    96  
    97  func TestSerializeCell(t *testing.T) {
    98  	now := time.Date(2001, 1, 2, 3, 4, 5, 6, time.UTC)
    99  	reset := time.Date(2000, 1, 2, 3, 4, 5, 6, time.UTC)
   100  
   101  	nowTS := timestamppb.New(now)
   102  	resetTS := timestamppb.New(reset)
   103  
   104  	emptyTaskRootLabels := []*pb.MetricsCollection_RootLabels{
   105  		target.RootLabel("proxy_environment", "pa"),
   106  		target.RootLabel("acquisition_name", "mon-chrome-infra"),
   107  		target.RootLabel("proxy_zone", "atl"),
   108  		target.RootLabel("service_name", ""),
   109  		target.RootLabel("job_name", ""),
   110  		target.RootLabel("data_center", ""),
   111  		target.RootLabel("host_name", ""),
   112  		target.RootLabel("task_num", int64(0)),
   113  		target.RootLabel("is_tsmon", true),
   114  	}
   115  
   116  	Convey("Int", t, func() {
   117  		ret := SerializeCells([]types.Cell{{
   118  			types.MetricInfo{
   119  				Name:        "foo",
   120  				Description: "bar",
   121  				Fields:      []field.Field{},
   122  				ValueType:   types.NonCumulativeIntType,
   123  			},
   124  			types.MetricMetadata{Units: types.Seconds},
   125  			types.CellData{
   126  				FieldVals: []any{},
   127  				Target:    &target.Task{},
   128  				ResetTime: reset,
   129  				Value:     int64(42),
   130  			},
   131  		}}, now)
   132  		So(ret, ShouldResemble, []*pb.MetricsCollection{{
   133  			RootLabels: emptyTaskRootLabels,
   134  			MetricsDataSet: []*pb.MetricsDataSet{{
   135  				MetricName:      proto.String("/chrome/infra/foo"),
   136  				FieldDescriptor: []*pb.MetricsDataSet_MetricFieldDescriptor{},
   137  				StreamKind:      pb.StreamKind_GAUGE.Enum(),
   138  				ValueType:       pb.ValueType_INT64.Enum(),
   139  				Description:     proto.String("bar"),
   140  				Data: []*pb.MetricsData{{
   141  					Value:          &pb.MetricsData_Int64Value{42},
   142  					Field:          []*pb.MetricsData_MetricField{},
   143  					StartTimestamp: nowTS,
   144  					EndTimestamp:   nowTS,
   145  				}},
   146  				Annotations: &pb.Annotations{
   147  					Unit:      proto.String(string(types.Seconds)),
   148  					Timestamp: proto.Bool(true),
   149  				},
   150  			}},
   151  		}})
   152  	})
   153  
   154  	Convey("Counter", t, func() {
   155  		ret := SerializeCells([]types.Cell{{
   156  			types.MetricInfo{
   157  				Name:        "foo",
   158  				Description: "bar",
   159  				Fields:      []field.Field{},
   160  				ValueType:   types.CumulativeIntType,
   161  			},
   162  			types.MetricMetadata{Units: types.Bytes},
   163  			types.CellData{
   164  				FieldVals: []any{},
   165  				Target:    &target.Task{},
   166  				ResetTime: reset,
   167  				Value:     int64(42),
   168  			},
   169  		}}, now)
   170  		So(ret, ShouldResemble, []*pb.MetricsCollection{{
   171  			RootLabels: emptyTaskRootLabels,
   172  			MetricsDataSet: []*pb.MetricsDataSet{{
   173  				MetricName:      proto.String("/chrome/infra/foo"),
   174  				FieldDescriptor: []*pb.MetricsDataSet_MetricFieldDescriptor{},
   175  				StreamKind:      pb.StreamKind_CUMULATIVE.Enum(),
   176  				ValueType:       pb.ValueType_INT64.Enum(),
   177  				Description:     proto.String("bar"),
   178  				Data: []*pb.MetricsData{{
   179  					Value:          &pb.MetricsData_Int64Value{42},
   180  					Field:          []*pb.MetricsData_MetricField{},
   181  					StartTimestamp: resetTS,
   182  					EndTimestamp:   nowTS,
   183  				}},
   184  				Annotations: &pb.Annotations{
   185  					Unit:      proto.String(string(types.Bytes)),
   186  					Timestamp: proto.Bool(false),
   187  				},
   188  			}},
   189  		}})
   190  	})
   191  
   192  	Convey("Float", t, func() {
   193  		ret := SerializeCells([]types.Cell{{
   194  			types.MetricInfo{
   195  				Name:        "foo",
   196  				Description: "bar",
   197  				Fields:      []field.Field{},
   198  				ValueType:   types.NonCumulativeFloatType,
   199  			},
   200  			types.MetricMetadata{},
   201  			types.CellData{
   202  				FieldVals: []any{},
   203  				Target:    &target.Task{},
   204  				ResetTime: reset,
   205  				Value:     float64(42),
   206  			},
   207  		}}, now)
   208  		So(ret, ShouldResemble, []*pb.MetricsCollection{{
   209  			RootLabels: emptyTaskRootLabels,
   210  			MetricsDataSet: []*pb.MetricsDataSet{{
   211  				MetricName:      proto.String("/chrome/infra/foo"),
   212  				FieldDescriptor: []*pb.MetricsDataSet_MetricFieldDescriptor{},
   213  				StreamKind:      pb.StreamKind_GAUGE.Enum(),
   214  				ValueType:       pb.ValueType_DOUBLE.Enum(),
   215  				Description:     proto.String("bar"),
   216  				Data: []*pb.MetricsData{{
   217  					Value:          &pb.MetricsData_DoubleValue{42},
   218  					Field:          []*pb.MetricsData_MetricField{},
   219  					StartTimestamp: nowTS,
   220  					EndTimestamp:   nowTS,
   221  				}},
   222  			}},
   223  		}})
   224  	})
   225  
   226  	Convey("FloatCounter", t, func() {
   227  		ret := SerializeCells([]types.Cell{{
   228  			types.MetricInfo{
   229  				Name:        "foo",
   230  				Description: "bar",
   231  				Fields:      []field.Field{},
   232  				ValueType:   types.CumulativeFloatType,
   233  			},
   234  			types.MetricMetadata{},
   235  			types.CellData{
   236  				FieldVals: []any{},
   237  				Target:    &target.Task{},
   238  				ResetTime: reset,
   239  				Value:     float64(42),
   240  			},
   241  		}}, now)
   242  		So(ret, ShouldResemble, []*pb.MetricsCollection{{
   243  			RootLabels: emptyTaskRootLabels,
   244  			MetricsDataSet: []*pb.MetricsDataSet{{
   245  				MetricName:      proto.String("/chrome/infra/foo"),
   246  				FieldDescriptor: []*pb.MetricsDataSet_MetricFieldDescriptor{},
   247  				StreamKind:      pb.StreamKind_CUMULATIVE.Enum(),
   248  				ValueType:       pb.ValueType_DOUBLE.Enum(),
   249  				Description:     proto.String("bar"),
   250  				Data: []*pb.MetricsData{{
   251  					Value:          &pb.MetricsData_DoubleValue{42},
   252  					Field:          []*pb.MetricsData_MetricField{},
   253  					StartTimestamp: resetTS,
   254  					EndTimestamp:   nowTS,
   255  				}},
   256  			}},
   257  		}})
   258  	})
   259  
   260  	Convey("String", t, func() {
   261  		ret := SerializeCells([]types.Cell{{
   262  			types.MetricInfo{
   263  				Name:        "foo",
   264  				Description: "bar",
   265  				Fields:      []field.Field{},
   266  				ValueType:   types.StringType,
   267  			},
   268  			types.MetricMetadata{},
   269  			types.CellData{
   270  				FieldVals: []any{},
   271  				Target:    &target.Task{},
   272  				ResetTime: reset,
   273  				Value:     "hello",
   274  			},
   275  		}}, now)
   276  		So(ret, ShouldResemble, []*pb.MetricsCollection{{
   277  			RootLabels: emptyTaskRootLabels,
   278  			MetricsDataSet: []*pb.MetricsDataSet{{
   279  				MetricName:      proto.String("/chrome/infra/foo"),
   280  				FieldDescriptor: []*pb.MetricsDataSet_MetricFieldDescriptor{},
   281  				StreamKind:      pb.StreamKind_GAUGE.Enum(),
   282  				ValueType:       pb.ValueType_STRING.Enum(),
   283  				Description:     proto.String("bar"),
   284  				Data: []*pb.MetricsData{{
   285  					Value:          &pb.MetricsData_StringValue{"hello"},
   286  					Field:          []*pb.MetricsData_MetricField{},
   287  					StartTimestamp: nowTS,
   288  					EndTimestamp:   nowTS,
   289  				}},
   290  			}},
   291  		}})
   292  	})
   293  
   294  	Convey("Boolean", t, func() {
   295  		ret := SerializeCells([]types.Cell{{
   296  			types.MetricInfo{
   297  				Name:        "foo",
   298  				Description: "bar",
   299  				Fields:      []field.Field{},
   300  				ValueType:   types.BoolType,
   301  			},
   302  			types.MetricMetadata{},
   303  			types.CellData{
   304  				FieldVals: []any{},
   305  				Target:    &target.Task{},
   306  				ResetTime: reset,
   307  				Value:     true,
   308  			},
   309  		}}, now)
   310  		So(ret, ShouldResemble, []*pb.MetricsCollection{{
   311  			RootLabels: emptyTaskRootLabels,
   312  			MetricsDataSet: []*pb.MetricsDataSet{{
   313  				MetricName:      proto.String("/chrome/infra/foo"),
   314  				FieldDescriptor: []*pb.MetricsDataSet_MetricFieldDescriptor{},
   315  				StreamKind:      pb.StreamKind_GAUGE.Enum(),
   316  				ValueType:       pb.ValueType_BOOL.Enum(),
   317  				Description:     proto.String("bar"),
   318  				Data: []*pb.MetricsData{{
   319  					Value:          &pb.MetricsData_BoolValue{true},
   320  					Field:          []*pb.MetricsData_MetricField{},
   321  					StartTimestamp: nowTS,
   322  					EndTimestamp:   nowTS,
   323  				}},
   324  			}},
   325  		}})
   326  	})
   327  
   328  	Convey("NonDefaultTarget", t, func() {
   329  		taskTarget := target.Task{
   330  			ServiceName: "hello",
   331  			JobName:     "world",
   332  		}
   333  
   334  		ret := SerializeCells([]types.Cell{{
   335  			types.MetricInfo{
   336  				Name:        "foo",
   337  				Description: "bar",
   338  				Fields:      []field.Field{},
   339  				ValueType:   types.NonCumulativeIntType,
   340  			},
   341  			types.MetricMetadata{},
   342  			types.CellData{
   343  				FieldVals: []any{},
   344  				Target:    &taskTarget,
   345  				ResetTime: reset,
   346  				Value:     int64(42),
   347  			},
   348  		}}, now)
   349  		So(ret, ShouldResemble, []*pb.MetricsCollection{{
   350  			RootLabels: []*pb.MetricsCollection_RootLabels{
   351  				target.RootLabel("proxy_environment", "pa"),
   352  				target.RootLabel("acquisition_name", "mon-chrome-infra"),
   353  				target.RootLabel("proxy_zone", "atl"),
   354  				target.RootLabel("service_name", "hello"),
   355  				target.RootLabel("job_name", "world"),
   356  				target.RootLabel("data_center", ""),
   357  				target.RootLabel("host_name", ""),
   358  				target.RootLabel("task_num", int64(0)),
   359  				target.RootLabel("is_tsmon", true),
   360  			},
   361  			MetricsDataSet: []*pb.MetricsDataSet{{
   362  				MetricName:      proto.String("/chrome/infra/foo"),
   363  				FieldDescriptor: []*pb.MetricsDataSet_MetricFieldDescriptor{},
   364  				StreamKind:      pb.StreamKind_GAUGE.Enum(),
   365  				ValueType:       pb.ValueType_INT64.Enum(),
   366  				Description:     proto.String("bar"),
   367  				Data: []*pb.MetricsData{{
   368  					Value:          &pb.MetricsData_Int64Value{42},
   369  					Field:          []*pb.MetricsData_MetricField{},
   370  					StartTimestamp: nowTS,
   371  					EndTimestamp:   nowTS,
   372  				}},
   373  			}},
   374  		}})
   375  	})
   376  
   377  	Convey("CumulativeDistribution", t, func() {
   378  		d := distribution.New(distribution.FixedWidthBucketer(10, 20))
   379  		d.Add(1024)
   380  		ret := SerializeCells([]types.Cell{{
   381  			MetricInfo: types.MetricInfo{
   382  				Name:        "foo",
   383  				Description: "bar",
   384  				Fields:      []field.Field{},
   385  				ValueType:   types.CumulativeDistributionType,
   386  			},
   387  			MetricMetadata: types.MetricMetadata{
   388  				Units: types.Seconds,
   389  			},
   390  			CellData: types.CellData{
   391  				FieldVals: []any{},
   392  				Target:    &target.Task{},
   393  				ResetTime: reset,
   394  				Value:     d,
   395  			},
   396  		}}, now)
   397  		So(ret, ShouldResemble, []*pb.MetricsCollection{{
   398  			RootLabels: emptyTaskRootLabels,
   399  			MetricsDataSet: []*pb.MetricsDataSet{{
   400  				MetricName:      proto.String("/chrome/infra/foo"),
   401  				FieldDescriptor: []*pb.MetricsDataSet_MetricFieldDescriptor{},
   402  				StreamKind:      pb.StreamKind_CUMULATIVE.Enum(),
   403  				ValueType:       pb.ValueType_DISTRIBUTION.Enum(),
   404  				Description:     proto.String("bar"),
   405  				Annotations: &pb.Annotations{
   406  					Unit: proto.String(string(types.Seconds)),
   407  					// serializeCells() should have set timestamp with true,
   408  					// although it is types.Seconds.
   409  					Timestamp: proto.Bool(false),
   410  				},
   411  				Data: []*pb.MetricsData{{
   412  					Value: &pb.MetricsData_DistributionValue{
   413  						DistributionValue: &pb.MetricsData_Distribution{
   414  							Count: proto.Int64(1),
   415  							Mean:  proto.Float64(1024),
   416  							BucketOptions: &pb.MetricsData_Distribution_LinearBuckets{
   417  								LinearBuckets: &pb.MetricsData_Distribution_LinearOptions{
   418  									NumFiniteBuckets: proto.Int32(20),
   419  									Width:            proto.Float64(10),
   420  									Offset:           proto.Float64(0),
   421  								},
   422  							},
   423  							BucketCount: []int64{
   424  								0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   425  								0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   426  								0, 1,
   427  							},
   428  						},
   429  					},
   430  					Field:          []*pb.MetricsData_MetricField{},
   431  					StartTimestamp: resetTS,
   432  					EndTimestamp:   nowTS,
   433  				}},
   434  			}},
   435  		}})
   436  	})
   437  
   438  	Convey("NonCumulativeDistribution", t, func() {
   439  		d := distribution.New(distribution.FixedWidthBucketer(10, 20))
   440  		d.Add(1024)
   441  		ret := SerializeCells([]types.Cell{{
   442  			MetricInfo: types.MetricInfo{
   443  				Name:        "foo",
   444  				Description: "bar",
   445  				Fields:      []field.Field{},
   446  				ValueType:   types.NonCumulativeDistributionType,
   447  			},
   448  			MetricMetadata: types.MetricMetadata{
   449  				Units: types.Seconds,
   450  			},
   451  			CellData: types.CellData{
   452  				FieldVals: []any{},
   453  				Target:    &target.Task{},
   454  				ResetTime: reset,
   455  				Value:     d,
   456  			},
   457  		}}, now)
   458  		So(ret, ShouldResemble, []*pb.MetricsCollection{{
   459  			RootLabels: emptyTaskRootLabels,
   460  			MetricsDataSet: []*pb.MetricsDataSet{{
   461  				MetricName:      proto.String("/chrome/infra/foo"),
   462  				FieldDescriptor: []*pb.MetricsDataSet_MetricFieldDescriptor{},
   463  				StreamKind:      pb.StreamKind_GAUGE.Enum(),
   464  				ValueType:       pb.ValueType_DISTRIBUTION.Enum(),
   465  				Description:     proto.String("bar"),
   466  				Annotations: &pb.Annotations{
   467  					Unit: proto.String(string(types.Seconds)),
   468  					// serializeCells() should have set timestamp with true,
   469  					// although it is types.Seconds.
   470  					Timestamp: proto.Bool(false),
   471  				},
   472  				Data: []*pb.MetricsData{{
   473  					Value: &pb.MetricsData_DistributionValue{
   474  						DistributionValue: &pb.MetricsData_Distribution{
   475  							Count: proto.Int64(1),
   476  							Mean:  proto.Float64(1024),
   477  							BucketOptions: &pb.MetricsData_Distribution_LinearBuckets{
   478  								LinearBuckets: &pb.MetricsData_Distribution_LinearOptions{
   479  									NumFiniteBuckets: proto.Int32(20),
   480  									Width:            proto.Float64(10),
   481  									Offset:           proto.Float64(0),
   482  								},
   483  							},
   484  							BucketCount: []int64{
   485  								0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   486  								0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
   487  								0, 1,
   488  							},
   489  						},
   490  					},
   491  					Field:          []*pb.MetricsData_MetricField{},
   492  					StartTimestamp: nowTS,
   493  					EndTimestamp:   nowTS,
   494  				}},
   495  			}},
   496  		}})
   497  	})
   498  }